JavaScript
HTML5
canvas
Slack

治安の悪い Slack Emoji を作るツールを作った

(治安の悪くない Emoji も作れます)

作ったもの

emoji1.gif emoji3.gif emoji4.gif emoji5.gif

emoji2.gifemoji2.gifemoji2.gifemoji2.gif

ここで遊べます https://zk-phi.github.io/MEGAMOJI/

おもしろいところ

GIF アニメのエンコードまですべて js で完結しているので、ありがちな「謎のサーバーに画像アップロードするといい感じに変換してくれる」的なサービスと違って、素性の知れたコードがクライアント側でサクサク動きます。

なにができるの?

画像を 128px x 128px に変形

画像を、 Slack にアップロードできる(現状)最大サイズの 128px x 128px に変形します。

ローカルのファイルから選ぶか、画像の URL を入力できます。アップロードするわけではないので、デカい画像でもサクサクなのがお気に入りです。

スクリーンショット 2018-06-18 1.00.26.png

変形は

  • 正方形に引き伸ばし(アス比無視)
  • 正方形いっぱいに拡大して、余ったところはトリミング(アス比維持)
  • 正方形に収まるように縮める(アス比維持)

から選べます。

スクリーンショット 2018-06-18 1.05.12.png

テキストから画像生成

既存の画像を使う代わりに、テキストから画像を生成することもできます。

生成したテキスト画像も、普通の画像と同じく、引き伸ばしたり、縮小したりして正方形に収められます。

フォントは CSS っぽい感じで指定します。

スクリーンショット 2018-06-18 1.23.03.png

アニメ

GIF アニメを作ります。節度を持ってどうぞ 😇

Slack にアップロードできるファイルサイズ制限の都合で、解像度が 1/4 (64px x 64px) になります。

キラ

emoji1.gif

絵文字がスーパーレアになります。パーティ感が出ます。

白、黒など無彩色のところはキラキラしないので、うまく使ってください。

スクロール

emoji2.gif

絵文字がスクロールします。複数並べてループできます。

emoji2.gifemoji2.gifemoji2.gifemoji2.gif

ぐるぐる

emoji3.gif

絵文字が回転します。

ガタガタ

emoji5.gifemojix.gif

絵文字がガタガタします。飲みたくて飲みたくて震える

and more ...

プルリク待ってます✨

巨大 Emoji

画像を複数の正方形に分割して、連結すると一枚の絵になるような Emoji が作れます

emoji6.png
emoji7.png

巨大 Emoji にもアニメーションをつけられます (ロードのタイミングの都合で微妙に連動しないことがあります)。

emoji8.gif
emoji9.gif

技術的な話

申し訳程度に技術的にハマったところとか学んだところとか。

<img>crossorigin プロパティ

img タグは src プロパティにヨソの URL を設定してもちゃんと読んでくれるのですが、読んだ画像を canvas に転写してゴリゴリ変形しようとかやると、同一生成元ポリシが発動して無が転写されます。

適切なヘッダが設定されていれば、 imgcrossorigin プロパティを設定するだけで CORS できるようなので、それを使いましたが、気付くまではハマりました。

canvas で文字列画像を作る

canvas で文字列画像を作るのが地味に大変というのがわかりました。

  • canvas.ctx.fillText が複数行テキストに対応してない

文字通り、対応していないので、一行づつレンダリングする必要があります。しかもテキストの正確な高さが取れないので、二行目をどこからレンダリングしたらいいかもよくわかりません。 phina.js が canvas.ctx.measureText('あ') ( の横幅) を高さとして使っていたので、今はそれを パクっ 参考にしています。

  • canvas 自体のサイズがちょっとでも足りてないと謎の物体がレンダリングされる

当初は、 canvas.ctx.measureText でテキストの幅を、上の方法でテキストの高さを、それぞれ事前に計算して、ぴったりサイズの canvas にレンダリングしていたのですが、謎の物体がレンダリングされて困っていました。

どうにもうまくいかなかったので、諦めてデカめの canvas に一度レンダリングしてからトリミングしています。

JS で GIF アニメをエンコードする

技術的には一番難しいところなんですが、ここはさすがにライブラリ頼みです。

jsgif: https://github.com/antimatter15/jsgif

これが使いやすそうだったので、これを submodule にして使っています。「gh-pages でも submodule は使えるけど、 git@... ではなく https://... でないとダメ」という学びがありました。

var encoder = new GIFEncoder();

encoder.setRepeat(0);     // リピート回数 (0 は無限)
encoder.setFrameRate(20); // フレームレート決める

encoder.start();

for (...) {
    // ... canvas で何かレンダリングする ...
    encoder.addFrame(ctx);
}

encoder.finish();

こんな感じで一つ一つのフレームをレンダリングしては encoder.addFrame していけば、謎の技術によって GIF アニメにエンコードされます。

img.src = "data:image/gif;base64," + encode64(encoder.stream().getData());

僕の作ったツールでは、できた GIF アニメをさらに data URL にエンコードして、 img タグの src にすることでユーザーに返しています。

まとめ

canvasつらい 楽しい!!

というわけで楽しい Emoji ライフを!病的なエフェクトを思いついたらぜひプルリクください!