Motion · Nhịp & montage
Vòng nhịp đổi từ theo theme
Máy đếm nhịp ba kênh — danh sách tính từ trên nền tối trượt lên một dòng mỗi nhịp, viên thuốc cố định giữa khung "bắt" từ tiếp theo và đổi màu, toàn bộ nền đổi màu cùng nhịp.
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
Đoạn "cùng một sản phẩm, nhiều sắc thái/đa theme" (chuỗi tính từ kiểu modern/playful/expressive); đoạn nhịp dày đặc nhất phim; đồng bộ theo nhịp nhạc.
Ý đồ
Đổi màu theme một lần chỉ là demo tính năng; 0.6 giây đổi một lần, đổi liên tiếp ba lần, mỗi lần "dòng được chọn + màu viên thuốc + nền toàn cảnh" nhảy cùng một khung hình — thế là thành máy đếm nhịp. Dùng biến đổi rẻ nhất để tạo mật độ nhịp điệu cao nhất phim. Có hai điểm sống còn: một là ba kênh phải khóa cùng một điểm nhịp, chỉ cần một kênh trễ nửa nhịp, "nhảy cùng lúc" sẽ thoái hóa thành ba hoạt ảnh rời rạc; hai là ranh giới giữa nhảy và trượt — cú nhảy chỉ chiếm 6f đầu nhịp, 12f còn lại đứng yên hoàn toàn, điểm nhịp phải "nhảy" chứ không "trượt", nếu không danh sách cuộn đều chỉ còn là một danh sách.
Thời lượng & năng lượng
- Thời lượng: Dẫn nhịp 30f + mỗi nhịp 18f × số nhịp + giữ hình kết; 3 nhịp ≈ 3.5s (demo 110f).
- Năng lượng: Cao (mỗi 0.6s ba kênh nhảy cùng lúc, mật độ cao tạo năng lượng cao).
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.6 秒换一次、连换三次、每次"选中行+胶囊色 +全场底色"三件事同帧齐跳——就成了节拍器。用最便宜的变量切换做出 全片最高的节奏密度。命门有二:一是三通道必须锁同一拍点,任何 一通道慢半拍,"齐跳"退化成三个各自为政的动画;二是跳与滑的分界 ——跳变只占拍头 6f,其余 12f 完全静置,拍点要"跳"不要"滑", 匀速滚动的列表就只是列表。
动效核心
- 胶囊固定在视口中央行不动,列表 translateY 逐拍步进——视觉上
读作"胶囊跳到下一行接住新词",实际动的是世界不是胶囊
- 拍长 18f(0.6s @30fps ≈ 100BPM),跳变窗仅拍头 6f:
snap(t) = 1-(1-x)^3.2(陡 ease-out),其余帧静置
- 三通道同源驱动:同一个 tInBeat 同时喂列表位移、胶囊/底色
rgb 逐通道 cross-mix、胶囊 squash——一个时钟,不许各挂各的曲线
- 主题表逐拍换对(demo:灰白/深棕→绿→紫→红/深藏青),胶囊色与
底色是配好的深浅对,不是独立随机色
- 胶囊落位 squash:scale 1.12→0.97→1 随拍头 6f 走完
- 选中词反白、非选中词 34% 白;上下各 300px 底色渐变羽化收视口
——羽化色必须跟 bgColor 实时同步,写死颜色换场时穿帮
- 开拍前 30f 静置铺垫——先让观众看清"这是个列表",再开始打拍
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 拍长 | 18f(BEAT_LEN,改它即改 BPM 对音轨) | <12f 观众来不及读词只见闪色;>26f 拍感散成逐个展示 | | 跳变窗 | 拍头 6f,ease-out 指数 3.2 | 窗 >10f 或缓曲线,"跳"变"滑",节拍器塌成滚动列表 | | 拍数 | 3–4 拍 | 5+ 拍观众适应了节奏开始走神;1–2 拍立不起"节拍器"人设 | | squash | 1.12→0.97→1 | 去掉它胶囊像贴图换色;>1.2 弹跳抢了换色的戏 | | 底色跨度 | 相邻主题底色须同明度不同色相(都深色) | 明度跳变(深→浅)会读作闪屏而非换肤 | | 铺垫/收尾 | 前 30f 静置;末拍后 hold ≥30f | 没铺垫第一拍读作 bug;末拍即切没有"停在最终色"的确认 |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验
- 与 theme-switch-moves 分工:那卡是单次换肤的仪式(扫场/涟漪,换一次
讲一次故事),本卡是连续多次换肤当节拍用——讲"深色模式功能"用那卡, 讲"多气质/多主题体量"用本卡
- 与 cel-flash-stomp 同为高密度拍点段,同片二选一或隔远——都是
"每 0.6s 一记"的手法,紧邻观众会拍疲劳
- 声音强依赖:拍点对齐鼓点(换 BEAT_LEN 对 BPM),无声版三通道
齐跳读作界面抽风
- 词表长度 ≥ 拍数+2——列表底部要留未选中的词垫着,最后一拍就停在
列表末行会露出"下面没了"
- 反白判定跟 beat 整数走、颜色跟 tInBeat 小数走(demo 即如此)——
两者都跟小数会出现半白半灰的中间态词
参考实现
demos/rhythm/beat-step-list-theme-cycle/ (BeatStepListThemeCycle.tsx) 原片出处:bear-app 22.3–24.6s
Mã nguồn tham chiếu (Remotion)
- demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.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.
- 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.
- Clone repo video-shotcraft; component import fixture chung từ demos/_fixtures và helper từ assets/lib.
- Đă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/beat-step-list-theme-cycle.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("beat-step-list-theme-cycle")Prompt
Use the video-shotcraft shot recipe "Vòng nhịp đổi từ theo theme" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/beat-step-list-theme-cycle.json Raw TSX: https://gimgs.net/motion/beat-step-list-theme-cycle/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 3.71s 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).
// ===== demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx =====
// beat-step-list-theme-cycle(bear 22.3–24.6s)
// 深色场形容词列表逐拍步进:每拍列表上移一行 + 选中胶囊跳到下一行并换色
// (绿→紫→红),整场底色同步换(深棕→深紫→深藏青)。
// 一拍之内"行、色、场"三通道同步跳变,~0.6s 一拍。
import React from 'react';
import { AbsoluteFill, interpolate, useCurrentFrame } from 'remotion';
const FONT = 'Helvetica, Arial, sans-serif';
const ROW_H = 150;
const WORDS = ['modern', 'playful', 'expressive', 'seamless', 'intuitive'];
// 拍点:~0.6s 一拍 = 18 帧
const BEAT_LEN = 18;
const FIRST_BEAT = 30; // 前 1s 静置铺垫
const N_BEATS = 3;
// 每拍的(胶囊色 / 场底色)——起始态 + 三拍
const THEMES = [
{ pill: '#d8d8d4', bg: '#241a12', ink: '#2a2018' }, // modern:灰白胶囊 / 深棕场
{ pill: '#4fae62', bg: '#1e2416', ink: '#173015' }, // playful:绿
{ pill: '#8e6fd8', bg: '#221a33', ink: '#2a2144' }, // expressive:紫
{ pill: '#d64d55', bg: '#141c2e', ink: '#1a2440' }, // seamless:红 / 深藏青
];
// 拍内跳变:5 帧内完成,ease-out + 微过冲
const snap = (t: number) => interpolate(t, [0, 1], [0, 1], {
easing: (x) => 1 - Math.pow(1 - x, 3.2),
extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
});
export const BeatStepListThemeCycle: React.FC = () => {
const frame = useCurrentFrame();
// 当前拍序号与拍内进度(跳变只占拍头 6 帧)
const raw = (frame - FIRST_BEAT) / BEAT_LEN;
const beat = Math.min(N_BEATS, Math.max(0, Math.floor(raw) + 1)); // 已触发的拍数
const beatStartFrame = FIRST_BEAT + (beat - 1) * BEAT_LEN;
const tInBeat = beat === 0 ? 1 : snap((frame - beatStartFrame) / 6);
// 连续步进量:整数拍 + 拍头 6 帧内的插值
const step = beat === 0 ? 0 : (beat - 1) + tInBeat;
// 三通道 —— 1) 列表上移一行
const listY = -step * ROW_H;
// 2) 胶囊换色(拍头硬跳,带一点 cross-fade)
const themePrev = THEMES[Math.max(0, beat - 1)];
const themeNow = THEMES[beat];
const mixT = beat === 0 ? 1 : tInBeat;
const mix = (a: string, b: string, t: number) => {
const pa = [1, 3, 5].map((i) => parseInt(a.slice(i, i + 2), 16));
const pb = [1, 3, 5].map((i) => parseInt(b.slice(i, i + 2), 16));
return `rgb(${pa.map((v, i) => Math.round(v + (pb[i] - v) * t)).join(',')})`;
};
const pillColor = mix(themePrev.pill, themeNow.pill, mixT);
const bgColor = mix(themePrev.bg, themeNow.bg, mixT);
// 胶囊每拍落位时 squash 弹一下
const pop = beat === 0 ? 1 : interpolate(tInBeat, [0, 0.6, 1], [1.12, 0.97, 1]);
// 选中行文字反白判定:胶囊固定在视口中央行,选中词 = WORDS[beat]
const selectedIdx = beat;
return (
<AbsoluteFill style={{ background: bgColor, fontFamily: FONT, justifyContent: 'center' }}>
{/* 中央固定胶囊(列表在其下滚动,视觉上"胶囊跳到下一行") */}
<div style={{
position: 'absolute', left: '50%', top: 540 - ROW_H / 2 + 10,
width: 900, height: ROW_H - 20, transform: `translateX(-50%) scale(${pop})`,
background: pillColor, borderRadius: 999,
boxShadow: '0 14px 40px rgba(0,0,0,0.35)',
}} />
{/* 词列表 */}
<div style={{
position: 'absolute', left: 0, right: 0, top: 540 - ROW_H / 2,
transform: `translateY(${listY}px)`,
}}>
{WORDS.map((w, i) => {
const isSel = i === selectedIdx;
return (
<div key={w} style={{
height: ROW_H, display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
<span style={{
fontSize: 92, fontWeight: 800, letterSpacing: -1.5,
color: isSel ? (beat === 0 ? '#2a2018' : '#ffffff') : 'rgba(255,255,255,0.34)',
position: 'relative', zIndex: 2,
}}>{w}</span>
</div>
);
})}
</div>
{/* 视口上下羽化 */}
<div style={{ position: 'absolute', left: 0, right: 0, top: 0, height: 300, background: `linear-gradient(${bgColor}, transparent)`, zIndex: 3 }} />
<div style={{ position: 'absolute', left: 0, right: 0, bottom: 0, height: 300, background: `linear-gradient(transparent, ${bgColor})`, zIndex: 3 }} />
</AbsoluteFill>
);
};
- Read the recipePrompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
- Get the codetool: git
git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install - Register + rendertool: remotion
npx remotion render <CompositionId> out.mp4 # component: demos/rhythm/beat-step-list-theme-cycle/BeatStepListThemeCycle.tsx - Or re-implementPort the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
| param | type | default | description |
|---|---|---|---|
| — | |||
Cùng danh mục

Ngữ pháp trailer: ba nhịp
Ba thủ pháp ngữ pháp trailer — trailer-bumper cắt nhanh móc câu mở đầu, card-footage-cadence xen thẻ chữ với cảnh đối thoại, smash-cut đập cắt vào tĩnh lặng.

Đổi tốc độ và đóng băng chú thích
Hai thủ pháp nhịp điệu remap số khung hình phi tuyến — speed-ramp (nhanh → 0.2x nhìn kỹ → nhanh) và freeze-annotate (chảy → đóng băng khoanh chú thích → tan băng).

Gạch chân hóa phổ tần
UI hóa phổ tần — gạch chân tiêu đề tách thành dải cột dọc nhảy theo phổ tần suốt hai nhịp, rồi thu lại thành đường thẳng; hình ảnh hóa nhạc sống trong UI.

Ảnh bóng lặp kiểu smear
Ảnh bóng smear — thẻ để lại 4 bản sao bán trong suốt rõ, đếm được khi lướt ngang nhanh, gộp lại làm một lúc dừng; phương án dựng hình thay cho motion blur.