Houdiniというプロジェクトをご存知ですか?HoudiniはJavaScirptからアクセスできるCSSの機能を広げ、プログラマブルなCSSを実現するためのものです。Houdiniが実現すれば、まるでハリー・フーディーニのように物事を自在に操れるようになること間違いなしです!

そんなHoudiniの中で、CSS Paint API(CSS Painting API)がChrome 65で実装予定です。 CSS Paint APIを使って、ブラウザの上の魔術であるHoudiniを体験してみましょう。

CSS Paint API

CSS Paint APIは、CSSで用いる画像をJavaScriptから動的に生成するためのAPIです。生成した画像は、background-imageやborder-imageで利用可能です。今までcanvas要素で無理やり実現していた複雑な背景なども、CSSの枠組みの中で実現することができるようになります。

画像はプログラマブルに生成できるので、単なる静止画だけでなく、アニメーションさせることもできます。また、パラメータを使ったバリエーションも作成できます。

実装状況

CSS Paint APIの実装状況は現在のところあまり良くはありません。実装状況は以下の通りです:

  • Chrome: 65で実装予定
  • Firefox: 実装中
  • Safari: 未定
  • Edge: 未定

また、Chrome 65でも一部機能はまだ使えません。

ここではChrome 65を前提に話を進めていきます。

ソースコード

この記事で使用するソースコードはGithubで公開しています。以下のURLからアクセスできます。ご自由にお使いください。

基本的な使い方

CSS Paint APIを使用するには、まずWorkletと呼ばれるものを作成する必要があります。WorkletはWorkerの一種で、特定用途向けの環境で実行されます。CSS Paint APIで使用するのはPaintWorkletと呼ばれていて、描画に関係する環境を備えたWorkletとなります。PaintWorkletはメインスレッド(UIスレッド)で動作します。

PaintWorkletの作成は簡単で、まず別ファイルとして(たとえばworklet.jsとしましょう)JavaScriptファイルを作り、その中に任意の処理を書きます。

CSS Paint APIを使用するには、まず適当なクラスを作ります。そしてそのクラスの中にpaintメソッドを実装します。そして最後にregisterPaint関数でクラスを登録します。例えば以下のようにです:

paintメソッドは引数を3つ取ります。最初の引数contextはCanvasの2DContextとほぼ同じです。contextに対して描画命令を発行することによってグラフィックを生成できます。

次のgeometryは適用した要素の縦横の大きさ(width, height)を含むオブジェクトです。他の情報は含んでいません。

最後のpropertiesはアクセスできるCSSプロパティの一覧です。これについては後述します。

つまり、contextを受け取って、geometryでサイズを測りながら、好きに図形を描いていけば完成するというわけです。この例では、要素の中心に半径50pxの赤い円を描いています。

そして最後にregisterPaint関数で任意の名前で登録します。今回はcircleという名前で登録しました。

次にこれをCSS側から利用します:

CSSから利用するには、まずCSS.paintWorklet.addModuleメソッドを使ってWorkletを登録します。そしてCSS側で、「paint(登録した名前)」とすることで画像として扱うことができます。今回はbackground-imageに使用しています。

そして何も中身のない500×500のdiv要素にこのpaint(circle)を背景として適用しています。さてどうなるでしょう。

これを実行してみます:

赤い丸が表示されました!

注意点

PaintWorkletにはひとつ注意点があります。それはcontextはcanvas要素のcontextとは完全に一緒ではないということです。簡単にいうと、いくつかの機能が欠けています。具体的にいうと、テキスト描画系の命令は存在せず、imageData系の命令も使えません。

あくまでcanvasのサブセットということに注意しつつ使用しましょう。

CSSのプロパティを読み出す

PaintWorkletを使用することでCSS用のグラフィックを描画することができました。しかしこれでは何か物足りません。もう少し凝ったことをしてみましょう。

先ほどの例でも少し見えましたが、paintメソッドはpropertiesという引数を受け取ります。これはCSSのプロパティを読み出すために使う引数です。CSSのプロパティをパラメータとして使うことで、柔軟にバリエーションを作ることができます。

プロパティを読み出すにはクラスにstatic get inputPropertiesを実装し、読み取りたいプロパティの名前の配列を返します。

そしてpaintメソッドの第3引数であるpropertiesからgetメソッドを使って値を取得します。例えば以下のようにします:

properties.getで返ってくるのはCSSStyleValueなのでtoStringメソッドで文字列に変換します。あとは普通にその値を使えば、プロパティをパラメータとしての描画ができます。

あとはCSS側で変数を定義し、そのままpaintを使います:

これを実行してみます:

今度は青い円が描画されました!これで自由自在に描画できます。

paint()の引数

注:この機能はChrome 65では未実装です。

CSSのプロパティを使ってパラメータを実現することができました。しかしプログラミングに少し興味のある人なら「方法がちょっとダサい」と思うはずです。

そこでもう少し賢い方法が用意されています。CSSで使うpaint()に、引数として値を渡す方法です。CSS側では、以下のようにします:

この方がプログラミング的にかっこいいですね!

このときWorklet側ではstatic get inputArgumentsを実装します。inputArgumentsは引数の型の配列を返します。例えば引数が色ならば'<color>’という文字列を配列に入れます。複数の引数を定義するときは、その数だけの型文字列を配列に入れてください。

そしてpaintメソッドの引数を4つに増やします。4つめの引数がCSSでの引数になります。

これを実装したWorkletは以下のようになります:

このようにしてCSSから引数を受け取ることができるようになります。

ここで少し馴染みの薄いものが出てきます。CSS引数の型です。ここには'<color>’や'<length>’などを指定します。これらの型については詳しくは以下のURLをご覧ください:

アニメーションさせる

静止画がひとつ描画されるだけ、というのもなんだか面白くないです。今度はアニメーションさせてみましょう。

アニメーションには、プロパティの取得かCSSの引数を使います。値をどんどん変化させていくことで、アニメーションが実現できます。実際にやってみましょう。

Wroklet側ではCSSのプロパティを見て描画する図形を変えます。

そしてJavaScriptでプロパティをどんどん変化させていきます。CSS VariablesはTransition/Animationに対応していなため、requestAnimationFrameを使っての手動での変化になります。

requestAnimationFrameを使ったアニメーションに馴染みのない方は、「JavaScriptとcanvasでアニメーションを作る」もあわせてご覧ください。

これを実行してみます:

アニメーションさせることができました!

このように、CSS Paint APIを使うことで、CSSで使える画像を様々に生成できます。まだまだ対応ブラウザが少ない状況ですが、実装が増えればいろいろと使いどころのあるAPIとなっています。各ブラウザの足並みが揃ってきたら、ぜひ使ってみましょう。