みんなの「作ってみた」

ブラウザのスクショを繋げてGIF画像を作ろう【Puppeteerを使って爆速】

2019/03/19

ToshioAkaneya
ToshioAkaneya
Ruby on RailsとVueでプログラミングスクールのレビューサイトを運営しています。良ければご覧ください。

こんにちは。
俳句や川柳から動くGIF画像を生成できるWebサービス「五七五メーカー」をリリースしたアカネヤ(@ToshioAkaneya)です。

ユーザーが入力した俳句からGIF画像を生成しています。
これはサーバーでブラウザ(Chrome)を立ち上げ、そのスクリーンショットを繋げることで生成されています。

どうやってGIF画像を作るのか、コード付きで解説

今回使用する言語はNodejsです。
まずは適当な空ディレクトリに移動して、以下のコマンドを実行して下さい。

$ npm init -f
$ npm install puppeteer  png-js gifencoder

使用する3つのライブラリを簡単に説明します。
まずPuppeteerは、GUIを操作することなく、プログラムからAPIでブラウザ(Chrome)を制御できるNode.jsで作られたライブラリ です。今回はPuppeteerのスクリーンショット機能を利用します。
png-jsはPuppteerで撮ったスクショのPNGデータをNodeで扱うためのライブラリで、gifencoderはpngからGIFを作るためのライブラリです。

次に、index.jsを作成し、次のようにして下さい。一回コピペして頂き、動作を確認するのが良いと思います。

index.js
const fs = require('fs');
const puppeteer = require('puppeteer');

const GIFEncoder = require('gifencoder');
const PNG = require('png-js');
function decode(png) {
  return new Promise(r => {
    png.decode(pixels => r(pixels))
  });
}

async function gifAddFrame(page, encoder) {
  const pngBuffer = await page.screenshot({clip: {width: 1024, height: 200, x: 0, y: 0}});
  const png = new PNG(pngBuffer);
  await decode(png).then(pixels => encoder.addFrame(pixels));
}

(async () => {
  const browser = await puppeteer.launch({
    headless: false, slowMo: 0,
  });
  const page = await browser.newPage();
  page.setViewport({width: 1024, height: 200});
  const text = '私は猫になりたい';
  const html = `<div id="main" style="font-size: 100px;">${text[0]}</div>`;
  await page.setContent(html, {
    waitUntil: ['networkidle0']
  });

  // record gif
  var encoder = new GIFEncoder(1024, 200);
  encoder.createWriteStream()
    .pipe(fs.createWriteStream('test.gif'));

  // setting gif encoder
  encoder.start();
  encoder.setRepeat(0);
  encoder.setDelay(150);
  encoder.setQuality(10); // default

  for (let i = 0; i < 10; i++) {
    await gifAddFrame(page, encoder);
    await page.evaluate(`document.getElementById("main").innerHTML = '${text.slice(0, i + 1)}'`)
  }

  // finish encoder, test.gif saved
  encoder.finish();

  await browser.close();
})();

そして実行します。

$ node index.js
動作が終了すると、test.gifというファイルが出来ているはずです。
それを開くと、次のような画像が出来ています!

詳しく解説

先ほどのコードを読み解くための解説をします。

全体の流れは
PuppeteerでHTMLをレンダリングさせる(この時、「私は猫になりたい」の、「私」だけをレンダリングします。)
以下ループ
→スクショを撮る
→gifencoderにスクショのデータを読ませる
⇨Puppeteerに、ブラウザでJSを実行するように指示する。

です。最後のJSを実行というのは、

await page.evaluate(`document.getElementById("main").innerHTML = '${text.slice(0, i + 1)}'`)

の部分です。ブラウザが表示するHTMLを変えるようなJSを実行することで、アニメーションを作ることが出来るのです。
あとはコードを読んでもらえると、理解できると思います。

まとめ

五七五メーカー」を作るにあたり、どうすれば良いか試行錯誤したところ、この形になりました。
GIF生成が出来るようになると、面白いサービスがたくさん出来そうでワクワクしますね。
最後まで読んでいただきありがとうございます。いいね、ストック、コメントをお待ちしております。
それでは、最後に一句。

終わりに

Ruby on RailsとVueで作成したプログラミングスクールのレビューサイトを運営しています。良ければご覧ください。https://school-report.com/