CanvasKit - (Skia + WebAssembly)を使ってみる


CanvasKit

公式ページによると、CanvasKit(SkiaのWebAssemblyビルド版を含む)は、
新しいCanvasとSVGプラットフォームAPIをテストするためのPlaygroundを提供するとなっています。

改めてWebAssemblyとは?

WebAssembly - Wikipedia

ブラウザ上でバイナリフォーマットの形で実行可能であることを特徴とする

とあり、C/C++, Rust, GO, Kotlin Nativeが現在対応している言語となっています。

環境構築


npmパッケージ も提供されていますが、今回はサクッと手元で
確認する為に unpkg.com から提供されているCDNを利用します。

実装


簡単なサンプル

index.html だけの線を描画するだけのドシンプルなサンプルを作ってみます。

index.html
<!DOCTYPE html>
<html>
  <head>
    <title>CanvasKit Sample</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
  </head>
  <body>
    <div class="canvas-container">
      <canvas width="500" height="500" id="canvas">
    </div>
    <script src="https://unpkg.com/[email protected]/bin/canvaskit.js"></script>
    <script type="text/javascript" charset="utf-8">
      CanvasKitInit({
          locateFile: (file) => 'https://unpkg.com/[email protected]/bin/'+file,
      }).then((CanvasKit) => {
        const surface = CanvasKit.MakeCanvasSurface('canvas');
        if (!surface) {
          throw 'Could not make surface';
        }

        function drawFrame(canvas) {
          const paint = new CanvasKit.SkPaint();
          paint.setStrokeWidth(1.0);
          paint.setAntiAlias(true);
          paint.setColor(CanvasKit.RED);
          paint.setStyle(CanvasKit.PaintStyle.Stroke);

          const path = new CanvasKit.SkPath();
          path.moveTo(0, 0);
          path.lineTo(500, 500);

          canvas.drawPath(path, paint);
          path.delete();
          paint.delete();
        };
        surface.drawOnce(drawFrame);
      });
    </script>
  </body>
</html>

上記コードを実行した結果がこちら↓

とりあえず動いている様子です

Skottie (Lottie Animation Player) でアニメーション

次にSkiaがLottieファイルのアニメーション再生をサポートしているので、試してみます。
今回テスト用に使用したLottieファイルはこちらになります。

Skottieに関してはCanvasKitとは別のnpmパッケージとして公開されています。
先ほどの index.html も使用するCDNが異なります。

完成した index.html がこちら ↓

index.html
<!DOCTYPE html>
<html>
  <head>
    <title>CanvasKit Sample</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
  </head>
  <body>
    <div class="canvas-container">
      <canvas width="500" height="500" id="canvas">
    </div>
    <script src="https://unpkg.com/[email protected]/bin/skottiekit.js"></script>
    <script type="text/javascript" charset="utf-8">
    const lottieUrl = "https://assets6.lottiefiles.com/private_files/lf30_4uTjNk.json";
    Promise.all([
      fetch(lottieUrl).then((res) => res.text()),
      SkottieKitInit({
        locateFile: (file) => 'https://unpkg.com/[email protected]/bin/'+file,
      })
    ]).then(([json, SkottieKit]) => {
      const animation = SkottieKit.MakeManagedAnimation(json);
      const duration = animation.duration() * 1000;
      const size = animation.size();
      const bounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500};

      const surface = SkottieKit.MakeCanvasSurface('canvas');
      if (!surface) {
        throw 'Could not make surface';
      }

      let firstFrame = Date.now();
      function drawFrame(canvas) {
        let seek = ((Date.now() - firstFrame) / duration) % 1.0;
        let damage = animation.seek(seek);

        if (damage.fRight > damage.fLeft && damage.fBottom > damage.fTop) {
          canvas.clear(SkottieKit.WHITE);
          animation.render(canvas, bounds);
        }
        surface.requestAnimationFrame(drawFrame);
      };
      surface.requestAnimationFrame(drawFrame);
    });
    </script>
  </body>
</html>

上手く動作できたらアニメーションがループして再生され続けます。

感想


Playgroundと言っている様に、Skiaの雰囲気をサクッと手元で確認するのには良さそうでした。
ただ、凝った事(Shader使ったり)しようとするとCDNのパッケージだけでは SkRuntimeEffect がundefinedになったりと
ドキュメントが不十分な感じはしましたが、Playgroundなのでそんなものな気もしました。
また機会があれば色々触ってみたいと思います。

参考になったURL