三重県の郷土料理「こしょう汁」「泣汁」とは

絹田
絹田

三重県民が泣汁について説明するよ.

胡椒汁,泣汁,涙汁の写真

三重県や岐阜県では「胡椒汁」「泣汁」という葬儀の火葬の後に食べる特別な汁物があります.胡椒とは唐辛子のことで,唐辛子で辛くしたお吸い物,薄い味噌汁のことです.辛いから泣いているのか,悲しいから泣いているのか分からないように火葬の後に食べるのです.別名「涙汁」「泣汁」というそうです.

「食の民族辞典」の唐辛子の欄にこのように記載があります.

三重県亀山市下庄朝神向山(昭和八年生まれ)は,葬式当日の朝餉ないし昼餉に、参列してもらう人々に精進の膳を出したが、その一品に「トウガラシ汁」があったという。同じく葬式のときに出されるものに甘辛く煮たヒリョウズ(がんもどき)があり、その煮汁を水でのばし、さらに醤油で味つけしたところに唐辛子を入れて辛味を移すように煮出す。そこに青昆布やアゲなどを浮き実にして出す。
三重県四日市市桜町斧研の近藤善治さん(昭和四年生まれ)も、葬式のときにはヒジヤド(非時宿)でこの汁を食
べたという。葬式で涙が出るように食べるといわれたこの辛い汁は、「涙汁」とも呼ばれていた。 (谷阪智佳子)

食の民族辞典

具はがんもどきを入れたり,油揚げを入れたりで地域差があるようですが,唐辛子が下に沈殿するくらいめちゃめちゃに辛くするのは共通しているようです.私も個人的にいなべ市出身の知り合いの30代の女性に話を聞いたところ,その女性の祖母が亡くなり自宅で葬儀を行なった際,火葬の後,地域の集会所で2010年頃に振る舞われたようです.

「長机をコの字に並べて,皆で泣汁を食べました.泣汁だけでそれ以外には食べません.辛いのがわかっているので私は飲んでいるふりをしていましたが,辛いから泣いているのか,悲しいから泣いているのかわからないようにするためのものだと思います.火葬の後,集会所の厨房に女性が集まって作りました.住職さんと父が残していきたいとよく話をしていました」

女性の話

作り方

がんもどきの煮物を作る

  1. がんもどきに熱湯をかけ油抜きをする
  2. それを鍋に入れて,砂糖とひたひたの水を入れて煮立てる。 煮立ったら醤油を入れて中火で煮る。
  3. 煮汁がなくなるまで煮詰める

煮物の作り方はこちらを参考にしました.

https://cookpad.com/recipe/4800225

汁物にして胡椒(唐辛子)の辛味をうつす

  1. がんもどきの煮物を水で薄めて醤油で味を整えて汁物にする
  2. 鷹の爪を大量に刻んで一緒に煮る

辛いから泣いているのか,悲しいから泣いているのかわからないように

するための汁物ということで,それを飲む際の情景がありありと思い浮かび,地域で大切にされてきた風習なのだと思いました.

参考文献

https://ja.wikipedia.org/wiki/%E8%83%A1%E6%A4%92%E6%B1%81

https://www.olive-hitomawashi.com/column/2021/03/post-13984.html

絹田
絹田

読んでくれてありがとう.これからもよろしくね

capacitorでadmobでアプリ広告をAndroidで表示する

いろんなプラグインがあります.以前は

https://github.com/rahadur/capacitor-admob

を使っていましたが,メンテナンスされていないようなので現在使うのはおすすめじゃないです.

今は

https://github.com/capacitor-community/admob

を使うのが良さそうです.

admob on capacitor

Android Studioの設定は別途ドキュメントで行うとして,バナーを表示するソースコードはtsファイルで下記のようにするだけで表示されました.

import { Component } from '@angular/core';
import { AdMob, BannerAdOptions, BannerAdSize, BannerAdPosition, BannerAdPluginEvents, AdMobBannerSize } from '@capacitor-community/admob';

@Component({
  selector: 'app-tab1',
  templateUrl: 'tab1.page.html',
  styleUrls: ['tab1.page.scss']
})


export class Tab1Page {


  constructor() {
    AdMob.initialize({
      requestTrackingAuthorization: true,
      //testingDevices: ['2077ef9a63d2b398840261c8221a0c9b'],
      //initializeForTesting: true,
    });
  }

