LWCで電子署名ごっこ(お絵描き)しよう


# 背景と本記事の方針
- そんなような業務要件があったので「きついっすねーどこのパッケージ買いましょうね」と受け流してたんですが、個人の趣味の範囲でちょっと書いてみたら動きはしたので、せっかくなので記事にします
- LWC,JavaScript,HTML一般に関する知識/技術は持ち合わせていないのでほぼスルーか参考リンク載せるだけ。素人がググりながらどうにか動くとこまでこぎ着けた過程を記します
- 間違ったこと書いてる、読みづらい等あればコメントください

# 忙しい人のための解答
SalesforceLabsがGithub上に「これぞ」という完成品を公開してくれてます感謝感謝感謝
https://github.com/SalesforceLabs/DrawAnnotations

# 実装の前提
### 目標
電子署名もどきにお絵描きができるLightningコンポーネントをつくりたい

### ライブラリについて
- 素の状態でも十分書けるようだけど、今回は外部ライブラリを使って実現

- ライブラリ選定
パッと調べた結果、以下２つがヒット。今回は諸々考慮しfabric.jsを採用

| | [jSignature](https://github.com/willowsystems/jSignature) | [fabric.js](https://github.com/fabricjs/fabric.js) |
|:-:|:-:|:-:|
| 機能概要 | 手書き署名専用 | 汎用的な画像・図形操作 |
| 要件充足 | 〇 | 〇 |
| ライセンス | MIT | MIT |
| 最新リリース | 2012.11.1 | 2022.2.21 |

<br>

# 下準備
### ライブラリのソースを貰ってくる
- distディレクトリの fabric.js (minじゃない方)
[Github: fabric.js](https://github.com/fabricjs/fabric.js)

### 弄る
- このfabric.js、一部処理にてLWC実装時のセキュリティ要件[(Lightning Lockerかな？)](https://developer.salesforce.com/docs/atlas.ja-jp.234.0.lightning.meta/lightning/security_code.htm)を満たしていないらしい
- [SalesforceLabsが公開するソース](#忙しい人のための解答)上で修正済みのfabric.jsソースが見られるので、そちらを参考に修正
(**SalesforceLabsの修正済ソースと、同verの元ソースとで差分確認のこと**)
> 具体的には下記の修正をしてるっぽい
> - Avoid using anything created
> DOMImplementation.createHTMLDocument()しないよう修正
> - scroll might not work
> Element.scrollTop, scrollLeftを参照しないよう修正
> - Cannot Create Elements
> Document.createElement()しないよう修正

### 静的リソースに格納する
[Trailhead: 静的リソースの使用](https://trailhead.salesforce.com/ja/content/learn/modules/visualforce_fundamentals/visualforce_static_resources)

# HTMLの実装
### 説明
- コンテナを置いてやるだけ
    - JavaScript上でinnerHTMLにCanvasを置いてやる
    - **HTML上に直置きしたCanvas要素を使うと怒られる**
        - Lightning Locker周りの制約と思われるけど詳細は不明

```test.html
<template>
  <div class="test-container"></div>
</template>
```

# JavaScriptの実装
### 説明
- (1) サードパーティライブラリ使用時のいつものやつで、メソッドをインポート、呼び出してやる
[Platform Resource Loader](https://developer.salesforce.com/docs/component-library/bundle/lightning-platform-resource-loader/documentation)

- (2) **予めレイヤー分けしたCanvasをテキストベースで用意**して、コンテナのinnerHTMLにセットしてやる
    - fabric.jsで使うCanvas要素は、一般的なペイントツールみたいにupper/lowerの二層にレイヤー分けされるらしいけど、この処理がまたLightning Locker周りの制約に引っかかってるっぽい
[canvasを便利にするfabric.jsの紹介と導入方法](https://qiita.com/seimiyajun/items/7c99271f0c720feac306)
[fabric.js公式の説明](https://github.com/fabricjs/fabric.js/wiki/How-fabric-canvas-layering-works)

- (3) コンテナの幅を取得して、それに基づいて適当にCanvasサイズを設定
    - 「コンテナの幅 = コンポーネント自体の幅」はプラットフォーム側で上手いことやってくれるので便利

- (4) 予め用意したCanvasのlowerレイヤーの方をベースにCanvasインスタンス生成
    - upperレイヤーの方とwrapperもそれぞれプロパティに渡してやる
    - "isDrawingMode" プロパティで手書きモードのon/offを制御

```test.js
import { LightningElement } from 'lwc';
// (1) いつもの
import { loadScript } from 'lightning/platformResourceLoader';
import fabricjs from '@salesforce/resourceUrl/fabricjs';

// (2) 階層化されたCanvas要素をjs側で用意
const CONTAINER_HTML = `<div class="test-wrapper">
	<canvas class="test-cvs-lower"></canvas>
	<canvas class="test-cvs-upper"></canvas>
	</div>`;

export default class Test extends LightningElement {
	// fabric.Canvasインスタンス用変数
	fab;

	// (1) 同じくいつもの
	connectedCallback(){
		Promise.all([
			loadScript(this, fabricjs)
		])
		.then(() => {
			// (2) 用意したCanvas要素をHTML上のコンテナにセット
			const container = this.template.querySelector('.test-container');
			container.innerHTML = CONTAINER_HTML;
			const wrapperEl = this.template.querySelector('.test-wrapper');
			const lowerEl   = this.template.querySelector('.test-cvs-lower');
			const upperEl   = this.template.querySelector('.test-cvs-upper');

			// (3) コンテナの幅に基づいてCanvasサイズを設定
			let cvsWidth  = container.getBoundingClientRect().width;
			let cvsHeight = container.getBoundingClientRect().width * 0.4;

			// (4) fabric.Canvasインスタンスの生成
			this.fab = new fabric.Canvas(lowerEl, {
				wrapperEl: wrapperEl,
				upperCanvasEl: upperEl,
				width: cvsWidth,
				height: cvsHeight,
				backgroundColor: 'rgb(230,230,230)',
				isDrawingMode: true,
			});
		});
	}
}
```

# 要点
- SalesforceLabs様を参考にライブラリのソースを弄ってやること
- HTML側にはコンテナだけ置いて、JavaScript側でinnerHTMLにCanvasをセットしてやること
- セットするCanvasはラッパー・下位レイヤー・上位レイヤーの構造にしてやること

# デモ
bandicamのロゴが麗しい
![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2539560/cb02f1dc-eefc-37ff-eee3-c99adcd057ae.gif)