motion-figure について
motion-figure は、アニメーションや入力に応答する図を作るエージェントスキルです。レッスン用の図、仕組みを操作するパネル、装飾用ディザーフィールドを対象に、実装制約やアクセシビリティの確認方法を示します。
主な機能
- 013つの図のティア
- 02SMIL とスライダー式のレッスン図
- 03操作可能な仕組みパネル
- 04WebGL ディザーフィールド
- 05モーションとアクセシビリティの確認
活用シーン
- 01時間とともに変わるプロセスの表示
- 02読者による仕組みの操作
- 03ページ見出しや区切りへの動的な模様の追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rohitg00/ ai-engineering-from-scratch --skill motion-figureインストール後、Claude Code で/motion-figureを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/dither-fields.mdmechanism-figures.mdsmil-rules.mdtokens.mdtoolkit.mdtemplates/dither.jsmechanism.html詳細
Motion figure
このスキルでは、動いたり入力に応答したりする図を作成します。3 つのティアがあります。SKILL.md を読み込む任意のエージェントである Claude、Cursor、Codex、OpenClaw、Hermes と組み合わせて、このスキルを使用してください。
| ティア | 作成するもの | 配置先 |
|---|---|---|
| 1. レッスン図 | 宣言型 SMIL アニメーション、または SVG を再描画するスライダーウィジェットです。どちらも ```figure フェンスからマウントされます。 | コースのレッスン |
| 2. メカニズム図 | システムの名前付きパーツを表示する HTML パネルです。読者が 1 つのコントロールを操作すると、計算された判定が表示されます。 | 単独ページ、README、エッセイ、レッスン以外のコースサイト |
| 3. ディザーフィールド | 用紙色、アクセント色、インク色で描画された規則的なディザードットの WebGL 帯で、4 つのシード付きモーションがあります。 | ページのマストヘッドとセクション区切り |
レッスンのランタイムには厳しい制限があるため、ティア 1 には最も多くのルールがあります。ティア 2 と 3 には、それぞれ専用のリファレンスファイルがあります。
使用する場面
- 時間とともに変化するプロセスについてレッスンで説明する場合。段階を移動するトークン、満たされていくキュー、繰り返すループなどです。
- 入力の変化によって出力が変わる数式についてレッスンで説明する場合。学習率、温度、チャンクサイズ、バッチサイズなどです。
- 作者が「この図をアニメーション化して」「スライダーを追加して」「図をインタラクティブにして」と依頼した場合。
- 読者が 1 つの値を変え、システムの動作を確認できるデモがページに必要な場合。キャッシュ、キュー、コンテキストウィンドウ、ルーターなどです。ティア 2 を使用してください。
- コースの色を使った、ゆっくり動くヘッダーがページに必要な場合。ティア 3 を使用してください。
使用しない場面
- 時間軸も入力もない、固定された構造のアイデアの場合は、静的な SVG 図を作成してください。静的な図については、同系統のスキル
blueprint-figureで扱います。 - 独自の状態モデルと機械の形状を備えた、完全な解説ページを作る場合は、同系統のスキル
isometric-explainerで扱います。 - 配色、フォント、または文章のルールだけが必要な場合は、同系統のスキル
course-styleを参照してください。このスキルでは、必要な要素をreferences/tokens.mdに複製しています。
手順
-
図が示す唯一の変化を表す文を 1 つ書きます。2 文必要なら、図を 2 つ作成してください。
-
ティアを選びます。レッスン内ではティア 1 を使用します。ティア 2 では
references/mechanism-figures.mdを読み、templates/mechanism.htmlから始めます。ティア 3 ではreferences/dither-fields.mdを読み、templates/dither.jsをコピーします。以下の手順はティア 1 用です。ティア 1 では、変数が時間の場合はアニメーション図を、変数が数値の場合はスライダーウィジェットを使用します。 -
request-pathやcontext-budgetのように、ケバブケースで名前を決めます。まずレジストリでその名前を検索してください。同じ名前を登録する後続モジュールが、先に登録された図を置き換えます。 -
コードを
figures-<topic>.jsという名前のモジュールファイルに記述します。IIFE で囲み、window.LFを読み込み、存在しない場合は早期に return します。 -
図の関数
function (host, cfg)を作成します。hostに.lfカードを 1 つ追加し、破棄関数を返すことができます。 -
カードを構築します。大文字の
.lf-labelと短いヒントを含む.lf-head、.lf-body、キャプションを含む.lf-capを使用します。 -
SVG には
viewBox="0 0 520 H"を使用します。コースの図では、H は 200〜250 が一般的です。棒が 1 本だけなら、もっと小さくできます。 -
任意のレンダー関数の外側で一度だけ作成する ID を使って、
<title>と<desc>を追加します。 -
アニメーション図では、まずエントリを追加してから、遅いループを 1 つ追加します。
references/smil-rules.mdに従い、各行を確認してください。 -
スライダーウィジェットでは、
LF.sliderまたはLF.selectを呼び出す前にstate._renderを定義します。最後にstate._render()を 1 回呼び出してください。 -
モジュールの最後で
LF.register({ 'your-name': fn })を呼び出します。 -
レッスンの Markdown にフェンスを追加します。1 行目に名前を記述し、その後に任意の JSON 設定を追加します。
```figure context-budget {"windowK": 32} ``` -
「完了前の確認」に記載されているチェックを実行します。ライトテーマ、ダークテーマ、幅 375 px、動きを抑える設定で図を確認してください。
ページが図をマウントする方法
- Markdown パーサーは、フェンスを
<div class="lesson-figure" data-figure="name {json}">に変換します。 - サイトのビルド処理は、レッスンの Markdown からフェンス名を検索します。引用された名前を含むモジュールファイルを見つけて、マニフェストを書き込みます。対応するプロバイダーがないフェンス名があると、ビルドは停止します。
- ページは、そのレッスンで必要なモジュールだけを、固定された順序で読み込みます。新しいモジュールファイルは、記載されているファイルの後にアルファベット順で追加します。
- マウント関数は、最初の単語を名前として読み取り、残りを JSON として解析します。不正な JSON の場合、空の
cfgが返されます。 - 図の関数が例外をスローすると、ランタイムはホストの内容を空にして、警告を記録します。レッスンの残りの部分は引き続きレンダリングされます。
- マウント後、ランタイムは、タイトル、説明、
role="img"、および不足しているコントロールラベルを追加します。図に SMIL アニメーションがある場合は一時停止ボタンを、いずれかのアニメーションが一度だけ再生される場合は再生ボタンを追加します。 - 図がビューポートから外れたとき、タブが非表示になったとき、または読者が一時停止を押したとき、ランタイムはアニメーションを一時停止します。視差効果を減らす設定が有効な場合、または印刷時には、静止フレームを表示します。
references/toolkit.md には、すべてのランタイム関数と、完全なモジュールの例が2つ記載されています。
ルール
アニメーション付きの図:
<animate>、<animateTransform>、<animateMotion>のみを使用します。図ではsetIntervalやrequestAnimationFrameを使用しないでください。- 各図の SVG 要素は55個以下にします。
- 要素は不透明度0、スケール0.95から、不透明度1、スケール1へ変化させます。スケールを0から開始してはいけません。
- 要素の表示には
calcMode="spline"とkeySplines="0.23 1 0.32 1"を使用します。表示時間は0.5〜0.6秒にします。 - 表示の開始タイミングは0.05〜0.4秒ずらします。非表示の時間は表示時間より短くします。
- ループの長さは2.5〜6秒とし、
repeatCount="indefinite"を使用します。ループにfill="freeze"を設定しないでください。ランタイムがアニメーションを1回だけ再生するようになります。 - 各
keyTimesの項目数はvaluesの項目数と同じにします。0から始まり、1で終わり、値が減少しないようにします。 - 各
keySplinesの項目数はkeyTimesより1つ少なくします。calcMode="spline"の場合にのみ使用します。 keyPointsを指定したanimateMotionでは、calcMode="linear"を使用します。keyPointsの項目数はkeyTimesの項目数と同じにします。valuesは指定しません。- アニメーションする
fillまたはstrokeの値にvar()を含めないでください。対象の色を持つ2つ目の要素を用意し、その不透明度をアニメーションさせます。 - ループでアニメーションさせるのは、不透明度、transform、
stroke-dashoffset、またはパスに沿った移動のみです。x、y、x1、y1、x2、y2、width、height、r、cx、cy、rx、ry のいずれかをアニメーションさせると、ランタイムはそれを1回だけ再生します。 - ホストの
data-static-time(デフォルトは1.5秒)時点のフレームは、完成した読みやすい状態にします。後の時点で全体が完成した状態になる場合は、ホストにこの属性を設定します。 - 要素は中心を基準にスケールさせます。グループの内容は (0, 0) を基準に描画し、グループは
translateで移動させます。スケールにはadditive="sum"を追加します。
スライダーウィジェット:
- ES5 のみを使用します。
var、関数式、文字列連結を使います。アロー関数、let、const、テンプレートリテラルは使用しません。 - 最初の
LF.sliderまたはLF.selectの呼び出しより前に、state._renderを定義します。スライダーを作成するときに、レンダー関数がラップされます。 state._renderでは、すべての SVG 子要素を削除してから追加し直します。ノードをその場で編集しないでください。- ID は
state._renderの外で一度だけ作成します。新しいノードにない属性(ID を含む)は、ランタイムによって削除されます。 - バーは長さ、またはパスの
stroke-dasharrayで伸ばします。ノードは中心を基準にスケールされるため、スケールでバーを伸ばさないでください。 - 各スライダーのラベルは短い小文字表記にし、例の
chunk size (tokens)のように、単位を括弧で囲みます。
両方に共通:
20. すべての色は CSS 変数から取得し、ライトテーマの値をデフォルトにします。例:var(--blueprint,#3553ff)。変数が使えない場合に限り、16進数の値を直接指定します。
21. メインの対象にはアクセントカラー(--blueprint)を1色まで、2つ目の状態には --warn を1色使います。それ以外には ink と rule のトークンを使います。
22. SVG のテキストは12単位以上にし、var(--font-mono) を使います。ラベルを小さくする必要がある場合は、SVG の最小幅を500 pxにし、横スクロールできるようにします。
23. ラベルのサイズはスマートフォンで測定します。幅375 pxの画面では、カード内の幅520単位の SVG が279 px幅で表示されました。その場合、12単位のラベルは約6.4 pxで表示されました。
24. キャプションは平易な技術英語で書きます。それぞれの色が何を表すか明記します。文は25語以内にします。
25. .lf-label のラベルは大文字で、2~4語にします。
メカニズム図(ティア2):
- 実際の名前と数値を表示します。ファイル名、キー、トークン数などです。
item 1や、名前のないグレーのバーは使いません。 - 読者が操作できる項目を1つ設けます。ステップ、スライダー、トグル、再生、停止のいずれかです。
- 本文の数値から算出した判定を、テキストで最後に示します。結果を固定で記述してはいけません。
- 図の35パーセントが表示されたら1回実行し、「Replay」ボタンを追加します。ループはさせません。
- アニメーションには
transformとopacityのみを使い、cubic-bezier(0.23, 1, 0.32, 1)を指定します。押下は120~160 ms、表示は200~300 ms、折りたたみは380~500 msにします。 - リストは項目ごとに30~70 msずつ遅らせます。対象は最初の8項目までです。
- すべてのラベルを、どのフレームでも省略、重なり、ドット上へのテキスト表示なしで、全体が見えるようにします。テキストは11 px以上、判定は12.5 px以上にします。
- 幅320 pxではパネルを1列に並べ、各コントロールのタッチ領域を40 pxにします。
- サイト内の他のページでは使われていないメカニズムを使い、サイトのメカニズム表に追加します。
ディザフィールド(ティア3):
--bg、--blueprint、--inkから色を読み取り、data-themeの変更時に再描画します。- 描画は最大でも毎秒30フレームとし、バンドが表示されている間だけ行います。
- モーション低減が有効な場合は静止フレームを1つ描画します。WebGL が使えない場合は静的なハッチを表示します。
- バンドに
aria-hidden="true"を指定し、ラベルチップ以外ではドット上にテキストを表示しません。
完了前に確認すること
- フェンス名が登録名と完全に一致し、ほかのモジュールが同じ名前を登録していない。
- モジュールに
=>、let、const、バッククォート文字列が含まれていない。 - モジュールに
setIntervalとrequestAnimationFrameが含まれていない。 - マウントされたホストに対して
references/smil-rules.mdのバリデーターを実行すると、空のリストが返される。 - アニメーションする各図の SVG 要素数が55個以下である。
- すべてのループの長さが2.5~6 sである。
- ループでルール11に記載された幾何属性をアニメーションさせていない。
- モーション低減が有効な場合、図全体が表示され、要素が欠けていない。
- ダークテーマで、すべての線とラベルが見える。
- 幅375 pxで、すべてのラベルが読みやすく、図の外側でページが横スクロールしない。
- ウィジェットの場合、各スライダーを最小値と最大値に動かしても、
NaN、負の幅、コンソールエラーが発生しない。 - SVG にタイトルが1つ、説明が1つあり、スライダーを動かしてもそれらの ID が変わらない。
- キャプションに、図で使われているすべての色が記載されている。
- ティア2:
references/mechanism-figures.mdの末尾にあるチェックリストの項目がすべて合格している。 - ティア3:
references/dither-fields.mdの末尾にあるチェックリストの項目がすべて合格している。
ファイル
references/smil-rules.md:12 個の SMIL ルールの全容、各ルールが防ぐ問題、およびブラウザーのコンソールで実行できるバリデーターです。references/toolkit.md:LF関数とマウント手順です。完全なスライダーウィジェット、完全な SMIL 図、およびコースサイト以外で使うためのプレビューページもあります。references/tokens.md:ライトテーマとダークテーマの色トークン、フォント、モーションのタイミング値です。references/mechanism-figures.md:ティア 2 です。実際の名前、却下された図、メカニズムの形状、構造、判定、モーションの予算、およびチェック項目について説明しています。references/dither-fields.md:ティア 3 です。シェーダーの描画方法、4 つのモーション、マークアップ、ルール、ディザ処理されたバー、およびチェック項目について説明しています。templates/mechanism.html:ティア 2 の完全な図です。サイズコントロール付きの LRU キャッシュで、どのブラウザーでも開けます。templates/dither.js:ティア 3 の完全なスクリプトです。読み込んで、<div class="dither" data-seed="...">を追加してください。
AI Engineering from Scratch リポジトリ内では、ランタイム用の site/lesson-figures.js と、その他の図用の site/figures-*.js も参照してください。