mcpField
V
vid-motion-graphics

vid-motion-graphics

github.com/varnan-techクリエイティブ・メディア68745 回インストール2 週間前に更新
リポジトリ

vid-motion-graphics について

vid-motion-graphicsは、複数シーンのモーショングラフィックス動画を作るクリエイター向けのスキルです。HTML/CSSアニメーションをフレーム単位で描画し、FFmpegでMP4にまとめて、決定的な出力を行います。

主な機能

  1. 01複数シーンのMP4動画生成
  2. 021~6シーンに対応
  3. 035種類のビジュアルスタイル
  4. 04画面比率、フレームレート、音楽の指定
  5. 05ChromiumとFFmpegによる書き出し

活用シーン

  1. 01コンテンツ概要から動画を作成
  2. 02データや要点を含む短編動画の作成
  3. 03用途に合わせた画面比率での書き出し

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/varnan-tech/opendirectory --skill vid-motion-graphics

インストール後、Claude Code で/vid-motion-graphicsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
evals/
evals.json
references/
scene-library.md
style-presets.md
scripts/
capture-frames.mjs
export-video.sh

詳細

vid-motion-graphics

複数シーンのモーショングラフィックスを MP4 として生成します。Playwright を介してヘッドレス Chromium で HTML/CSS アニメーションをレンダリングし(Web Animations API によるフレームシーク)、PNG フレームを FFmpeg で組み立てます。React、AI API、Python は不要です。graphic-gif ファミリー以外に新たな依存関係はありません。

CDN フォントのみを使用します。HTML 内で外部ライブラリは使用しません。


重要なルール(生成のたびに事前にお読みください)

  1. シーン遷移には window.renderFrame(t) を使用してください。CSS の @keyframes は使わないでください。 Chromium では、CSS アニメーションの currentTime を使ったシークは、後ろ向きのシーク時に黙って無視されます。renderFrame の方法では、純粋な JS 関数がミリ秒単位の値から opacity/transform を直接計算します。Playwright は各フレームでこの関数を 1 回呼び出します。決定論的で、競合状態が発生しません。
  2. どの要素にも animation-delay を設定しないでください。 renderFrame を使えば不要です。animation-delay を書こうとしていることに気付いたら、そこで止めてください。アーキテクチャが間違っています。
  3. window.__videoReady = true を設定するのは、document.fonts.ready.then(...) の内部だけにしてください。 同期的に設定しないでください。Playwright が 1 フレーム目をキャプチャする前にフォントを読み込む必要があります。そうしないと、テキストが代替フォントでレンダリングされます。
  4. window.__stopPreview() を公開してください。 ブラウザーの rAF プレビューループと、Playwright の evaluate/スクリーンショット呼び出しが競合します。capture-frames.mjs はフレームループの前に __stopPreview() を呼び出します。必ず含めてください。
  5. シーン境界の確認には t <= ではなく t < startMs を使ってください。 フレーム 0 で t <= 0 とすると、シーン 1 が黒くなります。正しいガードは if (t < startMs || t >= endMs) return hidden です。
  6. body の寸法はピクセル単位の正確な値にしてください。 幅と高さは整数(1080px、1920px)にします。%、vw/vh、レスポンシブ単位は使わないでください。
  7. 10% の登場オーバーラップを除き、複数のシーンを同時に表示しないでください。 すべてのシーンは renderFrame の表示対象時間以外では opacity: 0 にします。
  8. トランジションには opacity のみを使用してください。 display の切り替えや visibility は使わないでください。GPU 合成された opacity はフレーム単位で正確です。
  9. チャットに HTML を貼り付けないでください。 ファイルに保存し、概要のみを表示します。
  10. タイトルにはトピックではなくメッセージを示してください。 「Q4 2024 Performance Video」ではなく、「Q4 に目標を達成した 3 つの理由」とします。
  11. HTML を生成する前に、必ず references/scene-library.md を読んでください。 そのファイルに記載された HTML 構造と CSS クラス名をそのまま使用します。

Step 1:情報の確認

必須: content_brief

省略可能なパラメーターとデフォルト値:

パラメーターデフォルト値説明
content_brief—動画で伝える内容を説明するテキスト(必須)
scenesautoシーン数(1~6)。auto の場合はブリーフから決定します。
duration_per_scene3sシーンごとの長さ(秒、1~8 秒)
stylekinetic-darkkinetic-dark / editorial-light / data-pulse / bold-type / minimal-clean
aspect_ratio1:11:1(1080×1080) / 16:9(1920×1080) / 9:16(1080×1920)
fps301 秒あたりのフレーム数(24、30、または 60)
musicnoneバックグラウンドトラックに使用する音声ファイルのパス(mp3/m4a/wav)
source—最終フレームのフッターに表示する出典

content_brief がない場合は、次のとおり正確に尋ねてください:

"動画を作成するには、コンテンツブリーフが必要です。動画で何を伝えますか?

例:「Q4 の売上が 85% 増加した 3 つの理由を示してください。新規エンタープライズ契約、解約率の低下、値上げです。目を引く数字を使ってください。スタイル:data-pulse。」

