JavaScriptでcanvasをファイルに保存する方法(IE対応も)
前回、前々回と2回に渡りJavaScriptでcanvasタグを扱う方法について記事にしてきました。
ChromeはCanvasを右クリックして保存ができるとはいえ、それで終わらせるのもアレなので、Canvasの保存方法について解説していきます。
モダンブラウザでCanvasの保存をする方法
結論から言うとDataURIをクリックしたことにするのがてっとり早いです。
画像データをBase64エンコードされた文字列で表現すればimgタグに埋め込めることは前々回の記事で書いたと思います。
こんなの↓
<img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" >
ではこれと同じようにaタグのhrefにBase64を埋め込めば良いじゃないか、というのが主旨です。
■JavaScriptでcanvasを保存するコード例
function saveCanvas(canvas_id)
{
var canvas = document.getElementById(canvas_id);
//アンカータグを作成
var a = document.createElement('a');
//canvasをJPEG変換し、そのBase64文字列をhrefへセット
a.href = canvas.toDataURL('image/jpeg', 0.85);
//ダウンロード時のファイル名を指定
a.download = 'download.jpg';
//クリックイベントを発生させる
a.click();
}
■解説
- 指定されたIDからcanvasオブジェクトを取得
- aタグをメモリ中に作成
- aタグのhrefにDataURI化された文字列(data:image/jpeg;base64,ほにゃらら)をセット ※引数に指定している0.85はJPEGの品質85という意味です
- aタグのdownload属性にファイル名をセット
- aタグのclickイベントを発火
たぶんこれが一番てっとり早いと思います。
………………………Chromeとかのモダンブラウザであれば。
そう、このコード、IE11だと動かんとですよ…(´;ω;`)
IE11なんて近いうちに消えるブラウザのことなんか知るか、という方はここまでで構いません。
IE11でCanvasを保存する方法
IE専用の関数があるのでそれを使います。
■JavaScriptでcanvasを保存するコード例(IE11対応)
function saveCanvas(canvas_id)
{
var canvas = document.getElementById(canvas_id);
if (canvas.msToBlob) { //for IE
var blob = canvas.msToBlob();
window.navigator.msSaveBlob(blob, 'download.png');
} else {
//アンカータグを作成
var a = document.createElement('a');
a.href = canvas.toDataURL('image/jpeg', 0.85);
//ダウンロード時のファイル名を指定
a.download = 'download.jpg';
//クリックイベントを発生させる
a.click();
}
}
■解説
- 指定されたIDからcanvasオブジェクトを取得
- canvasオブジェクトのmsToBlobがあるかどうかでIEかどうか判断
- msToBlobでblobデータを取得
- msSaveBlobでblobデータを保存
おぉ実質たったの2行じゃないか素晴らしい!!
…………お気づきだろうか。
こっそり、download.png と書かれていることを。
そう、msToBlobはcanvasの描画データをそのままblobにしちゃうので、png固定なんですわ。
じゃあ、canvas.toDataURL('image/jpeg', 0.85); を使ってJPEGのBase64データを作ってから、blobデータに変換すれば良いじゃん、と言われればそのとおり。問題はIE11にtoBlob関数がないこと。
IE11には対応したいけど、別にPNGだけでええわ、という人はここまででOKです。
IE11でCanvasを保存する方法(JPEG対応)
canvas.toDataURL('image/jpeg', 0.85);はIE11でも問題なく動作するため、Base64からblobデータへの変換だけ自前で実装してあげる必要があります。
■Base64をblobに変換する例
function toBlob(base64) {
var bin = atob(base64.replace(/^.*,/, ''));
var buffer = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++) {
buffer[i] = bin.charCodeAt(i);
}
var blob = new Blob([buffer.buffer], {type: 'image/png'});
return blob;
}
とかえらそうに書いていますが、ググればそこら中に転がっている変換方法と特に変わりありません(;´∀`)
いやぁだって、Base64ってそもそもバイナリを文字列にしただけだから、文字列を数値に戻すだけですしね…。
たぶん、「Base64 blob 変換」とかで検索すればいろいろサンプルが出てくるのでお好みのを使えば良いと思います。
ソース全体
canvasに画像をロードして、文字を書き込んで、保存をする、という一連の流れをモダンブラウザとIE11に対応した例です。
ちょっと長いですが、全体が知りたいという方もいるかもなので載せておきます。
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>canvasへの文字描画</title></head>
<body onload="loadImage('preview');">
<p><canvas id="preview"></canvas></p>
<p>
<input type="text" id="canvas_text" value="我輩は犬である">
<button onclick="drawText('preview', 'canvas_text');">文字を描く</button>
<button onclick="saveCanvas('preview');">保存する</button>
</p>
<script>
//キャンバスに画像を描画する
function loadImage(id)
{
//画像を読み込んでImageオブジェクトを作成する
var image = new Image();
image.src = 'canvas.jpg';
image.onload = (function () {
//画像ロードが完了してからキャンバスの準備をする
var canvas = document.getElementById(id);
var ctx = canvas.getContext('2d');
//キャンバスのサイズを画像サイズに合わせる
canvas.width = image.width;
canvas.height = image.height;
//キャンバスに画像を描画(開始位置0,0)
ctx.drawImage(image, 0, 0);
});
}
//キャンバスに文字を描く
function drawText(canvas_id, text_id)
{
var canvas = document.getElementById(canvas_id);
var ctx = canvas.getContext('2d');
var text = document.getElementById(text_id);
//文字のスタイルを指定
ctx.font = '32px serif';
ctx.fillStyle = '#404040';
//文字の配置を指定(左上基準にしたければtop/leftだが、文字の中心座標を指定するのでcenter
ctx.textBaseline = 'center';
ctx.textAlign = 'center';
//座標を指定して文字を描く(座標は画像の中心に)
var x = canvas.width / 2;
var y = canvas.height / 2;
ctx.fillText(text.value, x, y);
}
//Base64データをBlobデータに変換
function toBlob(base64) {
var bin = atob(base64.replace(/^.*,/, ''));
var buffer = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++) {
buffer[i] = bin.charCodeAt(i);
}
var blob = new Blob([buffer.buffer], {type: 'image/png'});
return blob;
}
//
function saveCanvas(canvas_id)
{
var canvas = document.getElementById(canvas_id);
var uri = canvas.toDataURL('image/jpeg', 0.85);
if (canvas.msToBlob) { //IE対応
var blob = toBlob(uri);
window.navigator.msSaveBlob(blob, 'download.jpg');
} else {
//アンカータグを作成
var a = document.createElement('a');
a.href = uri;
a.download = 'download.jpg';
//クリックイベントを発生させる
a.click();
}
}
</script>
</body>
</html>
どうですかね。
モダンブラウザとIE11の両方に対応したわりにはけっこうシンプルに出来たと思いますが。
まとめ
- canvasの保存はモダンブラウザならBase64の文字列をクリックしたことにすれば簡単
- IE11でもPNG限定であれば専用の関数を2つ使うだけで簡単
- IE11に対応しつつJPEGに変換してからダウンロードしたいなら自前でBase64→blobの変換が必要
とまぁこんな感じでしょうか。
canvas使う需要がどのくらいあるかわかりませんが、使い始めるとけっこうコレが便利なものなので、活用してくださる方がいることを願います(*´ω`*)