オフスクリーンcanvasを作ってpng画像としてダウンロードする
/ 4 min read
Updated:Table of Contents
はじめに
自分はStarryKidsという入力したテキストを月文字に変換するサービスを作っています。
今までは月文字に変換するだけの機能だったのですが、画像として保存したいなと思い、画像ダウンロード機能を作りました。意外と大変だったので、どうやって作ったか解説したいなと思います。
仕組みとしては、オフスクリーンcanvas要素を作って、月文字を一行ずつ描画し、最後にpng画像としてダウンロードするようにしました。
作ったときのPull Requestはこちらです。
作り方
このように実装しました(説明用に↑のコードとは微妙に変更してます)。
実際にはVueで作っているので関数として作成し@click でイベントが発火するようにしました。
const mooonText = "月文字がここに入ってくる"const canvas = document.createElement("canvas");const ctx = canvas.getContext("2d")!;
const lines = mooonText.split("\n");
ctx.font = "10px serif";// 1行あたりの高さを取得const lineHeight = ctx.measureText(mooonText).actualBoundingBoxAscent + ctx.measureText(mooonText).actualBoundingBoxDescent;
canvas.width = ctx.measureText(lines[0]).width;canvas.height = lineHeight * lines.length;
lines.map((line, index) => { ctx.fillText(line, 0, 0 + (index + 1) * lineHeight);});
const imageDataURL = canvas.toDataURL("image/png");const link = document.createElement("a");link.download = "image.png";link.href = imageDataURL;link.click();最初はcanvas内にそのまま月文字を入れればいいと思っていたのですが、どうやらcanvas内では改行コードを認識してくれないっぽく、ただ横一列に月の絵文字が並ぶだけになってしまいました。
そのため最初に改行コードで分割して配列に入れてmapで一行ずつ描画するようにしました。y軸の高さはindex+1しないと、初回描画時にy=0の箇所に描画するので月文字がかけてしまいます。
canvas要素自体の縦は一行あたりの月文字の横幅(ctx.measureText(lines[0]).width)をそのまま使っています。
ただし、縦幅に関してはそのままheightで取れなかったので、actualBoundingBoxAscentとactualBoundingBoxDescent(テキストの基準線をもとに外形線の上と下までの距離)を足した値を一行あたりの高さとしています。
最後に、canvasに描画した内容を画像に変換し、擬似的にクリックしてダウンロードするようにしました。
終わりに
canvasでは改行コードを認識してくれないのは知らなかったので勉強になりました。オフスクリーンcanvasは便利ですね。