skip to content
私的歌詞倉庫

オフスクリーンcanvasを作ってpng画像としてダウンロードする

/ 4 min read

Updated:
Table of Contents

はじめに

自分はStarryKidsという入力したテキストを月文字に変換するサービスを作っています。

TOP

今までは月文字に変換するだけの機能だったのですが、画像として保存したいなと思い、画像ダウンロード機能を作りました。意外と大変だったので、どうやって作ったか解説したいなと思います。

仕組みとしては、オフスクリーンcanvas要素を作って、月文字を一行ずつ描画し、最後にpng画像としてダウンロードするようにしました。

作ったときのPull Requestはこちらです。

Lorem ipsum dolor sit amet, consectetur adipiscing elit.
00K00KMIT

作り方

このように実装しました(説明用に↑のコードとは微妙に変更してます)。

実際には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で取れなかったので、actualBoundingBoxAscentactualBoundingBoxDescent(テキストの基準線をもとに外形線の上と下までの距離)を足した値を一行あたりの高さとしています。

最後に、canvasに描画した内容を画像に変換し、擬似的にクリックしてダウンロードするようにしました。

終わりに

canvasでは改行コードを認識してくれないのは知らなかったので勉強になりました。オフスクリーンcanvasは便利ですね。

参考文献