amagiです。先日React用コンポーネントライブラリのREACT-VFXをリリースしました。 REACT-VFXを使うと、画像や動画、テキストにWebGLでエフェクトをかけることが出来ます。
⚡𝙍𝙀𝘼𝘾𝙏-𝙑𝙁𝙓 released!⚡
— 𝘼𝙈𝘼𝙂𝙄 (@amagitakayosi) 2019年12月27日
I created React components to add WebGL effects to images, videos and plain texts in your app.
It also supports animated GIFs...😎
Visit website for details:https://t.co/mlnmExpUVZ#REACTVFX #WebGL #React #Threejs #GLSL pic.twitter.com/uDUQ8MKNcK
例えば、画像をグリッチしたり:
GIFアニメをエモい感じにしたり……
プレーンテキストにもエフェクトをかけたり出来ます!!
この記事では、REACT-VFXを作った経緯や、動作の仕組みを解説します🦄
REACT-VFXを作った経緯
今後の仕事のために自分のポートフォリオを作り直そうと思い、インスピレーションを得るためにAWWWARDSを見ていた所、こんな感じでWebGLエフェクトが多様されてるのを発見した。
かっこ良すぎる…………!僕もこういうWebGLエフェクトを使いたい!
ポートフォリオはReactベースで作ろうとしてたんだけど、ReactでWebGLを扱うのは結構難しい。 react-three-fiberはReact向けWebGLライブラリの中で最も筋が良いライブラリだ(個人の感想)。 react-three-fiberを使うと、JSXでThree.jsのシーンやオブジェクト、イベントなどを記述できる。 WebGLなサイトをちょちょっと作るのにすごく便利なんだけど、今回やりたい事に対してはオーバーキルだし、canvasの制御やシーン生成を手でやりたいって時には向いていない。
僕がやりたいのは、単にDOM要素をWebGLの空間に配置して、シェーダーでエフェクトをかけて描画する、って事だけだ。 という訳で、自分でライブラリを作る事にした。
使い方
インストールは npm i
で行います。
npm i -S react-vfx
REACT-VFXは VFXProvider と VFXElements で構成されています。
まず、Reactアプリケーション
全体を <VFXProvider>
で囲みます。
次に、VFXElementsを配置していきます。
<img>
や <video>
の代わりに<VFXImg>
、 <VFXVideo>
で置き換えてください。
これで完了!
エフェクトの種類は shader
プロパティで変更できます。
この例では、画像にグリッチ風エフェクトがかかっているはずです。
シェーダーは自分で書くことも出来ます。
shader
プロパティにGLSLシェーダーを渡してください。
このコードはこんな感じに描画されます:
より詳しい機能については、Webサイトの Usage
セクションをご覧ください。
https://amagi.dev/react-vfx/#usage
仕組み
REACT-VFXはWebサイト全体を巨大な <canvas>
要素で覆うことで動作しています。
VFXElementsの位置やサイズをトラッキングし、WebGLのテクスチャとして読み込んで、毎フレーム描画しているだけで、特に変わった事はしていません。
とはいえ、GIFアニメ対応やテキストの画像変換周りでちょっと苦労したので、その辺を解説します。
GIFアニメ対応
WebGLは画像や動画をテクスチャとしてロードできますが、GIFアニメには対応していません。 (2年前に試した時は、chromeではGIFアニメをロードできてたんだけどな……)
そのため、今回GIFアニメに対応するんは、まずGIFアニメをパースして画像に分割し、順番にテクスチャにロードしていく、ということをする必要がありました。
GIFアニメのパースにはSuperGIFが良く使われるけど動作が重すぎるので、今回は matt-way/gifuct-js を使う事にした。 PromiseベースのAPIだしサクサク動いて便利なんだけど、bower時代の産物なのでforkする必要があった。
gifuct-jsによってフレームごとの画像データが手に入ったので、次はこれをWebGLでロード出来るようにする。 gifuct-jsはフレームごとの長さも返してくれるので、現在時刻と再生時間、フレームの長さを比較して順番に描画する事で、簡易的なGIFプレイヤーを実装できる。
あとはこのcanvasをWebGLに渡すだけで、GIFアニメをWebGL上に描画できた。
テキストを画像に変換する
WebGLでテキストを扱いたい場合、事前にテキストを画像に変換しておくか、Three.jsのドキュメントで解説されているようなトリックを使う必要があった。
https://threejs.org/docs/#manual/en/introduction/Creating-text
これらのトリックを使うのも大変だし、デザイン変更の度にPhotoshop等で画像を書き出すのも面倒……という事で、もっと手軽にDOM要素を画像に変換できないか調べた。
まず、html2canvasが使えるか試してみた。
html2canvas - Screenshots with JavaScript
html2canvasはWebページのスクリーンショットをブラウザ上で生成するライブラリだ。一応いい感じにDOM要素の画像を生成できたんだけど、html2canvasは毎回ページ全体をクローンするため、不要なリクエストが走りまくって動作がメチャクチャ重くなってしまい、今回の用途には合わないと判断した。
結局、html2canvasと似た動作をするdom2canvasというスクリプトを自作する事にした。
SVGのforeignObjectという要素をつかうと、DOM要素をSVGに埋め込む事ができる。 これを利用すると、次の要領でDOM要素をcanvasに描画できる。
DOM要素 -> HTML文字列 -> SVG文字列 -> Canvas要素に描画 -> WebGL texture
いろいろ制限はあるけど(クロスオリジンなリソースを読み込めない等)、今回はプレーンテクストだけ変換できればいいので、これで充分だ。
こうして、渡された文字列を画像に変換してエフェクトをかけるコンポーネント VFXSpan
ができた。
VFXSpanは文字列が更新されたら自動的に再描画するので、例えばtextareaと組み合わせたりもできる。
foreignObject周りはこの辺の記事を参考に実装しました🙏
今後の課題
今後は以下の機能を追加したいと考えています。
- カスタムuniform変数のサポート
- backbufferサポート
- エフェクトプリセット追加
REACT-VFXはまだまだ粗もあるけど、ちょっとしたエフェクトをWebサイトに追加したい場合にはかなり便利に使えるはずです。 ぜひ試してみてください!
感想や機能リクエストはGitHubやTwitterで気軽にどうぞ!
よろしく〜👻👻👻