Motion · Intro

Trường icon nhuộm màu

Lưới icon nhỏ thang xám xuất hiện lệch nhịp phủ kín màn hình, dừng một nhịp, rồi các dải sóng màu thương hiệu quét cực nhanh xuống dưới nhuộm màu toàn cảnh trong tích tắc.

1920×108016:930 fps5.06s · 152 frameremotionGiấy phép: Apache-2.0#shotcraft#remotion#intro#opening#ui-entrance#outro

Nguồn: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Composition Remotion (React/TSX) — có video preview, không có player trong trình duyệt

Khi nào dùng

Mở đầu bày ra bề mặt năng lực sản phẩm (icon = vũ trụ tính năng) rồi bất ngờ bật màu thương hiệu; trang tổng hợp tính năng, thể hiện quy mô hệ sinh thái/tích hợp, đệm trước logo mở đầu.

Ý đồ

Trước tiên bày đủ "nhiều" bằng thang xám — hàng trăm icon nhỏ lệch nhịp xuất hiện phủ kín màn hình, thang xám đảm bảo đọc như họa tiết nền chứ không giành spotlight; dừng một nhịp để khán giả nhận ra "đã đầy rồi", rồi màu thương hiệu quét cực nhanh qua toàn cảnh dưới dạng dải sóng ngang, thế giới xám bừng sáng trong tích tắc. Điểm chốt là đổi màu không phải lật cứng cùng khung hình: tư liệu gốc đo được nhiều đợt sóng màu quét xuống lần lượt trong ~0.5s, trạng thái cuối là bốn dải màu phân lớp — cảm giác gợn sóng cho "bật sáng" một hướng và một tốc độ; lật cứng chỉ đọc như đổi một tấm ảnh khác.

Thời lượng & năng lượng

  • Thời lượng: Xuất hiện lệch nhịp ~45f + đứng yên ~10f + đổi màu 12–45f + đứng yên trạng thái cuối; cả đoạn 3–4s
  • Năng lượng: Trung bình (xuất hiện là bước dọn đường, khoảnh khắc đổi màu là đỉnh duy nhất)

Công thức cảnh quay (bản gốc tiếng Trung — timing, easing, bảng tham số, lỗi hay gặp)

意图

先用灰阶把"多"摆足——上百个小图标错峰浮进来铺满画面,灰阶保证 读作背景纹理不抢戏;停一拍让观众意识到"摆满了",然后品牌色以 横带波纹形式极快扫过全场,灰世界一瞬间被点亮。命门在翻色不是 同帧硬翻:原片实测是 ~0.5s 内多道色波依次向下快扫(蓝先覆盖全场, 橙/绿/红依次覆盖更低行带),终态呈四色横带分层——波纹感让"点亮" 有方向和速度,硬翻只读作换了张图。

动效核心

  • 图标场:150–200 个小图标网格排布(行间半格错位),灰阶 3–4 档

随机;mulberry32 定分组/灰度/形状

  • 浮现:分 ~10 批错峰淡入(批间 4f + 批内 ≤3f 抖动),每个带

0.55→1.07→1 微弹(ease-out 入 + 7% 过冲回落)

  • 静置一拍(~10f):让"铺满"被读到,翻色前的蓄力
  • 翻色:多道色波按行推进,行差 ~1.4f + 列微倾 0.25f + 每图标 ≤1.5f

抖动(斜切波前沿,不是整行同帧);每道波从自己的起始行向下覆盖, 后波覆盖更低行带 → 终态多色横带

  • 翻色瞬间该图标 1.22x pop(正弦鼓包 3f),给波前沿加"啪"的质感

参数表

| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 图标数 | 170(17×10 @1080p,CELL 110px) | <100 读不出"原野";>250 单个太小翻色 pop 看不见 | | 浮现总长 | ~45f(10 批 × 4f + 尾批淡入 8f) | 太快读作闪现;>60f 观众等不及爆点 | | 翻色总长 | 原片 ~12f 全场完成;demo 45f | 越快越爆;教学/慢节奏场合才拉长 | | 波数 | 4 道(对应终态色带数) | 1 道=单色点亮也成立;波色=品牌色板 | | 行差 | 1.4f/行 + 列 0.25f 微倾 | 行差 >3f 波读作逐行扫描仪;0 = 硬翻 | | 翻色 pop | 1.22x 正弦 3f | 无 pop 波前沿读不出来;>1.4x 图标互相打架 |

已知坑

  • demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,

首次实战须以真实素材回验

  • 翻色不许同帧硬翻——原片密帧核实为多道波纹快扫(bear-app

25fps 密帧:蓝→橙/绿/红约 0.5s 内完成),硬翻读作换图无"点亮"感

  • 图标需可辨认的实心简笔形(心/星/闪电/对勾…);抽象色块读不出

"功能宇宙"语义

  • 与 bubble-swarm-takeover 分工:那张是气泡群涌入接管

画面的运动戏;本卡运动极克制,戏眼在灰→彩的瞬间翻色

  • 与 theme-switch-moves 分工:那张是 UI 界面明暗主题切换;本卡

是装饰性图标场的品牌色点亮,不承载界面语义

参考实现

demos/opening/icon-field-colorize/ (IconFieldColorize.tsx) 原片出处:bear-app.mp4 ≈0.0–3.0s

Mã nguồn tham chiếu (Remotion)

  • demos/opening/icon-field-colorize/IconFieldColorize.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/opening/icon-field-colorize/IconFieldColorize.tsx

Component import fixture chung từ demos/_fixtures và helper từ assets/lib của repo; xem SKILL.md trong repo để biết quy trình render.

Mọi thứ trên trang này đều có bản máy đọc. Đưa agent link JSON, hoặc thêm MCP server gimgs rồi bảo nó tìm motion template.

  1. Tải JSON — trong đó có công thức cảnh quay (timing, easing, bảng tham số, lỗi hay gặp) và toàn bộ source TSX.
  2. Clone repo video-shotcraft; component import fixture chung từ demos/_fixtures và helper từ assets/lib.
  3. Đăng ký component thành <Composition> trong project Remotion rồi render bằng `npx remotion render`, hoặc dựng lại công thức bằng engine của bạn.
curl -s https://gimgs.net/motion/icon-field-colorize.json
git clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/opening/icon-field-colorize/IconFieldColorize.tsx
npx remotion render <CompositionId> out.mp4
claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("icon-field-colorize")

Prompt

Use the video-shotcraft shot recipe "Trường icon nhuộm màu" (Remotion composition, Apache-2.0) published on gimgs.net.
Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/icon-field-colorize.json
Raw TSX: https://gimgs.net/motion/icon-field-colorize/template.html
Repository: https://github.com/Vincentwei1021/video-shotcraft  (component: demos/opening/icon-field-colorize/IconFieldColorize.tsx; imports demos/_fixtures and assets/lib)
Spec: 1920x1080, 30 fps, 5.06s

1. Read the JSON and follow its "prompt" section (when to use, intention, parameter table, known pitfalls).
2. Adapt it for: <describe what you want>.
3. Either clone the repo, register the component as a <Composition> in a Remotion project and run npx remotion render <CompositionId> out.mp4 — or re-implement the recipe in your own engine (HTML/WAAPI works: keep the same timing and easing).

JSON · Markdown · llms.txt

5,126 chars · Apache-2.0Mở template gốc ↗
// ===== demos/opening/icon-field-colorize/IconFieldColorize.tsx =====
// icon-field-colorize —— 灰阶图标原野错峰浮现(错峰淡入+微弹),停一拍后
// 蓝色整场极快扫翻,随后橙/绿/红三道色波依次向下扫过,终态四色横带。
// 对标 bear-app.mp4 0–3s(密帧核实:非同帧硬翻,是极快多道波纹翻色)。
import React from 'react';
import { AbsoluteFill, interpolate, useCurrentFrame } from 'remotion';