  ionViewDidEnter(){

    AdMob.addListener(BannerAdPluginEvents.Loaded, () => {
      // Subscribe Banner Event Listener
      console.log('loaded');
    });

    AdMob.addListener(BannerAdPluginEvents.SizeChanged, (size: AdMobBannerSize) => {
      // Subscribe Change Banner Size
      console.log('size changed');

    });

    const options: BannerAdOptions = {
      adId: 'ca-app-pub-3940256099942544/15453xxxxxx',//test
      adSize: BannerAdSize.BANNER,
      position: BannerAdPosition.BOTTOM_CENTER,
      margin: 100,
      isTesting: true
      // npa: true
    };
    AdMob.showBanner(options);

  }

}

moduleファイルには何の記載もなくてOK

toDataURL()メソッド利用の際の 「Tainted canvases may not be exported」エラーについて

Google Cloud のStorageからJavaScriptで画像をダウンロードしてCanvasに書き込んだとき,そのCanvas要素の画像をtoDataURL()メソッドでdataURIに変換する際に

Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

というエラーが出る.直訳すると

「HTMLCanvasElement」で「toDataURL」の実行に失敗しました:汚染されたキャンバスはエクスポートされない可能性があります。

つまり,Corsポリシーに引っかかってしまう.解決するためにはフロントエンドのJavaScriptで任意のドメインからのデータを利用できるように明示的に指定する必要と,生成元からデータを返す際にCorsポリシーを適用しないドメインを明示的に指定する必要がある.

フロントエンドの方法

Canvas要素に描く際に生成元をanounymousに設定すればいい

  download(){
    var gsReference = this.firestorage.refFromURL('gs://my-app.appspot.com/imgs/'+this.authUid +'/'+this.query_img_name)
    const task = gsReference.getDownloadURL().subscribe(dataurl => {
      console.log(dataurl,'tsk');
      const image = new Image();
      image.onload = () => {
        this.canvas.nativeElement.width = image.width;
        this.canvas.nativeElement.height = image.height;
        this.canvas_rendering_context.drawImage(image, 0, 0);
      }
      (→これを追加)image.crossOrigin = "anonymous";
      image.src = dataurl;

    })
  }

生成元の変更

今回はGoogle CloudのStorageから画像をダウンロードしている.従ってStorageの設定をいじる必要がある.

my-cors.jsonなどのファイルを使って設定情報を書いたあと,gsutilコマンドを使ってStorageに反映させればいい.

touch my-cors.json
 nano my-cors.json

my-cors.jsonは下記のように変更しておく

[
    {
      "origin": ["*"],
      "method": ["GET","POST"],
      "responseHeader": ["Content-Type"],
      "maxAgeSeconds": 3600
    }
]

あとはコマンドから反映させる

gsutil cors set ./my-cors.json gs://my-app.appspot.com 

参考文献

https://cloud.google.com/storage/docs/configuring-cors

Angularでpipeを使って日付データをフォーマットする

準備

標準機能なので追加のインストールは必要ありません.module.tsで@angular/commonからDatePipeをインストールし,providersに登録します.

import { IonicModule } from '@ionic/angular';
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Tab3Page } from './tab3.page';
import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
+import { DatePipe } from '@angular/common';

import { Tab3PageRoutingModule } from './tab3-routing.module';

@NgModule({
  imports: [
    IonicModule,
    CommonModule,
    FormsModule,
    ExploreContainerComponentModule,
    RouterModule.forChild([{ path: '', component: Tab3Page }]),
    Tab3PageRoutingModule,
  ],
-  declarations: [Tab3Page]
+  declarations: [Tab3Page],
+  providers: [DatePipe]
})
export class Tab3PageModule {}

次に,page.tsファイルで読み込みます.

import { Component,ElementRef,ViewChild } from '@angular/core';
+import { DatePipe } from '@angular/common';

@Component({
  selector: 'app-tab3',
  templateUrl: 'tab3.page.html',
  styleUrls: ['tab3.page.scss']
})
export class Tab3Page {

  constructor(
+    private datePipe: DatePipe
  ) {
  }

  ionViewDidEnter(){
  }

}

HTMLでの表示を変えたい場合

import { Component,ElementRef,ViewChild } from '@angular/core';
import { DatePipe } from '@angular/common';

