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();
}

■解説

  1. 指定されたIDからcanvasオブジェクトを取得
  2. aタグをメモリ中に作成
  3. aタグのhrefにDataURI化された文字列(data:image/jpeg;base64,ほにゃらら)をセット ※引数に指定している0.85はJPEGの品質85という意味です
  4. aタグのdownload属性にファイル名をセット
  5. 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();
	}
}

■解説

  1. 指定されたIDからcanvasオブジェクトを取得
  2. canvasオブジェクトのmsToBlobがあるかどうかでIEかどうか判断
  3. msToBlobでblobデータを取得
  4. 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使う需要がどのくらいあるかわかりませんが、使い始めるとけっこうコレが便利なものなので、活用してくださる方がいることを願います(*´ω`*)

『JavaScriptでcanvasをファイルに保存する方法(IE対応も)』へのコメント

  1. 名前:ほそかわ 投稿日:2019/10/06(日) 13:07:08 ID:060513d72 返信

    大変参考にさせて頂いております。
    早速ですが、ソース全体のコードを使用し、
    image.src = ‘canvas.jpg’;
    の部分の画像ファイルをローカルファイルに
    書き換えて画像を表示するところまでは出来たのですが、
    「保存する」ボタンを押下してもファイル保存のダイアログが
    表示されず、保存が出来ませんでした。

    IE11では、問題なく保存する事が出来たのですが、
    クロームでは何故か上記の状態でして、保存する事が出来ません。

    対処方法などあれば、ご教示頂けませんでしょうか。

    • 名前:とっちら 投稿日:2019/10/06(日) 18:18:26 ID:d944c3397 返信

      そういうときは[F12]でコンソールログを確認し、どんなエラーが出ているか確認すると良いでしょう。
      恐らく Failed to execute ‘toDataURL’ といったエラーが出ているのではないかと思われます。
      Chromeでローカルに置いたhtml上からファイル操作を行おうとするとセキュリティ関連で弾かれます。
      xampp等でWebサーバーを起動し、localhostへアクセスすれば問題ないはずです。

  2. 名前:ほそかわ 投稿日:2019/10/06(日) 20:54:27 ID:060513d72 返信

    ご回答有難う御座います。
    仰る通りのエラーが出ておりました。。

    htmlファイルの自分のサーバーにアップロードしまして、
    画像ファイルも同じディレクトリにアップしました。

    その後、開いてみた所、正常にダウンロードする事が出来ました。

    有難う御座いました。