最近のChromeにはCSSの新機能が次々と搭載されています。2017年にはCSS Grid Layoutなどインパクトの大きい新機能が話題になりましたが、他にも有用な新機能があることをご存じでしょうか? この記事では、2018年のChromeに搭載された新機能の中から、CSS Paint APIを紹介します。

CSS Paint APIはグラフィックを描く機能

CSS Paint APIは自由にグラフィックを描ける仕様です。HTML5 Canvasのようなものを要素の背景画像(background-imageborder-image)として設定できます。JavaScriptを使って自由にグラフィックを描けるため、従来のCSSで再現の難しかった表現が実現可能になります。

たとえば、次のようなビジュアルを考えてみましょう。四隅の形状が欠けたような表現となっています。従来のCSSではdivタグを複雑に組む必要がありました。

CSS Paint APIを使うと、1つのdivタグにCSSを割り当てるだけで実現できます。CSSだけでなくJavaScriptを作成する必要はありますが、HTMLの構造を汚さずに装飾を自由に設計できることが利点といえるでしょう。

▼CSS Paint APIを使って実現したHTMLコード

<script>
  // ペイントワークレットを登録する
  CSS.paintWorklet.addModule('worklet.js');
</script>
<style>
  .graphics {
    /* 背景としてペイントを指定 */
    background-image: paint(rect);
  }
</style>
<body>
  <div class="graphics">
    ・・・
  </div>
</body>

基本的な利用方法の解説

CSS Paint APIを使って、簡単な三角形を描いてみましょう。下図のようにdivタグの背景にCSS Paint APIで描いた三角形が背景画像として設定されている状態を目指します。

CSS Paint APIを利用するには、独立したJavaScriptのファイルを作成します。これをペイントワークレットといいます。必ず独立した.jsファイルとして作成する必要があります。

ペイントワークレット側の作成

ペイントワークレットのJavaScriptファイルには任意のクラスを作成します。クラスのフィールドにはpaint()メソッドを実装し、registerPaint()メソッドでクラスとメソッド名の関連付けします。

/** 三角形を描くペインタークラスです。 */
class TrianglePainter {

  /**
   * 描画時に呼び出される関数です。
   * @param context {CanvasRenderingContext2D} コンテキストです。
   * @param geometry {{width:number, height:number}} 描画領域の情報(width, heightのみ)です。
   */
  paint(context, geometry) {

    // 三角形の頂点を定義
    const points = [
      {x: 0, y: geometry.height}, // 左下
      {x: geometry.width, y: geometry.height},// 右下
      {x: geometry.width / 2, y: 0}, // 上
    ];

    // 三角形の形状を作成
    context.beginPath(); // パスの開始
    context.moveTo(points[0].x, points[0].y); // 最初の点
    context.lineTo(points[1].x, points[1].y); // 2番目の点
    context.lineTo(points[2].x, points[2].y); // 3番目の点
    context.closePath(); // 最後の頂点を最初の頂点まで結ぶ

    // 塗りを描く
    context.fillStyle = 'red'; //塗りつぶしの色
    context.fill();
  }
}

// paint(triangle)メソッドとして登録
registerPaint('triangle', TrianglePainter);

paint()メソッドはHTML5 CanvasのCanvas2DRendererContextオブジェクトと似たコンテキストを引数として受け取れます。このコンテキストに対して命令をすることで描画結果を作成します。引数のgeometrypropertiesに含まれる情報をもとに描画結果を調整します。

  • geometry : この引数には描画領域のサイズ情報が入っている
  • properties : CSS変数と連携できる引数

メインのHTML側の作成

メインのHTML側からはJavaScriptでCSS.paintWorklet.addModule()メソッドを利用してワークレットを登録します。引数にはワークレットのJavaScriptファイルを指定します。

▼ JavaScript

// workletを登録する
CSS.paintWorklet.addModule('worklet.js');

スタイルシート側では、background-imageプロパティーに対してpaint()メソッドを指定します。paint()メソッドにはワークレットのregisterPaint()登録した描画クラスを指定します。

▼ スタイルシート(CSS)