省略可能:スタイル(デフォルト:kinetic-dark)、アスペクト比(デフォルト:1:1)、シーンごとの秒数(デフォルト:3s)"

content_brief がある場合は、すぐに Step 2 に進みます。


ステップ 2:内部アーキテクチャ(ユーザーには表示しない)

1. ブリーフをシーンに分割します(最大 6 シーン):

  • シーン 1 = フックまたはタイトル(必ず含めます)
  • シーン 2〜N-1 = 補足ポイント、指標、またはストーリーの展開
  • シーン N = CTA または締めくくりの要約(シーンが 2 つ以上の場合は必ず含めます)
  • 1 シーンにつき重要なアイデアは 1 つにします。ブリーフにアイデアが 7 つ以上ある場合は、重要度の低いものをまとめます

2. references/scene-library.md を読み、各シーンのタイプを選びます:

  • フック/オープニング → title-card
  • 単一の指標 → stat-reveal
  • 2〜4 個のポイントのリスト → bullet-list
  • ビフォー/アフターまたは 2 つの値 → split-screen
  • お客様の声/引用 → quote-card
  • 最終シーン/CTA → cta-card

3. references/style-presets.md を読み、選択したスタイルの CSS トークンとアニメーションの個性を読み込みます。

4. タイミングを計算します:

totalDuration = sceneCount × duration_per_scene  (seconds)
totalFrames   = totalDuration × fps

各シーンは、@keyframes の全範囲のうち (100 / sceneCount)% を占めます。

シーン開始 %終了 %
10%(100/N)%
2(100/N)%(200/N)%
………
N((N-1)×100/N)%100%

各シーンの範囲内:

  • 表示:シーン範囲の最初の 10%
  • 維持:シーン範囲の 10% から 85%
  • 非表示:シーン範囲の 85% から 100%

5. ピクセル寸法を決定します:

  • 1:1 → W=1080, H=1080
  • 16:9 → W=1920, H=1080
  • 9:16 → W=1080, H=1920

ステップ 3:HTML 生成

コードを書く前に、references/scene-library.md と references/style-presets.md を読みます。

必須の HTML 構造:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
[font CDN link from style preset]
<style>
:root {
  [all CSS tokens from style preset]
}

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
  width: [W]px; height: [H]px;
  overflow: hidden;
  background: var(--bg);
  font-family: var(--font-body);
  position: relative;
}

.scene {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 80px;
  opacity: 0;
  will-change: opacity, transform;
}
.scene-inner {
  width: 100%;
  max-width: 960px;
}

/* Scene-type CSS from scene-library.md */
[paste scene-type CSS here]
</style>
</head>
<body>

<div class="scene scene-1">
  <div class="scene-inner">
    [scene 1 HTML from scene-library.md template]
  </div>
</div>

[repeat for each scene]

<script>
window.__videoReady = false;
window.TOTAL_DURATION_MS = [totalDuration * 1000];

// ── Animation helpers ─────────────────────────────────────────────────────────
function lerp(a, b, p) { return a + (b - a) * p; }
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
function easeOutCubic(t) { return 1 - Math.pow(1 - clamp(t, 0, 1), 3); }

function sceneState(t, startMs, endMs) {
  if (t < startMs || t >= endMs) return { opacity: 0, ty: 0 };
  const prog = (t - startMs) / (endMs - startMs);
  if (prog < 0.10) {
    const p = easeOutCubic(prog / 0.10);
    return { opacity: p, ty: lerp(24, 0, p) };
  }
  if (prog < 0.85) return { opacity: 1, ty: 0 };
  const p = (prog - 0.85) / 0.15;
  return { opacity: 1 - p, ty: lerp(0, -12, p) };
}

function applySceneState(el, state) {
  el.style.opacity = state.opacity;
  el.style.transform = state.ty !== 0 ? `translateY(${state.ty.toFixed(2)}px)` : '';
}

// ── Main render function — called by Playwright once per frame ────────────────
window.renderFrame = function(t) {
  // Scene 1: 0ms – [D]ms
  applySceneState(document.querySelector('.scene-1'), sceneState(t, 0, [D]));
  // Scene 2: [D]ms – [2D]ms
  applySceneState(document.querySelector('.scene-2'), sceneState(t, [D], [2D]));
  // ... repeat per scene ...
};

// ── Preview loop — stopped by Playwright before frame capture ─────────────────
let __previewActive = false;
let __previewRafId = null;
window.__stopPreview = function() {
  __previewActive = false;
  if (__previewRafId !== null) { cancelAnimationFrame(__previewRafId); __previewRafId = null; }
};

document.fonts.ready.then(() => {
  window.renderFrame(0);
  window.__videoReady = true;
  __previewActive = true;
  const startTime = performance.now();
  function previewTick() {
    if (!__previewActive) return;
    const elapsed = performance.now() - startTime;
    if (elapsed < window.TOTAL_DURATION_MS) {
      window.renderFrame(elapsed);
      __previewRafId = requestAnimationFrame(previewTick);
    } else {
      window.renderFrame(window.TOTAL_DURATION_MS - 1);
      __previewActive = false;
    }
  }
  __previewRafId = requestAnimationFrame(previewTick);
});
</script>
</body>
</html>

