背景と本記事の方針
- そんなような業務要件があったので「きついっすねーどこのパッケージ買いましょうね」と受け流してたんですが、個人の趣味の範囲でちょっと書いてみたら動きはしたので、せっかくなので記事にします
- LWC,JavaScript,HTML一般に関する知識/技術は持ち合わせていないのでほぼスルーか参考リンク載せるだけ。素人がググりながらどうにか動くとこまでこぎ着けた過程を記します
- 間違ったこと書いてる、読みづらい等あればコメントください
忙しい人のための解答
SalesforceLabsがGithub上に「これぞ」という完成品を公開してくれてます感謝感謝感謝
https://github.com/SalesforceLabs/DrawAnnotations
実装の前提
目標
電子署名もどきにお絵描きができるLightningコンポーネントをつくりたい
ライブラリについて
-
素の状態でも十分書けるようだけど、今回は外部ライブラリを使って実現
-
ライブラリ選定
パッと調べた結果、以下2つがヒット。今回は諸々考慮しfabric.jsを採用
jSignature | fabric.js | |
---|---|---|
機能概要 | 手書き署名専用 | 汎用的な画像・図形操作 |
要件充足 | 〇 | 〇 |
ライセンス | MIT | MIT |
最新リリース | 2012.11.1 | 2022.2.21 |
下準備
ライブラリのソースを貰ってくる
- distディレクトリの fabric.js (minじゃない方)
Github: fabric.js
弄る
- このfabric.js、一部処理にてLWC実装時のセキュリティ要件(Lightning Lockerかな?)を満たしていないらしい
-
SalesforceLabsが公開するソース上で修正済みのfabric.jsソースが見られるので、そちらを参考に修正
(SalesforceLabsの修正済ソースと、同verの元ソースとで差分確認のこと)
具体的には下記の修正をしてるっぽい
- Avoid using anything created
DOMImplementation.createHTMLDocument()しないよう修正- scroll might not work
Element.scrollTop, scrollLeftを参照しないよう修正- Cannot Create Elements
Document.createElement()しないよう修正
静的リソースに格納する
HTMLの実装
説明
- コンテナを置いてやるだけ
- JavaScript上でinnerHTMLにCanvasを置いてやる
-
HTML上に直置きしたCanvas要素を使うと怒られる
- Lightning Locker周りの制約と思われるけど詳細は不明
test.html
<template>
<div class="test-container"></div>
</template>
JavaScriptの実装
説明
-
(1) サードパーティライブラリ使用時のいつものやつで、メソッドをインポート、呼び出してやる
Platform Resource Loader -
(2) 予めレイヤー分けしたCanvasをテキストベースで用意して、コンテナのinnerHTMLにセットしてやる
- fabric.jsで使うCanvas要素は、一般的なペイントツールみたいにupper/lowerの二層にレイヤー分けされるらしいけど、この処理がまたLightning Locker周りの制約に引っかかってるっぽい
canvasを便利にするfabric.jsの紹介と導入方法
fabric.js公式の説明
- fabric.jsで使うCanvas要素は、一般的なペイントツールみたいにupper/lowerの二層にレイヤー分けされるらしいけど、この処理がまたLightning Locker周りの制約に引っかかってるっぽい
-
(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はラッパー・下位レイヤー・上位レイヤーの構造にしてやること