@Component({
  selector: 'app-tab3',
  templateUrl: 'tab3.page.html',
  styleUrls: ['tab3.page.scss']
})
export class Tab3Page {
+  time_on_html:any;
  constructor(
    private datePipe: DatePipe
  ) {
  }

  ionViewDidEnter(){
+    this.time_on_html = new Date();
+    console.log(this.time_on_html);
  }

}

そして,HTML側は{{time_on_html | date:”MM/dd/yy” }}で参照します.

tsファイルの中で表示を変えたい場合

import { Component,ElementRef,ViewChild } from '@angular/core';
import { DatePipe } from '@angular/common';

@Component({
  selector: 'app-tab3',
  templateUrl: 'tab3.page.html',
  styleUrls: ['tab3.page.scss']
})
export class Tab3Page {
  time_on_html:any;
  constructor(
    private datePipe: DatePipe
  ) {
  }

  ionViewDidEnter(){
    this.time_on_html = this.datePipe.transform(new Date(), 'yyyyMMddHHmmss');
    console.log(this.time_on_html);

  }

}

2021/07/19 18:42:52と表示される.

画像のオブジェクトURLをCanvas要素に描写する

Capacitorのカメラプラグインでの動作記録

image2.onloadファンクションでアロー関数を使う場合

export class Tab3Page {
  @ViewChild('canvas') public canvas: ElementRef;
  public cx: CanvasRenderingContext2D;

  constructor(
  ) {}

  ionViewDidEnter(){
    this.startCamera();
    const canvasEl: HTMLCanvasElement = this.canvas.nativeElement;
    this.cx = canvasEl.getContext('2d');
  }
  
async startCamera(){
    const imageFunc = await Camera.getPhoto({
      quality: 90,
      allowEditing: false,
      resultType: CameraResultType.Uri,
    }).then((image) => {

      // image.webPath will contain a path that can be set as an image src.
      // You can access the original file using image.path, which can be
      // passed to the Filesystem API to read the raw data of the image,
      // if desired (or pass resultType: CameraResultType.Base64 to getPhoto)
      var imageUrl = image.webPath;
      console.log(imageUrl);

      let image2:any = new Image();
      image2.onload = () => {
        console.log(image2.width);
        console.log(image2.height);
        this.cx.canvas.width = image2.width;
        this.cx.canvas.height = image2.height;
        aa.drawImage(image2, 0, 0);
        console.log('done');
      }
      image2.src = imageUrl;
    });
  };

よく乗っているもの

  export class Tab3Page {
  @ViewChild('canvas') public canvas: ElementRef;
  public cx: CanvasRenderingContext2D;

  constructor(
  ) {}

  ionViewDidEnter(){
    this.startCamera();
    const canvasEl: HTMLCanvasElement = this.canvas.nativeElement;
    this.cx = canvasEl.getContext('2d');
  }

async startCamera(){
    const imageFunc = await Camera.getPhoto({
      quality: 90,
      allowEditing: false,
      resultType: CameraResultType.Uri,
    }).then((image) => {

      // image.webPath will contain a path that can be set as an image src.
      // You can access the original file using image.path, which can be
      // passed to the Filesystem API to read the raw data of the image,
      // if desired (or pass resultType: CameraResultType.Base64 to getPhoto)
      var imageUrl = image.webPath;
      console.log(imageUrl);
      let canvasVar:any =this.cx;

      let image2:any = new Image();
      let aa:any =this.cx;
      image2.onload = function() {
        canvasVar.canvas.width = image2.width;
        canvasVar.canvas.height = image2.height;
        aa.drawImage(image2, 0, 0);
        console.log('done');
      }
      image2.src = imageUrl;
    });
  };

bind(this)を使ってこれでもいく

export class Tab3Page {
  @ViewChild('canvas') public canvas: ElementRef;
  public cx: CanvasRenderingContext2D;

  constructor(
  ) {}