const mulberry32 = (a: number) => () => {
  let t = (a += 0x6d2b79f5);
  t = Math.imul(t ^ (t >>> 15), t | 1);
  t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
  return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};

// 16 个简笔图标(viewBox 0 0 24 24,纯填充),求可辨认不求精致
const SHAPES: ((c: string) => React.ReactNode)[] = [
  (c) => <path fill={c} d="M12 21s-7.5-4.7-9.6-9C.9 8.7 2.7 5 6.2 5c2 0 3.3 1 4 2.1h3.6C14.5 6 15.8 5 17.8 5c3.5 0 5.3 3.7 3.8 7-2.1 4.3-9.6 9-9.6 9z" transform="scale(0.92) translate(1,0)" />,
  (c) => <path fill={c} d="M12 2l2.7 6.3 6.8.5-5.2 4.4 1.6 6.6L12 16.2 6.1 19.8l1.6-6.6L2.5 8.8l6.8-.5z" />,
  (c) => <path fill={c} d="M13 2L4 14h6l-1 8 9-12h-6z" />,
  (c) => <circle fill={c} cx="12" cy="12" r="9" />,
  (c) => <path fill={c} d="M9 3v12.3A3.5 3.5 0 1 0 11 18V7h7v6.3A3.5 3.5 0 1 0 20 16V3z" transform="scale(0.85) translate(1.5,1.5)" />,
  (c) => <path fill={c} d="M4 4h16a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H9l-5 4V6a2 2 0 0 1 2-2z" transform="scale(0.85) translate(2,2)" />,
  (c) => <><rect fill={c} x="3" y="6" width="18" height="13" rx="2.5" /><circle fill="#fff" cx="12" cy="12.5" r="3.6" /><rect fill={c} x="8" y="3.5" width="8" height="4" rx="1.5" /></>,
  (c) => <path fill={c} d="M6.5 19a4.5 4.5 0 0 1-.4-9A6 6 0 0 1 17.8 8.7 4.2 4.2 0 0 1 17.5 19z" />,
  (c) => <path fill={c} d="M12 2a7 7 0 0 0-7 7c0 5 7 13 7 13s7-8 7-13a7 7 0 0 0-7-7zm0 9.5A2.5 2.5 0 1 1 12 6a2.5 2.5 0 0 1 0 5.5z" transform="scale(0.9) translate(1.2,0.5)" />,
  (c) => <path fill={c} d="M3.5 10L12 3l8.5 7V21h-6v-6h-5v6h-6z" />,
  (c) => <><rect fill={c} x="3" y="8" width="18" height="4" rx="1" /><rect fill={c} x="5" y="12" width="14" height="9" rx="1" /><rect fill="#fff" x="10.9" y="8" width="2.2" height="13" /></>,
  (c) => <path fill={c} d="M9.4 20.6l-1 1.9 7.2-.1 4.9-8.5-2.2.1-2 3.4L13 4.5h-2.1L7.6 17.3z" transform="scale(0.9) translate(1,1)" />,
  (c) => <path fill={c} d="M20.3 6.7L9.6 17.4l-5.9-5.9 2.1-2.1 3.8 3.8 8.6-8.6z" />,
  (c) => <path fill={c} d="M21.5 15.5L14 12V5.5a2 2 0 1 0-4 0V12l-7.5 3.5v2l7.5-2v4l-2.5 2v1.5l4.5-1 4.5 1V21.5l-2.5-2v-4l7.5 2z" transform="scale(0.85) translate(1.8,1.8)" />,
  (c) => <><circle fill={c} cx="12" cy="12" r="4" /><path fill={c} d="M12 1.5l1.4 3.6h-2.8zM12 22.5l-1.4-3.6h2.8zM1.5 12l3.6-1.4v2.8zM22.5 12l-3.6 1.4v-2.8zM4.6 4.6l3.5 1.5-2 2zM19.4 19.4l-3.5-1.5 2-2zM19.4 4.6l-1.5 3.5-2-2zM4.6 19.4l1.5-3.5 2 2z" /></>,
  (c) => <path fill={c} d="M20 4C10 4 4.5 9 4 15.7 3.9 17.5 4 20 4 20s2.7-.2 4.4-.3C15 19.2 20 13.5 20 4zM6.8 17.2C10 11 15 8 15 8s-5.5 1.5-8.8 8z" />,
];