デザイン品質のルール:

  • 見出しのフォントサイズは 60px 未満にしないでください。モバイルのサムネイルサイズでも文字が読める必要があります
  • 数字や統計値は、画面上で常に最も大きな要素にします(120〜200px)
  • ディスプレイ用書体では、文字間隔を詰めます:-0.02em〜-0.04em
  • 1 シーンにつきアクセントカラーは 1 色にします。複数の要素にアクセントを散らさないでください
  • ダークプリセット:区切り線は rgba(255,255,255,0.10) にし、不透明な単色は使いません
  • ライトプリセット:区切り線は rgba(0,0,0,0.10) にし、不透明な単色は使いません
  • transform を使用するすべての要素に transform-origin: center center を設定します
  • .scene 内のパディングは最小 80px にします。テキストがビューポートの端に触れないようにしてください

ステップ 4:セルフ QA(ステップ 5 に進む前にすべての不備を修正します)

renderFrame の正確性:

  • window.renderFrame(t) が定義されている。純粋関数であり、スタイルの書き込み以外の副作用がない
  • シーン境界で t < startMs を使う(t <= startMs ではない)。フレーム 0 が黒くなるのを防ぐ
  • シーン遷移に animation-delay または @keyframes を使用していない
  • 2 つのシーンの opacity: 1 の表示期間が重複していない(表示時の 10% の重複を除く)
  • window.__stopPreview() が公開されており、プレビューの rAF ループが __previewActive を確認している

準備完了シグナル:

  • document.fonts.ready の前に window.__videoReady = false が宣言されている
  • window.__videoReady = true の設定は document.fonts.ready.then(...) の内部のみで行われている
  • window.renderFrame(0) が document.fonts.ready.then(...) の内部で、__videoReady = true の設定前に呼び出されている

レイアウト:

  • html, body に正確なピクセル寸法([W]px、`[H]px)が指定されている
  • body の幅/高さに %、vw、vh、rem 単位を使用していない
  • html, body に overflow: hidden が設定されている
  • すべてのシーンに position: absolute; inset: 0 が設定されている

デザイン:

  • すべての色がスタイルプリセットのトークンを使用している。独自の hex 値は使用していない
  • すべてのフォントがスタイルプリセットのものを使用している。独自の font-family 文字列は使用していない
  • 見出しのフォントサイズが 60px 以上である
  • source パラメーターが指定されている場合、最終シーンにソースがある

ステップ 5:エクスポート

ブリーフの内容からスラッグを決定します(ケバブケース、30 文字以下):

mkdir -p chart/[slug]

HTML を保存します:chart/[slug]/video.html

ブラウザでプレビューします:

open chart/[slug]/video.html

エクスポートを実行します([skill-root] をこのスキルのディレクトリのパスに置き換えます):

bash [skill-root]/scripts/export-video.sh \
  chart/[slug]/video.html \
  chart/[slug]/video.mp4 \
  --duration [totalDuration] \
  --fps [fps] \
  --width [W] \
  --height [H] \
  [--music path/to/audio.mp3]

初回実行時にスクリプトが Playwright をインストールし(Chromium は約 200MB)、すべてのフレームをキャプチャしてから、FFmpeg で MP4 を組み立てます。


ステップ 6:出力の概要

## Video: [title from brief]
Date: [YYYY-MM-DD] | Scenes: [N] | Style: [style] | Aspect: [ratio]
Duration: [N]s | FPS: [fps] | Frames: [N]

Files
  Source:   chart/[slug]/video.html
  Output:   chart/[slug]/video.mp4
  Size:     [X] MB

Checklist
- [ ] All scenes appear in sequence with no blank frames
- [ ] Text legible at mobile thumbnail size
- [ ] Scene transitions smooth (no jump cuts)
- [ ] Final scene includes CTA or closing message
- [ ] Source attribution present in final frame (if provided)

プロンプトのヒント(ユーザーがガイダンスを求めたときに表示)

「コンテンツの概要を提示してください。各シーンで伝える内容を箇条書きにしてください。」

「インサイトを直接示してください。『第4四半期に85%成長した3つの理由』のようにすると、動画の軸が定まります。」

「希望するスタイルがあれば指定してください。キネティック・ダーク(デフォルト)、エディトリアル・ライト、データ・パルス、ボールド・タイプ、ミニマル・クリーン。」

「プラットフォームに合わせてアスペクト比を指定してください。LinkedIn/Instagramのフィードは1:1、ストーリーズ/リールは9:16、YouTube/プレゼンテーションは16:9です。」

✅ 良い例:「9秒の動画を作成してください。第4四半期の売上は420万ドル(85%成長)でした。成長要因:エンタープライズ案件、解約率1.2%、値上げ。CTA:acme.com/q4。スタイル:データ・パルス。アスペクト比:1:1。」

❌ 悪い例:「当社についての動画を作って」

コメント