.graphics {
  /* 任意のサイズを指定する */
  width: 80vw;
  height: 80vh;

  /* paint(登録した命令)で背景画像として利用可能 */
  background-image: paint(triangle);
}

パラメーターで動的に描画を変更可能に

CSS Paint APIとCSS変数を連携することで、パラメーターで動的に描画を変更可能になります。

ワークレット側のクラスにstatic get inputProperties()を実装し、読み取りたいプロパティ名の配列を戻り値とします。

ワークレット側のpaint()メソッドの第三引数propertiesはCSSのプロパティを読み出すために使う引数です。get()メソッドを使うとstatic get inputProperties()で割り当てたCSS変数を受け取れます。

▼ ペイントワークレット(JavaScript)

/** 三角形を描くペインタークラスです。 */
class TrianglePainter {

  /**
   * 入力してのプロパティーを定義します。
   * 配列として返したプロパティーが取得できます。
   * @returns {string[]}
   */
  static get inputProperties() {
    return [
      '--fill-color',
    ];
  }

  /**
   * 描画時に呼び出される関数です。
   * @param context {CanvasRenderingContext2D} コンテキストです。
   * @param geometry {{width:number, height:number}} 描画領域の情報(width, heightのみ)です。
   * @param properties {Object} CSS変数です。
   */
  paint(context, geometry, properties) {

    // properties の get メソッドを使って値を取得
    // CSSStyleValue の値を取得
    const color = properties.get('--fill-color').toString();

    // 三角形の頂点を定義
    const points = [
      {x: 0, y: geometry.height}, // 左下
      {x: geometry.width, y: geometry.height},// 右下
      {x: geometry.width / 2, y: 0}, // 上
    ];

    // 三角形の形状を作成
    context.beginPath(); // パスの開始
    context.moveTo(points[0].x, points[0].y); // 最初の点
    context.lineTo(points[1].x, points[1].y); // 2番目の点
    context.lineTo(points[2].x, points[2].y); // 3番目の点
    context.closePath(); // 最後の頂点を最初の頂点まで結ぶ

    // 塗りを描く
    context.fillStyle = color; //塗りつぶしの色
    context.fill();
  }
}

// paint(triangle)メソッドとして登録
registerPaint('triangle', TrianglePainter);

利用する側にはCSS変数を定義します。ハイフンを二個つなげて定義します。

▼ スタイルシート(CSS)

.graphics {
/* 任意のサイズを指定する */
width: 80vw;
height: 80vh;

/* ペイントワークレットに任意のカラーを渡す */
--fill-color : blue;

/* paint(登録した命令)で背景画像として利用可能 */
background-image: paint(triangle);
}

CSS変数はデベロッパーツールで変化させてみると、動的に変化可能であることがわかります。

アニメーションの実装方法

JavaScriptで時間経過によって値が変動するようにしましょう。次のサンプルでは、時間経過で塗りの色が変化するようにしています。

具体的にはJavaScriptのrequestAnimationFrame()メソッドを使って、時間経過でCSS変数の値を書き換えます。

▼ メイン側のJavaScript

// workletを登録する
CSS.paintWorklet.addModule('worklet.js');

window.addEventListener('DOMContentLoaded', () => {
  const el = document.querySelector('.graphics');
  tick();

  // アニメーションを開始
  function tick() {
    // 色相を計算(経過時間から算出 0〜360の値を取り得る)
    const hue = Math.round((Date.now() / 10) % 360);
    // CSS変数を更新
    el.style.setProperty('--fill-color', `hsl(${hue}, 100%, 50%)`);

    requestAnimationFrame(tick);
  }
});

このように、CSS変数との連携によってカスタマイズ性に富んだオリジナルのCSSを設計できるようになります。

応用作例を紹介〜タイル調グラデーション

次のサンプルはCSS変数を使って、グラデーションを描いたものです。動画を再生して、CSS Paint APIの可能性をご覧ください。

格子状の分割数はCSS変数で管理しているため、簡単に数値の変化でグラフィックを自在に編集できます。プログラミングアートと相性がよく、自由にグラフィックを描けるので応用の幅が広いです。

次のページでは気になる動作環境や注意点について紹介します。