  ionViewDidEnter(){
    this.startCamera();
    const canvasEl: HTMLCanvasElement = this.canvas.nativeElement;
    this.cx = canvasEl.getContext('2d');
  }
  
async startCamera(){
    const imageFunc = await Camera.getPhoto({
      quality: 90,
      allowEditing: false,
      resultType: CameraResultType.Uri,
    }).then((image) => {

      // image.webPath will contain a path that can be set as an image src.
      // You can access the original file using image.path, which can be
      // passed to the Filesystem API to read the raw data of the image,
      // if desired (or pass resultType: CameraResultType.Base64 to getPhoto)
      var imageUrl = image.webPath;
      console.log(imageUrl);
      let canvasVar:any =this.cx;

      let image2:any = new Image();
      let aa:any =this.cx;
      image2.onload = function() {
        this.cx.canvas.width = image2.width;
        this.cx.canvas.height = image2.height;
        aa.drawImage(image2, 0, 0);
        console.log('done');
      }.bind(this)
      image2.src = imageUrl;
    });
  };

https://stackoverflow.com/questions/30824756/javascript-saving-this-variable-inside-of-image-onload

Angular(Ionic)でアコーディオンを作成する

Angular(Ionic)でカスタムコンポーネントを使ってアコーディオンを作成する方法です.

今回はIonicのTabsプロジェクトをもとに作成し,Tab1に設置することにします.

まず,tab1の中にコンポーネントを作成します.

ionic g component tab1/components/expandable

次に作成したコンポーネントを下記のように書き換えます.

<div #expandWrapper class='expand-wrapper' [class.collapsed]="!expanded">
    <ng-content></ng-content>
</div>
.expand-wrapper {
  transition: max-height 0.4s ease-in-out;
  overflow: hidden;
  height: auto;
}

.collapsed {
  max-height: 0 !important;
}
import { Component, AfterViewInit, Input, ViewChild,  ElementRef, Renderer2 } from "@angular/core";

@Component({
  selector: 'app-expandable',
  templateUrl: './expandable.component.html',
  styleUrls: ['./expandable.component.scss'],
})
export class ExpandableComponent implements AfterViewInit {
  @ViewChild("expandWrapper", { read: ElementRef }) expandWrapper: ElementRef;
  @Input("expanded") expanded: boolean = false;
  @Input("expandHeight") expandHeight: string = "150px";

  constructor(public renderer: Renderer2) {}

  ngAfterViewInit() {
    this.renderer.setStyle(this.expandWrapper.nativeElement, "max-height", this.expandHeight);
  }
}

以上でコンポーネントは作成できました.

次にTab1にコンポーネントを登録します.moduleで読み込んで,declarationsに登録します.これによりapp-expandableタグを使ってTab1のHTMLからコンポーネントを呼び出せるようになります.

import { IonicModule } from '@ionic/angular';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Tab1Page } from './tab1.page';
import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';

import { Tab1PageRoutingModule } from './tab1-routing.module';
import { ImageCropperModule } from 'ngx-image-cropper';

+import { ExpandableComponent } from "./components/expandable/expandable.component";

@NgModule({
  imports: [
    IonicModule,
    CommonModule,
    FormsModule,
    ExploreContainerComponentModule,
    Tab1PageRoutingModule,
    ImageCropperModule
  ],
  declarations: [
    Tab1Page,
+    ExpandableComponent
  ]
})
export class Tab1PageModule {}

実装

あとはtab1.page.htmlとtab1.page.tsに書くだけです.

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>
      Tab 1
    </ion-title>
  </ion-toolbar>
</ion-header>


<ion-content>
  <ion-card (click)="expandItem()">
    <ion-card-header>
      <ion-card-title>My Neighbor Totoro</ion-card-title>
    </ion-card-header>

    <ion-card-content>
      <app-expandable expandHeight="100px" [expanded]="item1expand">
        <p>
          Hello there.
        </p>
      </app-expandable>
    </ion-card-content>
  </ion-card>
  <ion-card (click)="expandItem()">
    <ion-card-header>
      <ion-card-title>My Neighbor Totoro</ion-card-title>
    </ion-card-header>

    <ion-card-content>
      <app-expandable expandHeight="100px" [expanded]="item2expand">
        <p>
          Hello there.
        </p>
      </app-expandable>
    </ion-card-content>
  </ion-card>


</ion-content>
import { Component, ViewChild, ElementRef } from '@angular/core';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-tab1',
  templateUrl: 'tab1.page.html',
  styleUrls: ['tab1.page.scss']
})
export class Tab1Page {
  item1expand:boolean = true;
  item2expand:boolean = false;

  constructor(
  ) {}

  expandItem(): void {
    this.item1expand = !this.item1expand;
    this.item2expand = !this.item2expand;
  }

}