キャンバスの中のライブな HTML
drawElement は、本物の DOM 要素を spryte のキャンバスの中に描きます。メニュー、テキスト入力のあるフォーム、折り返しのあるリッチテキストの段落などです。要素はライブなままです。描かれた場所でクリック、フォーカス、入力を受け付け、テキストは選択でき、Ctrl+F で検索でき、スクリーンリーダーも読み上げます。ほかのスプライトと同じように、spryte の変換とシェーダーも掛かります。
ブラウザの HTML-in-Canvas API を使います。Chrome と Edge ではバージョン 157 から使えます。Firefox と Safari にはまだないので、必ずフォールバックを用意してください。
ライブな HTML のデモで実際に動く様子を見られます。シェーダーで浮かび上がるカードの後ろをスプライトの群れが泳ぎ、カードのテキストは選択でき、リンクとボタンも使えます。
実験的な機能
spryte 0.9 で追加された新しい機能です。ブラウザの API が固まるまで、drawElement は変わる可能性があります。
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);
}仕組み
- 最初の呼び出しで、要素をキャンバスの中へ移します(
<canvas>の子になり、drawableが付きます)。このときは何も描きません。 - ブラウザが一度描画した後は、呼び出すたびに論理単位の
x, yへ、今の変換、アルファ、シェーダーを通して描きます。 - spryte は要素が描かれた位置をブラウザに伝えます。そのため、拡大や回転をしても、ポインタ、フォーカス、選択は描かれた位置に合います。
drawElement は、描いたときに true を返します。API がない、最初のフレームである、draw.into() やマスクの中で呼んだ(どちらも今は未対応)、のいずれかなら false を返します。
知っておくこと
- 位置を決めるのは CSS ではありません。 キャンバスの中では、要素のマージンや位置指定は画面上の位置になりません。
x, yが位置を決めます。それでも CSS は残してください。API のないブラウザでは要素がページに残り、その CSS でレイアウトされます。 - 大きさは CSS の大きさです。 何も指定しなければ、ボーダーボックスの大きさで、CSS の 1 ピクセルを 1 論理単位として描きます。拡大縮小するには
{ w, h }を渡します。 - 変更は 1 フレーム遅れて見えます。 ブラウザが要素を描画するのはフレームの後なので、今の変更は次のフレームに現れます。
- 描き直しにはブラウザの時間がかかります。 毎フレーム変わる要素は、毎フレーム描き直されます。変わらない要素なら、変わったときにテクスチャを 1 回送るだけです。
- エフェクトも掛かります。
draw.withShader()で囲めば、ディゾルブ、波紋、CRT 風の見た目にできます。エフェクトを掛けても、テキストは選択できます。
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 があるかを返します。
import { canDrawElements } from "spryte";
if (!canDrawElements()) showHtmlMenu();試す
サンプルは、小さなゲーム画面の中に、テキスト入力のあるフォームを傾けて描き、その前後にスプライトを動かします。spryte のチェックアウトで npm run example:html を実行し、URL に ?backend=2d を付けると Canvas 2D の経路も見られます。