本文へ移動

Fuda Cards札

画像やライブ3Dを入れるコレクション用のカード枠です。本物の厚み、縁、箔、山、傷み、バインダーを、2つのWebコンポーネントで使えます。

「札(ふだ)」は、カードや札を指す日本語です。 名前について(英語)

目次

やり方

カード内のライブ3D

1つのWebGLコンテキストで、同時に1枚のカードだけを本物の3Dで回し、ほかは画像にします。live-3dモジュールと自分のthree.jsを使います。

50枚のカードが、それぞれWebGLコンテキストを持つことはできません。全カードをライブにしたときは41個のコンテキストが測定され(ブラウザーの上限は約16個)、24枚が代替表示に落ち、40枚で3秒間に108,654回の描画が行われました。@johnmorrisdotca/fuda-cards/live-3dは、それを避けるためのフォーカスの方針です。ライブになるカードは常に1枚だけで、ページ全体で1つのコンテキストを使い、ほかのカードは画像です。画像だけのページは、3Dのコードをまったく読み込みません。

使い方

threeは利用側で用意します。省略可能なピア依存(>=0.160.0)で、最初にフォーカスされたとき、loadに渡した関数が読み込みます。

import { createLiveCards } from '@johnmorrisdotca/fuda-cards/live-3d';

const live = createLiveCards({ load: () => import('three') });   // loads nothing yet

const rocket = {
  key: 'rocket:starship',                                         // cards with the same key share their pictures
  async build({ three, assets, width, height }: any) {
    const scene = new three.Scene();
    const camera = new three.PerspectiveCamera(30, width / height, 0.01, 1000);
    const object = new three.Mesh(new three.BoxGeometry(1, 2, 1), new three.MeshNormalMaterial());
    scene.add(object);
    camera.position.z = 5;
    return { scene, camera, object, resize: (w: number, h: number) => { camera.aspect = w / h; camera.updateProjectionMatrix(); } };
  },
};

const card = document.querySelector('fuda-card') as HTMLElement;
const off = live.add(card, rocket, { still: card.querySelector('img') as HTMLImageElement });
// later: off() takes the card out; live.dispose() lets go of the renderer

item.buildは、シーン、カメラ、そしてobject(縦軸のまわりで回るもの)を返します。複数のカードで共有するジオメトリ、マテリアル、テクスチャ、モデルは、すべてassets.acquireを通して取得してください。実体が1つだけ保たれます(共有レンダラーのガイドを参照)。

createLiveCardsが返すもの

メンバー働き
add(card, item, { still })フォーカスされたときにカードをライブにし、そのカードを外す関数を返す。stillはカードがすでに持っている画像で、あればカードはフォーカスされるまで3Dのコードを必要としない。
focus(card)このカードにフォーカスを当てる。ライブにならなかった場合は偽で、理由はほかのカードがライブになっている、動きの軽減、WebGLがない、のいずれか。
blur()どのカードもフォーカスしない。ライブだったカードは画像に固定され、何も描かれない。
focusedフォーカスされているカード(あれば)。
statsレンダラー自身の数値。必要になるまでundefined。
renderer共有レンダラー。必要になるまでundefined。
dispose()すべてのカードを外し、レンダラーを手放す。

オプションは、turnSeconds(1周の秒数、12。0なら回さない)、dwellMs(ポインターがカードの上で止まる時間、150 ms)、lookAheadPx(自前の画像を持たないカードの静止画を作る、表示範囲の外側の距離、300 px)、そしてレンダラー自身のオプション(maxPixels、maxSide、stillSize、stillBytes、lingerMs、releaseMs、onContext)です。

コレクションの中で

import '@johnmorrisdotca/fuda-cards/collection-element';
import { createLiveCards } from '@johnmorrisdotca/fuda-cards/live-3d';

const live = createLiveCards({ load: () => import('three') });
const collection = document.querySelector('fuda-collection') as HTMLElement & { live: unknown };
collection.live = { cards: live, item: (card: { id: string }) => ({ key: card.id, build: async () => { throw new Error('make your scene here'); } }) };

どの表示でも、フォーカスされたカードがライブになります。

ルール

  • ライブは同時に1枚だけ。 dwellMsのあいだ止まったポインターの下のカード、キーボードのフォーカスを持つカード、最後に押したカード。カードの上を通り過ぎるだけでは何も始まりません。カードから離れる、または別の場所を押すと終わります。
  • 古いカードは最後の角度で止まります。 その静止画は同じコンテキストからその角度(5度ごとに共有)で作られるので、再びフォーカスされたとき、そこから続きます。
  • 動きの軽減:ライブなし。 カードは画像のままで、3Dのコードは読み込まれません。
  • 静止中は何も動かない。 フォーカスされたカードがないとき、画面の外にあるとき、非表示のタブでは、フレームを要求せず、何も描きません。
  • WebGLがない、またはコンテキストが失われた場合: カードは画像のままで、コンテキストが戻れば再び描かれます。

テストで守る予算

pnpm test:freeze(test/e2e/live-3d.e2e.mjs)が次の項目を守ります。フォーカスを30回変えてもWebGLコンテキストは1つで、キャンバスも1つまで。静止中の2秒間は、描画0回、フレーム要求0回、フレームコールバック0回で、画面外や非表示のタブでも同様。最初の1巡のあとでGPUオブジェクトが増えない。ピクセル比2の5120×2880の画面で、キャンバスが600万ピクセル以内、1辺4096以内。最初のフォーカスの前に、three、レンダラー、モデルを取得しない。dist/live-3d.jsは7.5 KB、gzipで2.3 KBです。テストはソフトウェア描画で行うので、数の確認はできても、実際のGPUについては何も証明しません。フレーム時間、ドライバーの挙動、GPUメモリは測っていません。ライブ3Dのデモで試せます。

ソースを見るGitHub で編集@johnmorrisdotca/fuda-cards 0.11.0 のソースから作成しました。

この日本語は、まだ日本語を母語とする方の確認を受けていません。訂正を歓迎します。