iso:アニメーションアイソメトリック説明図(Explainer)
1つのシーンは、1つの製品ストーリーを6~14秒のループで伝えます。ものは配置され、光はそれらの間を移動し、1つのパーツが一度に点灯し、ループは最初のフレームに戻ります。フローは常に1つの形状です。ビートテーブルからストーリーに必要な形状を選びます。内部のレイヤーを見せるために開くボックス、2つのオブジェクト間のハンドオフ、埋まっていくグリッド、選択肢を回転させるスタック、またはホバーでビューアが持ち上げるタイルなどです。すべては実際のSVGおよびHTMLテキストであり、そのボックスに合わせてスケールし、画面外では一時停止し、動きを減らす設定では静止します。
キットはこのファイルと同じ場所にあります:
lib/iso.ts:Sceneビルダー(投影、スラブ、面、ワイヤー、パルス、ラベル、モーショントラック)、scene.exportSvg()、responsive()、lazyBox()。lib/runtime.ts:レンダリングされたシーンをWAAPIで再生し、シーンの40%(または背の高いシーンでは画面の高さの半分)がビューポートに入るとイントロを開始し、画面外では一時停止し、画面の半分以上離れるとアニメーションを破棄し、戻る前に同じフレームに復元し、動きを減らす設定では静止させ、window.isoSeek(t)を公開します。scripts/render.ts:シーンモジュール(デフォルトエクスポートPage)を、ランタイムがインライン化された1つのスタンドアロンHTMLファイルにレンダリングします。scripts/shots.mjs:任意のページを選択した時間と幅でスクリーンショットし、sheet.pngを書き込みます。scripts/check.ts:キットに対してディスク上の任意の場所にあるシーンファイルを厳密に型チェックします(bun scripts/check.ts scene.ts)。scripts/page-idle.mjs:複数のシーンがあるページを一度スクロールし、0%、20%、35%、50%、60%、80%で停止し、それぞれの時点でのメインスレッド時間を、CPUを1倍および4倍で表示します(node scripts/page-idle.mjs page=<url> width=393 dpr=3 scroll=4)。シーンがいくつかある任意のページで実行します。scripts/perf.mjs:Chromeで電話幅でCPUを制限してページを再生し(node scripts/perf.mjs page=<out.html> width=393 throttle=4 seconds=20)、フレームレート、フレーム時間パーセンタイル、遅延フレーム、長時間タスク、メインスレッド時間を表示します。30個以上の動くオブジェクトがある任意のシーンで実行します。scripts/contrast.mjs、scripts/render-all.ts:レンダリングされたページ内のすべてのテキストを、静止フレームおよびさらに4つの瞬間でそのグリフのすぐ後ろのピクセルに対して測定し、4.5:1未満の場合は失敗します(node scripts/contrast.mjs <out.html>;bun scripts/render-all.ts <dir>は最初にすべてのカタログ例を書き込みます)。完成とみなす前にすべてのシーンで実行します。scripts/study-rive.mjs、scripts/compare-rive.mjs:独自の.rivアニメーションをフレームごとに調査し、キットシーンと比較するためのオプションツール。どちらもruntime=<canvas_advanced.mjsとrive.wasmを含むフォルダ>が必要です。examples/:架空の製品向けの64のシーン。各ファイルはexample(タイトル、ストーリー、プロンプト、レベル、カテゴリ、レイアウト、インタラクション、およびart:コンパクトシーンとワイドシーンをart(compact, wide, breakpoint)で)とデフォルトのPageをエクスポートするため、scripts/render.tsはそのいずれかをレンダリングできます。spotシーンは単一の小さなシーン、art(compact)です。hero.tsはhero(Art)をエクスポートし、material-gallery.tsはgalleries(仕上げファミリごとに1つのArt)をエクスポートし、それぞれデフォルトのPageを持ちます。examples/shared.tsは、サーフェス、カラーランプ、paints、フォント、およびfitX()を保持します。fitX()は、ラベルが配置される最も狭いカード内にラベルを収めます。
ストーリーに最も近い例を選び、描画する前に読んでください:
| 例 | レベル | 説明 |
|---|---|---|
notifications.ts | simple | 最小限のファイル:1つのコアから3つのタイルに広がり、パルス、到着フラッシュ、ホップ。最初に読んでください |
ci-deploy.ts | simple | チェーンに沿ってホップするパッケージ、着地時のスカッシュ、最後だけの状態色 |
file-upload.ts | simple | プログレスバーを埋めるチャンク、次にエッジポイントに広がるコピー。空のスロットのマーチングダッシュ |
db-branching.ts | simple | 実線になる破線のゴースト、後ろにスライドしてマージする変更タイル |
service-map.ts | simple | ホバー:9つのslab({ hover })タイル。ホバーまたはタップでラベルが表示されるが、ループはストーリーを伝え続ける |
auth-flow.ts | medium | 直立したpanel()スマートフォン、タイピングされた入力、トークンを運ぶハンドオフゴースト |
etl-pipeline.ts | medium | ワイヤーに沿ってコアに供給されるソースタイル、次に埋まって空になるグリッド。一緒に移動するように、重ね合わせは配置されているスラブとグループ化される |
queue-workers.ts | medium | ビルドが検証するイベントテーブルによって駆動されるスロットキュー(オーバーフローなし、空からの取得なし) |
search-index.ts | medium | 明るいページの線画タイル、タイピングされたクエリ、マッチする照明、ランク付けされた結果が浮上 |
feature-flags.ts | medium | 再生:ラベル内の実際の<button>と、seekで1つのループステップを再生するインラインスクリプト |
agent-tools.ts | complex | 衛星を持つコア、呼び出しと応答パルス、入力される回答カード |
checkout-payment.ts | complex | 破線の戻りパスとクロスフェードするステータスラインを持つステップのリング |
edge-cache.ts | complex | 同じリクエストを2回:オリジンへのウォームミス、次にエッジでのクールヒット |
observability.ts | complex | 3つのシグナルルック(ダッシュ、ドット、スパン)、heightTo付きのprismバー、アラートとリカバリ |
keycard-slot.ts、battery-charge.ts、tile-loader.ts、empty-box.ts | spot | 機能リスト、ローダー、または空の状態のための1つの小さなオブジェクト。スロットの縁でクリップされた直立したpanel()カード、heightToセル、対角線ドロップウェーブ、マーチングダッシュ |
sync-loop.ts、inbox-fill.ts、alert-stack.ts、table-scan.ts | spot | 平面に押しつぶされた矢印、ガラス前面のトレイ、バッジがカウントして一番上のカードに乗るスタックにスライドするカード、行ごとに移動するレンズ |
audit-ledger.ts、dns-resolve.ts、cron-jobs.ts | simple | 封印で積み重なるページ、一度に1つのホップと応答、ジョブをトリガーするダイヤル |
vault-rotate.ts、firewall-filter.ts、zero-trust.ts | medium | スロットパターンによるキー交換、跳ね返る拒否パケット、スタンプされるすべてのリクエスト |
workload-scheduler.ts、autoscale.ts、health-check.ts | medium | 小さなルールテーブルからの配置、新しいノードのためのスペース確保、ノードの取り出しと再参加 |
blue-green.ts、snapshot-restore.ts | medium | 再ターゲットされたワイヤー、タイムラインに沿って戻るカーソル |
embedding-clusters.ts、training-loss.ts、recs-rerank.ts、ab-test.ts | medium | クラスターに落ち着くポイント、落下するprismバー、再並べ替えられるスロットシェルフ、勝者との分割 |
data-erasure.ts、rag-answer.ts | complex | カウンターを持つ6つのシステムに広がる1つのリクエスト、モデルに飛び込む取得されたカードと入力される回答 |
hero.ts | complex | 1つのプロンプトが1、5、15、40の階層でシステムを成長させる。ターンテーブルスピンインと加重ドロップ、下からスライドアウトする出力、ポップ、グリッドフィルでドロップインするプール、無限のリクエスト、静止時のマウスへの傾き。塗りつぶしはfill、繰り返しタイルは<use>で安価に。SVG text()のみなので、exportSvg()が機能する |
funnel-live.ts、billing-cycle.ts、parcel-delivery.ts、test-shards.ts | medium, complex | layout: 'wide':3:1のストリップを左から右に読む(狭くなるステージ、ジャンプせずにシフトする月ストリップ、箱を運ぶ乗り物、レーンで競争してマージするシャード) |
kanban-move.ts、stock-sync.ts、diff-merge.ts、refund-flow.ts | simple ~ complex | layout: 'diagonal':1つのアイソ軸に沿って移動するカードまたはトークン、緑色になるゲート、2番目の色で同じパスを戻る払い戻し |
live-cursors.ts、two-approvals.ts、design-comments.ts、translate-six.ts | simple, medium | layout: 'panels':互いに向かい合う直立したpanel()。タイピングするカーソル、ロックを解除する2つのチェックマーク、ピンとスレッド、タイ語と日本語フォントスタックを持つ6言語の1つのメッセージ |
cms-publish.ts、sensor-gateway.ts、smart-home.ts、currency-convert.ts | simple, medium | layout: 'radial':5チャンネルへの1コピー、次にフロアウェーブアウトする読み取り値、順番に状態を変えるデバイス、リングワイヤーを一周する1パルス |
ota-rollout.ts、build-cache.ts、calendar-booking.ts、load-slider.ts | simple ~ complex | layout: 'field':停止と再試行を伴うデバイスフィールド全体のウェーブ、依存関係テーブルから計算されるキャッシュヒット、競合をスキップするミーティング。load-sliderは、ループをシークするドラッグコントロール(実際の範囲入力、マウス、タッチ、キーボード)で、周囲のトラフィックは動き続ける |
video-transcode.ts、image-resize.ts、fraud-gauge.ts、data-lineage.ts、drill-down.ts | simple ~ complex | layout: 'exploded':持ち上げられてエンコードされるレンディション、剥がれるサイズ、線を超えて蓄積されるリスクレイヤー、レイヤーを下って照らされる上流パス。drill-downは、実際のボタンからそのパーツのループセグメントをシークして、パーツをそのレイヤーに開く |
materials-*.ts、material-gallery.ts | 6ファミリー(Metal, Glass, Natural, Tech pattern, Retro, Generative)の49のサイド仕上げを(scene) => Materialファクトリとして、ファミリごとにラベルなしのギャラリー。Generative仕上げはシードを取る。新しい仕上げを発明するのではなく、既存のものをコピーすること |
セットアップは1回だけ:cd <このディレクトリ>/scripts && bun install && bunx playwright install webkit(スクリーンショットはインストールされたGoogle Chrome for Chromiumを使用)。型チェック:scripts/node_modules/.bin/tsc -p <このディレクトリ>。
1. 描画前にストーリーボードを作成する
最初にrecipes.mdを開いてください。これには、すべての測定済みモーションとコンポジションパターン(到着、接続、ストリーム、比較、選択、アイドル、インタラクション、イージング、サーフェス)が、それぞれの数値とキット呼び出しとともに含まれています。これらのレシピからストーリーを構成し、タイミングを守ってください。
ストーリーを最初にタイムラインテーブルとして記述します。ビューアーが理解すべきこと、それを示す3~6のビート、各ビートでどの要素がいつ、どのように、どれだけ動くか。ビートごとに1つのアイデア。説明に段落が必要なビートは、2つのビートであるか、間違った絵です。
実績のあるビート形状と測定されたタイミング:
| ビート | 何が起こるか | タイミング |
|---|---|---|
| 到着 | ピースが1つずつドロップインし、少しオーバーシュートして落ち着く | dropIn:各0.55秒、83ミリ秒のスターター、46ピクセルの落下 |
| ハンドオフ | タイルのゴーストアウトラインが1つのオブジェクトから別のオブジェクトにスライドする | 4ゴースト、0.22秒間隔、各1.2秒、ease.io |
| 移動 | 短い光パルスがワイヤーに沿って走る。ターゲットは到着時にフラッシュする | 270 px/秒、パルスはワイヤーの約0.12~0.16、立ち上がり0.1秒、フェード0.6秒 |
| レイヤーを表示 | ソリッドボックスが透明になる。内側のレイヤーがラベルとともに一度に1つずつ点灯する | 点灯0.23秒、ホールド約1秒、消灯0.2秒、次は前のレイヤーの終了と同時に開始 |
| 選択肢を回転 | スタックが1スロット上にシフトする。アクティブなカードがアクセントカラーになる | 3秒ごとに0.9秒の移動、スロースタート、ロングイージングアウト |
| 埋めて空にする | グリッドがタイルごとに埋まり、ホールドし、空に戻る | インは7フレーム(0.12秒)間隔、アウトは2フレーム間隔 |
| 張り | ワイヤーが縮んだり伸びたりすると、取り付けられているボックスがワイヤーに沿って約6ピクセル引っ張られ、ホールドし、その後イージングバックする。これでワイヤーに張りがあるように見える | 0.23秒引っ張り、0.25秒ホールド、0.65秒イージングアウトで戻る |
| ホバーリフト | タイルが16ピクセル上昇し、ブランドカラーになり、名前が表示される | slab({ hover }):オン270ミリ秒、オフ325ミリ秒、ease.out |
接続ルール:ワイヤーは、両端が画面内にある間のみ表示されます。ループの途中で到着するものへのワイヤーは、パルスとともに描画されます(wire({ dash: '1 1' })、次にat(id, 'strokeDashoffset', t, d, 1, 0, ease.linear))。そして、それが接続するものと共に消えます。このようなワイヤーをイントロに決して配置しないでください。ワイヤーは決してボックスの上面を横切りません。接続するオブジェクトの前に追加し(オブジェクトが上に描画されるように)、その中心ではなくボックスの端から開始します。
ループルール:最後のフレームは最初のフレームと等しくなければなりません。ループ中に変更されるものはすべて、ループ終了前に元に戻ります。イントロ専用のモーションはintro()に入れます。ループはイントロが終了したときに開始し、イントロはループが開始するのと同じ絵で終了しなければなりません。イントロのない要素は、イントロ中にループの開始値を表示します。これは、ループセグメントの最後のtoです。
描画順序は固定です。後で追加されたものは常に以前に追加されたものを覆います。誰が誰を覆うかを変更するモーション(スタックの一番上から一番下に移動するカードなど)は、代わりにスロットとして描画されます。各要素は、1ステップ移動し、隣接する要素のようにフェードまたは再着色するスロットです。すべてのセグメントはスロットの静止値から再び開始するため、すべてのスロットは絵が同じように見える間に一緒にジャンプバックします。オプションごとに各スロットに1つのラベルを与え、アクティブなスロットの横に表示されるラベルをクロスフェードします。
2. レイアウトを選択し、最初に電話向けに構成する
すべての例は、layoutで構成を指定しています。wide(左から右に読む長いストリップ。ワイドシーンは約3:1、例:1200x400、ブレークポイント720、ショーケースでは全幅カードとして表示)、diagonal(アイソ軸に沿った長い行)、panels(互いに向かい合う直立したpanel())、radial(リング上のものを持つコア)、field(上から見た多数のタイル。少し急なratioが効果的)、exploded(垂直に持ち上げられたレイヤー)、またはobject(機能リストや空の状態のための1つまたは2つのオブジェクト)。描画する前にストーリーに必要なものを選んでください。シーンが異なる場合、ページは読みやすくなります。
ビューアーが再生できるシーン(interaction: 'play' | 'drag' | 'explore')は、art()のheadに小さなインラインモジュールスクリプトを同梱します。label({ html })内の実際の<button>または`<input type=