const COLS = 17, ROWS = 10, CELL = 110, ICON = 44;
const GRAYS = ['#9a9a9a', '#7c7c7c', '#b0b0b0', '#8b8b8b'];
// 四道色波:蓝覆盖全场,橙/绿/红依次覆盖更低的行带 → 终态四色横带
const WAVES = [
  { color: '#5b67e8', fromRow: 0, start: 78 },
  { color: '#e7a03c', fromRow: 3, start: 90 },
  { color: '#34a56f', fromRow: 6, start: 100 },
  { color: '#e2606b', fromRow: 9, start: 110 },
];

export const IconFieldColorize: React.FC = () => {
  const frame = useCurrentFrame();
  const rand = mulberry32(20260718);
  const icons: React.ReactNode[] = [];

  for (let r = 0; r < ROWS; r++) {
    for (let c = 0; c < COLS; c++) {
      const r1 = rand(), r2 = rand(), r3 = rand();
      // —— 错峰浮现:分 10 批,批内再抖动
      const batch = Math.floor(r1 * 10);
      const t0 = 4 + batch * 4 + r2 * 3;
      const ap = interpolate(frame, [t0, t0 + 8], [0, 1], {
        extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
      });
      if (ap <= 0) continue;
      // 微弹:0.55 → 1.07 → 1
      const easeOut = 1 - Math.pow(1 - ap, 3);
      const appearScale = ap < 1 ? 0.55 + easeOut * 0.52 - Math.max(0, ap - 0.72) * 0.25 : 1;

      // —— 色波:每道波从起始行向下快扫(行 1.4 帧 + 列微倾 + 抖动)
      const gray = GRAYS[Math.floor(r2 * GRAYS.length)];
      let color = gray;
      let flipPhase = 0; // 最近一次翻色的进行度,用于 pop
      for (const w of WAVES) {
        if (r < w.fromRow) continue;
        const arrive = w.start + (r - w.fromRow) * 1.4 + c * 0.25 + r3 * 1.5;
        const p = interpolate(frame, [arrive, arrive + 3], [0, 1], {
          extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
        });
        if (p > 0) { color = w.color; flipPhase = p; }
      }
      const flipPop = flipPhase > 0 && flipPhase < 1
        ? 1 + Math.sin(flipPhase * Math.PI) * 0.22 : 1;

      const shape = SHAPES[Math.floor(r3 * SHAPES.length) % SHAPES.length];
      const x = 48 + c * CELL + (r % 2) * 26;
      const y = 26 + r * CELL;
      icons.push(
        <div key={`${r}-${c}`} style={{
          position: 'absolute', left: x, top: y, width: ICON, height: ICON,
          opacity: ap, transform: `scale(${appearScale * flipPop})`,
        }}>
          <svg viewBox="0 0 24 24" width={ICON} height={ICON}>{shape(color)}</svg>
        </div>,
      );
    }
  }

  return <AbsoluteFill style={{ background: '#ffffff' }}>{icons}</AbsoluteFill>;
};


  1. Read the recipe
    Prompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
  2. Get the code
    tool: git
    git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install
  3. Register + render
    tool: remotion
    npx remotion render <CompositionId> out.mp4 # component: demos/opening/icon-field-colorize/IconFieldColorize.tsx
  4. Or re-implement
    Port the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
paramtypedefaultdescription
—

Cùng danh mục