JavaScriptでjsgifを使ってアニメーションGIFを動的生成する
先日の記事、「JavaScriptでcanvasを録画して動画ファイルに保存する方法」では複数の画像ファイルからwebm形式の動画を生成しましたが、Twitter等のSNSではwebm形式に対応していないようなので、今回は汎用性の高いアニメーションGIFを作ってみようと思います。
必要なライブラリ
canvasに描いた画像をアニメーションGIFにしてくれるjsgifというオープンソースのライブラリがあるので、ありがたく使わせて頂きましょう。
https://github.com/antimatter15/jsgif
リンク先のZIP内にある下記4つのJavaScriptファイルを読み込んで使う感じです。
- LZWEncoder.js
- NeuQuant.js
- GIFEncoder.js
- b64.js
HTMLの例
<script src="/js/b64.js"></script>
<script src="/js/LZWEncoder.js"></script>
<script src="/js/NeuQuant.js"></script>
<script src="/js/GIFEncoder.js"></script>
<div id="anime">
<img src="[画像ファイル01]" width="80" />
<img src="[画像ファイル02]" width="80" />
<img src="[画像ファイル03]" width="80" />
<img src="[画像ファイル04]" width="80" />
<img src="[画像ファイル05]" width="80" />
<img src="[画像ファイル06]" width="80" />
<img src="[画像ファイル07]" width="80" />
<img src="[画像ファイル08]" width="80" />
<img src="[画像ファイル09]" width="80" />
<img src="[画像ファイル10]" width="80" />
<img src="[画像ファイル11]" width="80" />
<img src="[画像ファイル12]" width="80" />
<img src="[画像ファイル13]" width="80" />
</div>
<p>速度:<input type="range" id="anime_speed" value="500" min="100" max="500"></p>
<p><button onclick="createGIF();">アニメGIFを作成する</button></p>
<canvas id="canvas" style="display:none;"></canvas>
<p><img id="anime_gif" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="></p>
<p><button id="download" onclick="downloadGIF();" style="display:none;">ダウンロード</button></p>
解説
最初の4つのスクリプト読み込みはjsgif用です。それぞれのjsファイルを置いたパスに適宜書き換えてください。
次にGIFアニメーションにする画像ファイルを並べます。13枚という数に特に意味はありません。以前撮った写真を使いまわしているだけですw
任意の画像を選択してアニメーションGIFを作りたい場合はこの部分を「JavaScriptでFileReaderを使った複数画像のプレビュー例」あたりを参考に書き換えると良いでしょう。
input type="range"の速度調整は「JavaScriptで画像を連続表示してコマ撮りムービー風の表示をしてみる」で使ったスライダーですが、そのときと違ってCSSによる左右反転はしていません。というのも、Chromeだと良い感じでしたが、Edgeだと反転させただけでは見た目がイマイチだったんですよね。
それなら変に小細工せずにそのまま使っちゃおうかな、と。
canvasはアニメーションGIFの作成時の一時的な保管場所として使うだけなのでdisplay:none;で非表示にしてあります。
また、imgタグのID="anime_gif"には完成したアニメーションGIFが入る予定なので、空の画像 (data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)を入れています。このエンコード、わざわざファイルを用意しなくても良いので地味に便利。
ボタン類はJavaScriptのほうで解説しましょう。
JavaScriptの例
<script>
var encoder;
function createGIF()
{
//canvasの取得
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//GIFEncoderの初期処理
encoder = new GIFEncoder();
encoder.setRepeat(0); //繰り返し回数 0=無限ループ
encoder.setDelay(document.getElementById('anime_speed').value); //1コマあたりの待機秒数(ミリ秒)
encoder.start();
//画像ファイル一覧を取得
frames = document.getElementById('anime').getElementsByTagName('img');
//canvasのサイズを1枚目のコマに合わせる
canvas.width = frames[0].naturalWidth;
canvas.height = frames[0].naturalHeight;
//全ての画像をcanvasへ描画
for (var frame_no = 0; frame_no < frames.length; frame_no++) {
ctx.drawImage(frames[frame_no], 0, 0);
encoder.addFrame(ctx); //コマ追加
}
//アニメGIFの生成
encoder.finish();
document.getElementById('anime_gif').src = 'data:image/gif;base64,' + encode64(encoder.stream().getData());
//ダウンロードボタンを表示
document.getElementById('download').style.display = 'block';
}
function downloadGIF()
{
encoder.download("download.gif");
}
</script>
解説
[アニメGIFを作成する]というボタンから呼ばれるcreateGIF関数と、[ダウンロード]ボタンから呼ばれるdownloadGIF関数を用意しました。
createGIFでは、
- canvasの取得
- GIFEncoderの初期処理(速度とか繰り返し回数とか)
- 画像ファイルの一覧取得
- canvasの大きさ調整
- 全画像をcanvasへ書きつつ、GIFEncoderへコマ追加
- 最後に生成されたデータをimgタグのsrcへセット
- [ダウンロード]ボタンの表示
というようなことを実行しています。
downloadGIFでは、GIFEncoderのdownload関数を呼び出しているだけですね。そのために変数encoderをグローバル変数として宣言しています。
自前でファイルを作りたい人は下記のように encoder.stream().bin からblobデータを生成して、アンカータグのURLに埋め込むなんて方法もアリです。
var bin = new Uint8Array(encoder.stream().bin);
var blob = new Blob([bin.buffer], {type: 'image/gif'});
var dataUrl = window.URL.createObjectURL(blob);
var anchor = document.createElement('a');
anchor.download = 'download.gif';
anchor.href = dataUrl;
実行結果
速度:
[アニメGIFを作成する]ボタンをクリックするといつものクマちゃんのアニメーションGIFが生成されて表示されるハズです。
(PCスペック次第ですが、生成まで少し時間がかかるかも)
一応、ChromeとEdgeで動作確認済み。
アニメーションGIFって256色しか使えないからかなり画質が劣化するだろうと思いましたが、意外と綺麗ですね。