canvasタグに現在描画されている内容を画像としてクライアントにダウンロードしてみます。
Webブラウザなどのクライアントだけで完結するためサーバの負荷などを気にする必要がありません。
今回はひたすらカウントアップするcanvasを、ボタンを押したタイミングでダウンロードできるサンプルです。
Canvasダウンロード
サンプル
以下のページから実際にサンプルを実行できます。
適当なタイミングで「ダウンロード」ボタンをクリックしてください。クリックした瞬間のcanvasの状態が画像としてダウンロードされます。
ソースコード
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>CANVAS ダウンロード</title>
<style>
#countup{ border: 1px solid gray; }
#btn-dl{ width: 300px; height: 50px; }
</style>
</head>
<body>
<h1>CANVAS ダウンロード</h1>
<section>
<!-- このCanvasをダウンロードします -->
<canvas id="countup" width="300" height="200"></canvas>
<p><button type="button" id="btn-dl"><img src="icon/download-solid.svg" width="32" height="32"></button></p>
</section>
<script>
window.onload = () => {
const canvas = document.querySelector("#countup");
const ctx = canvas.getContext("2d");
// カウントアップを開始
ctx.font = "38px serif";
ctx.fillStyle = "Red";
setInterval( () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillText(getFormatDate(), 10, 110);
}, 100);
// DLボタンをクリック
document.querySelector("#btn-dl").addEventListener("click", ()=>{
canvasDownload("#countup");
});
};
/**
* 現在時間をhh:mm:ss.msecで返却
*
* return {string}
*/
function getFormatDate(){
const time = new Date();
const str = ("0" + time.getHours()).slice(-2) + ':'
+ ("0" + time.getMinutes()).slice(-2) + ':'
+ ("0" + time.getSeconds()).slice(-2)
+ '.'
+ ("00" + time.getMilliseconds()).slice(-3);
return(str);
}
/**
* Canvasを画像としてダウンロード
*
* @param {string} id 対象とするcanvasのid
* @param {string} [type] 画像フォーマット
* @param {string} [filename] DL時のデフォルトファイル名
* @return {void}
*/
function canvasDownload(id, type="image/png", filename="canvas"){
const blob = getBlobFromCanvas(id, type); // canvasをBlobデータとして取得
const dataURI = window.URL.createObjectURL(blob); // Blobデータを「URI」に変換
// JS内部でクリックイベントを発動→ダウンロード
const event = document.createEvent("MouseEvents");
event.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
const a = document.createElementNS("http://www.w3.org/1999/xhtml", "a");
a.href = dataURI; // URI化した画像
a.download = filename; // デフォルトのファイル名
a.dispatchEvent(event); // イベント発動
}
/**
* 現状のCanvasを画像データとして返却
*
* @param {string} id 対象とするcanvasのid
* @param {string} [type] MimeType
* @return {blob}
*/
function getBlobFromCanvas(id, type="image/png"){
const canvas = document.querySelector(id);
const base64 = canvas.toDataURL(type); // "data:image/png;base64,iVBORw0k~"
const tmp = base64.split(","); // ["data:image/png;base64,", "iVBORw0k~"]
const data = atob(tmp[1]); // 右側のデータ部分(iVBORw0k~)をデコード
const mime = tmp[0].split(":")[1].split(";")[0]; // 画像形式(image/png)を取り出す
// Blobのコンストラクタに食わせる値を作成
let buff = new Uint8Array(data.length);
for (let i = 0; i < data.length; i++) {
buff[i] = data.charCodeAt(i);
}
return(
new Blob([buff], { type: mime })
);
}
</script>
</body>
</html>
ダウンロード部分は以下のページを参考にさせていただきました。
解説
大雑把なロジック
正直まどろっこしいのですが、canvasのデータを一発で変換できないので以下のような流れをたどります。
- CanvasをBase64に変換
- Base64をBlobに変換
- BlobをdataURIに変換
最終的にこのdataURIをJSで生成したaタグにセット、JSでクリックイベントを発生させダウンロードを行っています。
ここらへんもう少しかんたんに実装できる機能がHTML5標準でありそうな気もするんですけどね。
素材
- FontAwesome
参考ページ
- https://oar.st40.xyz/article/133
- https://developer.mozilla.org/ja/docs/Web/API/HTMLCanvasElement/toBlob
このブログを応援する
お寄せいただいたお気持ちは全額サーバ代や次の記事を執筆するための原資として活用させていただいております。この記事が参考になった場合などぜひご検討ください。