2014年6月23日月曜日

【実験】GIFアニメーション(GIF89a)をSVGで実現してみた

実験意図


つい先日、TwitterがGIFアニメーション(以下GIF)に対応した。

ショック! Twitterがサポートを始めたGIFアニメはGIFではなかった―しかしうまい考え – Techcrunch

Google+もすでに対応済みで、あと主要なSNSとしてはFacebookだが、おそらく時間の問題だろう。

GIF対応の理由は分かる。それは動画はクリックしないと再生されないからだ。特にごく短い動画を再生させるためにいちいちユーザーに対してクリックを強要するのか?ということだろう。実際、おもしろいGIF動画があることも事実だ。

しかし、GIFは25年も前に作られた「インターネットの化石」とでも言うべき画像フォーマットだ。

Graphics Interchange Format - Wikipedia

しかも最大で256色しか使えないので基本的に画質が悪い。それでも…

「WEBの世界はこれからも永遠にGIFを使い続けるのか?本当にこれがWEBの未来なのか?」

と強い疑問を感じて色々と考えていたら、GIFと同じことをSVGで実現できるのではないか?と思いついた。

SVGにはGIFと比べて様々なメリットがある。

  1. 基本的にベクター形式の画像ファイルフォーマットだが、ビットマップ形式の画像を埋め込むとができる(ベクターも使える)
  2. フルカラーの画像を使える(256色に制限されない)
  3. スクリプトにより複雑なアニメーションをさせることができる
  4. 強力な画像フィルターを使うことができる
  5. GIFはバイナリーファイルだが、SVGは基本的にテキストファイルなので画像作成ツールがより作りやすい

などなど。そして、いわゆるモダンなブラウザは皆SVGに対応していて環境も揃ってきている。

しかし、本当にできるのか?実験してみることにした。


デモ(実験結果)


実験は成功。


GIFとSVGを両方表示して比較できるようにした。ページが重いのでモバイルでは注意が必要だ。

GIF vs SVG (別タブで表示します。約1.3MB。)


アニメーション速度的にも画質的にもオリジナルと見分けがつかないと思うが、どうだろうか?GIFとSVGのアニメーションが少しずれるのは、SVGのアニメーションの開始が少し遅れて始まるためだ。

全成果物はGitHubにアップした。
junkoro/gifvssvg


メイキング


アニメーションは上のWikiに張ってあった地球のアニメーションを使うことにした。


ImageMagickでコマ画像に分解


まずGIFをImageMagickを使ってPNGのコマ画像に分解した。総コマ数は44コマで、総ファイル容量は1,577,611バイト。元のGIFは316,029バイトなので5倍程度に増加したことになる。

# mkdir pre_optim
# convert -coalesce rotating_earth.gif pre_optim/rotating_earth%02d.png
# cp -r pre_optim post_optim


ImageOptimで各コマを最適化


次にImageOptimを使ってpost_optimにコピーした各コマを最適化した。この時点で936,276バイトに減少。


node.jsでコマ画像をbase64化>imageタグ化>xmlファイル書き出し


次にnodeを使って最適化した各コマをbase64にエンコードしつつ、imageタグとしてxmlファイルに書き出す。この時点で1,252,564バイトに増加(ノ∀`)アチャ

var fs = require('fs');

var path = "./post_optim/";
var fileNames = fs.readdirSync(path);
fileNames = fileNames.filter(function(element, index, array) {
  return element.match(/\.png$/);
});

var xml = "";
for (var i = 0; i < fileNames.length; i++) {
  var fileName = fileNames[i];
  console.log("encoding:" + fileName);
  var file = fs.readFileSync(path + fileName);
  var base64 = new Buffer(file).toString('base64');
  var imgTag = "<image width='200' height='200' style='display: none;' xlink:href='data:image/png;base64,";
  imgTag += base64;
  imgTag += "' />\n";
  xml += imgTag;
}

console.log("writing imgTags.xml");
fs.writeFileSync("imgTags.xml", xml);

# node encode2xml.js


SVGにimageタグとアニメーションスクリプトを埋め込み


こんな感じで、書き出したimageタグをSVGに埋め込みつつ、各コマを90msecごとに表示したり非表示にしたりするJavaScriptも埋め込む。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve"
     width="200" height="200">

  <image width='200' height='200' style='display: none;' xlink:href='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMg・・・' />
  <image width='200' height='200' style='display: none;' xlink:href='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMg・・・' />
  ・・・・

  <script type="text/javascript">
  <![CDATA[

    var imgs = document.getElementsByTagName("image");

    var currImgIdx = 0;
    setInterval(function() {

      var prevImgIdx = -1;
      if (currImgIdx == 0) {
        //console.log("start");
      } else if (currImgIdx > imgs.length - 1) {
        //console.log("end > loop");
        prevImgIdx = currImgIdx - 1;
        currImgIdx = 0;
      } else if (currImgIdx != 0) {
        //console.log("normal");
        prevImgIdx = currImgIdx - 1;
      }

      if (prevImgIdx != -1) {
        var imgPrev = imgs[prevImgIdx];
        imgPrev.style.display = "none";
      }

      var imgCurr = imgs[currImgIdx];
      imgCurr.style.display = "block";

      currImgIdx++;

    }, 90);

  ]]>
  </script>

</svg>


SVGZに圧縮


完成したSVGをgzipでSVGZに圧縮する。

# gzip -9 --stdout svganim.svg > svganim.svgz

これで完成。最終的には943,22バイトになった。元のアニメーションGIFが316,029バイトなので、ファイル容量的には3倍近く増えてしまったことになる(ノ∀`)アチャー


結論:採用を検討する価値あり


実験は成功したものの、いくつか問題点が見つかった。

ファイル容量の増加


GIFに較べてファイル容量が増加する可能性があるが、背景が変更されない単純なスプライトアニメーションなどの場合は数十分の一になる可能性もある。さらに、WebPなどのより圧縮率の高い画像形式を使えば効率が高くなる可能性もある。


サーバーがSVGZのmime-typeに対応していない場合が多い


デモサイトはGoogleDriveを使ったが、残念ながらSVGZに対応していないためにやむなく非圧縮のSVGを使わざるを得なかった。これはサーバーの設定次第なので、独自サイトの場合は問題にはならないだろう。


変換に手間がかかる


もっと簡単に変換できるツールを開発する必要がある。


いくつか問題はあるが、SVGのメリットを考えれば十分に採用を検討する価値があるのではないだろうか。