🖼️
canvasで激軽アイキャッチジェネレーターを作成する
成果物
- あいきゃっちじぇねれーた / github
- canvas + Next.jsで作成。フォームに入力したものをcanvasに出力してる。
- ↓みたいなのができます。
canvasって何?
- ブラウザ上で2Dを描画できるようにするためのWeb APIのひとつ。
- クライアントサイドではライブラリ不要な上に、IEでも使える。
- サーバサイドでもnode-canvas等をインストールすれば使える。OGP動的生成によく使われてるっぽい。
基本的なcanvasの扱い方
- 絶対座標で物体を配置していくだけ
- 再描画するときは全消しからのすべて再配置
実装
ざっくり流れ
- フォームとjs部のデータやり取りは
useState
を利用 -
useEffect
を利用してフォームへの入力を検知し、フォームに入力されたらするように再描画の関数を呼ぶ
canvasの部分に関してだけ解説
まずすべてクリア
- canvasのwidth/heightは実際に表示されているcanvasの要素から取得
const canvas: NullableHTMLCanvasElement = getCanvasRef();
if(!canvas) return
ctx.clearRect(0, 0, canvas.width, canvas.height);
画像を描画
-
drawImage
を使って、canvasに画像をはりつけ。画像の左上を左上にあわせたいので0, 0
を設定。
if (outputImage !== null) {
ctx.drawImage(outputImage, 0, 0);
}
文字を設定
- 改行を反映したいので、改行文字を読み取って分割。
- 1行ごとに処理をしていく。
ctx.font = `${fontSize}px serif`
const splitedText = text.split("\n")
splitedText.forEach((target, index) => {
<<中略>>
})
文字の後ろのマスクの大きさを計算・描画(forEachの中部分)
- 描画順で出るので、先に文字の下のマスクを描画。
- 高さはMDNを参考にちょっと不思議なとりかたをする。
-
fillStyle
で色を設定しておく。文字の色も同じ方法で指定する。 - 矩形は
ctx.fillRect(x, y, width, height)
で指定できるので、文字に対してうまい位置になるようにする。今回は複数行対応したいので、タテのY座標は行数にあわせて色々追加。 - 今回は矩形と文字の間の
margin
もよしなになるように計算式に追加。
const measure = ctx.measureText(target)
var textWidth = measure.width;
var textHeight = measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent
if (target.length !== 0) {
ctx.fillStyle = rgbaFormatter({...backgroundRgb, alpha: backgroundOpacity})
ctx.fillRect(
(canvasWidth - textWidth) / 2 - xMargin,
(canvasHeight - textHeight) / 2 - yMargin + (textHeight + yMargin * 2) * (index - 1),
textWidth + xMargin * 2,
textHeight + yMargin * 2 + 2
);
}
文字の位置を計算・描画(forEachの中部分)
- 文字サイズは上で計算したので、ここでは位置を計算して描画するのみ。
ctx.fillStyle = rgbaFormatter({ ...textRgb, alpha: textOpacity })
ctx.fillText(
target,
(canvasWidth - textWidth) / 2,
(canvasHeight / 2) + textHeight / 2 + (textHeight + yMargin * 2) * (index - 1)
);
以上
- 以上を入力のたびに発生させたいので、
useEffect
で一連の流れを発生させる。
おわりに
- XY座標とタテヨコの長さを組み合わせるだけなのでHTMLの要素の配置よりはずっとシンプルで簡単。
- 動作としても軽くてサクサクですごいいい。コードとして全消し再描画ってどうなのと思ってたけど、全くチラつきがないし色々値変えても重くなったりしないので最高。
Discussion