本文へスキップ

キャンバスの中のライブな HTML ​

drawElement は、本物の DOM 要素を spryte のキャンバスの中に描きます。メニュー、テキスト入力のあるフォーム、折り返しのあるリッチテキストの段落などです。要素はライブなままです。描かれた場所でクリック、フォーカス、入力を受け付け、テキストは選択でき、Ctrl+F で検索でき、スクリーンリーダーも読み上げます。ほかのスプライトと同じように、spryte の変換とシェーダーも掛かります。

ブラウザの HTML-in-Canvas API を使います。Chrome と Edge ではバージョン 157 から使えます。Firefox と Safari にはまだないので、必ずフォールバックを用意してください。

ライブな HTML のデモで実際に動く様子を見られます。シェーダーで浮かび上がるカードの後ろをスプライトの群れが泳ぎ、カードのテキストは選択でき、リンクとボタンも使えます。

実験的な機能

spryte 0.9 で追加された新しい機能です。ブラウザの API が固まるまで、drawElement は変わる可能性があります。

ts
import { createRenderer, drawElement } from "spryte";

const draw = createRenderer(canvas);
const menu = document.getElementById("menu")!; // an ordinary element in your page

function frame() {
  draw.begin("#000", 320, 180, scale);
  drawWorld();
  if (!drawElement(draw, menu, 40, 30)) {
    // No HTML-in-Canvas here: the menu is still a normal element in the page, so there's nothing to do.
  }
  draw.end();
  requestAnimationFrame(frame);
}

仕組み ​

  1. 最初の呼び出しで、要素をキャンバスの中へ移します(<canvas> の子になり、drawable が付きます)。このときは何も描きません。
  2. ブラウザが一度描画した後は、呼び出すたびに論理単位の x, y へ、今の変換、アルファ、シェーダーを通して描きます。
  3. spryte は要素が描かれた位置をブラウザに伝えます。そのため、拡大や回転をしても、ポインタ、フォーカス、選択は描かれた位置に合います。

drawElement は、描いたときに true を返します。API がない、最初のフレームである、draw.into() やマスクの中で呼んだ(どちらも今は未対応)、のいずれかなら false を返します。

知っておくこと ​

  • 位置を決めるのは CSS ではありません。 キャンバスの中では、要素のマージンや位置指定は画面上の位置になりません。x, y が位置を決めます。それでも CSS は残してください。API のないブラウザでは要素がページに残り、その CSS でレイアウトされます。
  • 大きさは CSS の大きさです。 何も指定しなければ、ボーダーボックスの大きさで、CSS の 1 ピクセルを 1 論理単位として描きます。拡大縮小するには { w, h } を渡します。
  • 変更は 1 フレーム遅れて見えます。 ブラウザが要素を描画するのはフレームの後なので、今の変更は次のフレームに現れます。
  • 描き直しにはブラウザの時間がかかります。 毎フレーム変わる要素は、毎フレーム描き直されます。変わらない要素なら、変わったときにテクスチャを 1 回送るだけです。
  • エフェクトも掛かります。 draw.withShader() で囲めば、ディゾルブ、波紋、CRT 風の見た目にできます。エフェクトを掛けても、テキストは選択できます。
ts
const dissolve = draw.createShader(`
  uniform float progress;
  vec4 effect(vec4 color, vec2 uv) {
    float n = fract(sin(dot(floor(uv * 40.0), vec2(12.9898, 78.233))) * 43758.5453);
    return source(uv) * color * step(n, progress);
  }
`);
dissolve.set("progress", Math.min(1, secondsSinceShown / 1.5));
draw.withShader(dissolve, () => drawElement(draw, card, 80, 60));

対応しているか調べる ​

canDrawElements() は、描く前に、このブラウザに API があるかを返します。

ts
import { canDrawElements } from "spryte";

if (!canDrawElements()) showHtmlMenu();

試す ​

サンプルは、小さなゲーム画面の中に、テキスト入力のあるフォームを傾けて描き、その前後にスプライトを動かします。spryte のチェックアウトで npm run example:html を実行し、URL に ?backend=2d を付けると Canvas 2D の経路も見られます。