Motion · Nhịp & montage
Cắt cứng theo nhịp
Hai kiểu dùng cắt cứng làm nhạc cụ gõ nhịp — chuỗi cắt cứng tăng tốc dần (khoảng cách giảm nửa) và chớp đèn liên hoàn định hình (ba lần lóe trắng, mỗi lần một khung hình khác).
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
Biến thể
Sáu góc nhìn cắt cứng toàn màn hình theo khoảng cách giảm nửa dần (16→12→8→6→4f), tăng tốc áp sát; cú cắt cuối đóng băng đột ngột về khung chính kèm đẩy nhẹ để ổn định.
Cảm giác tăng tốc áp sát: dồn lực kiểu trailer, tính năng bắn liên tiếp lao đến kết luận.
Ba lần lóe trắng, mỗi lần cắt cứng sang một khung hình khác của cùng footage (toàn cảnh → cận thẻ → cận số liệu), dư âm màn trập lắng xuống, lần lóe thứ ba dừng ở số liệu.
Cảm giác nghi thức của khoảnh khắc cao trào: chụp liên hoàn trao giải, tôn vinh con số then chốt.
Khi nào dùng
Biến "cú cắt" thành tiếng trống cho đoạn cao trào/tăng tốc; kiểu A giống trailer tăng tốc áp sát, kiểu B mang cảm giác nghi thức chụp liên hoàn trao giải.
Ý đồ
Từ vựng nhịp điệu hiện có trong thư viện chỉ quản lý tốc độ "trong một chuyển động" (speed-ramp là đổi tốc trong cùng một take) và "giữ hình bao lâu sau khi ổn định" (các quy tắc thở hệ R) — chưa có từ nào quản lý cách sắp xếp điểm cắt giữa nhiều cảnh. Thẻ này bổ sung phần đó: cắt cứng không qua chuyển tiếp nào, bản thân khoảng cách giữa các điểm cắt chính là bản nhạc — khoảng cách giảm dần đọc thành tăng tốc áp sát (A), ba lần lóe cách đều đọc thành chụp liên hoàn (B). Không xung đột với hệ R: R quản lý thời gian giữ sau khi ổn định, thẻ này quản lý cách sắp điểm cắt, và sau khi chuỗi cắt kết thúc phải có đủ thời gian giữ để "trả lại" nhịp thở. Lưu ý đây là ngoại lệ có chủ đích — quy tắc ưu tiên nhịp điệu vốn chỉ một chiều "chậm lại", nhưng chuỗi cắt càng dồn dập thì thời gian giữ sau đó càng phải dài hơn bình thường.
Thời lượng & năng lượng
- Thời lượng: A: toàn đoạn ~4.3s (xây dựng 49f + chuỗi năm cú cắt + giữ hình đóng băng 35f); B: toàn đoạn ~4.3s (footage sống 30f + ba lần lóe + giữ 60f).
- 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)
意图
库内节奏词汇一直只管"一条运动内"的速率(speed-ramp 是同一条 take 里变速)和"落定后停多久"(R 系呼吸判例),没有词管多镜之间切点 怎么排。本卡补这一块:硬切不做任何过渡,切点间隔本身就是乐谱—— 间隔递减读作加速逼近(A),等距三闪读作快门连拍(B)。与 R 系不 冲突:R 管落定后的停,本卡管切点排布,切串结束必须接足量 hold 把 呼吸还回去。注意这是刻意的例外——节奏偏好判例是单向"放慢",硬切 串切得越狠,切完的 hold 要比平常更长。
两式选型
| 式 | 做法 | 适用 | |----|------|------| | A beat-cut-accelerando 递进硬切串 | 六视图按 16→12→8→6→4f 间隔减半全屏硬切,加速逼近,末刀戛然定格回主画面轻推收住 | 冲刺逼近感:预告片式蓄力、功能连打冲向结论 | | B paparazzi-flash 连闪定格 | 三次白闪各硬切同素材不同裁切(全景→卡片特写→数字特写),快门余韵沉降,第三闪停在数字收束 | 高光时刻仪式感:颁奖连拍、关键数字的加冕 |
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | A 切点帧 | 0/49/65/77/85/91/95(建立 49f,间隔 16→12→8→6→4f) | 间隔按"减半律"缩,等差缩读不出加速;末间隔 <4f 肉眼跟不上 | | A 视图池 | 6 个 = 两块 FakeDashboard × scale 1/1.8/2.6 + 对焦点,transformOrigin 钉焦点再平移居中 | 视图必须是同一产品的不同构图(语义连续),六个无关画面读作故障 | | A 切帧快门感 | 切帧 1f brightness 1.05 + 叠 6% 不透明度白层 | 只在切帧亮一下,是"咔"不是闪光灯;再厚就抢了 B 式的活 | | A 末段收住 | 定格回主画面 hold 35f,scale 1→1.06 ease-out 慢推 20f 后静止 | 戛然而止是这式的句号——末刀后不许再有任何切 | | B 三闪切点 | 30/52/70(间隔 22f→18f 渐紧),闪前 30f 慢推"活素材"衬托闪后定格 | 闪前必须是活的,全片死素材三闪读不出"定格" | | B 白闪层 | 每闪 opacity 0.95→0 走 4f ease-out 衰减;闪帧 ±2px seed 哈希全屏抖动 | 0.95 起步才有闪光灯的爆点;衰减 >6f 变柔光转场,失去快门感 | | B 快门余韵 | 切入视图 scale 1.03→1 走 6f ease-out 回落 + −16px 沉降归零 | 余韵是"被拍到晃了一下";去掉后三闪读作幻灯片翻页 | | B 三裁切 | 同一 Footage 换 scale/transformOrigin:1.0 全景 / 2.3 卡片 / 4.0 数字 | 层层逼近有叙事方向;乱序(特写→全景→特写)读作剪错 | | B 末段收束 | 第三闪后数字特写 hold 60f 静止 | 三闪是重击,hold 要给足平常两倍以上 |
已知坑
- **demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验**
- 两式都是声音强依赖手法:切点必须钉在音轨拍点上,每切一声
tick(A)/ 快门咔嚓(B),无声版只剩画面乱跳;钉帧走 sound-design.md §4.5 相对 shot 起点,不钉绝对帧
- A 式六视图要"同一产品的不同构图"而非六个无关画面——观众要在加速
中始终认得这是谁;跨产品/跨场景的素材别用这式
- 切串全片 ≤1 次(P4 克制原则):这是全片最响的一记,第二次出现
第一次就白打了
- 节奏偏好判例是单向"放慢",本卡是刻意例外——切完的 hold(A 35f /
B 60f)是底线不是建议,宁多勿少
- 真硬切 = frame 落区间渲染对应视图,无任何过渡帧;手滑加 2f 交叉
淡化就不是硬切了,节拍感立即糊掉
参考实现
demos/rhythm/beat-cut-moves/ (BeatCutAccelerando.tsx / PaparazziFlash.tsx)
Mã nguồn tham chiếu (Remotion)
- demos/rhythm/beat-cut-moves/BeatCutAccelerando.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/beat-cut-moves/BeatCutAccelerando.tsx
- demos/rhythm/beat-cut-moves/PaparazziFlash.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/beat-cut-moves/PaparazziFlash.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-cut-moves.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/rhythm/beat-cut-moves/BeatCutAccelerando.tsx, demos/rhythm/beat-cut-moves/PaparazziFlash.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("beat-cut-moves")Prompt
Use the video-shotcraft shot recipe "Cắt cứng theo nhịp" (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-cut-moves.json Raw TSX: https://gimgs.net/motion/beat-cut-moves/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/rhythm/beat-cut-moves/BeatCutAccelerando.tsx, demos/rhythm/beat-cut-moves/PaparazziFlash.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 4.39s 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-cut-moves/BeatCutAccelerando.tsx =====
import React from 'react';
import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';
import { FakeDashboard, G } from '../../_fixtures/Fixtures';
// beat-cut-accelerando:六个不同构图按 16→12→8→6→4 帧递减间隔全屏硬切,
// 加速逼近,最后一切戛然定格回主画面并 1→1.06 慢推收住。
// 真硬切:frame 落在哪个区间就渲染哪个视图,无任何过渡。
// 一个视图 = FakeDashboard 变体 + 缩放 + 对焦点(画面上要被推到屏幕中心的点)
type View = { variant: 'A' | 'B'; scale: number; cx: number; cy: number };
const VIEWS: View[] = [
{ variant: 'A', scale: 1, cx: 960, cy: 540 }, // v0 全景(建立)
{ variant: 'A', scale: 1.8, cx: 1070, cy: 576 }, // v1 卡片区
{ variant: 'A', scale: 2.6, cx: 600, cy: 340 }, // v2 单卡特写
{ variant: 'B', scale: 1, cx: 960, cy: 540 }, // v3 列表页全景
{ variant: 'B', scale: 1.9, cx: 1070, cy: 500 }, // v4 列表行区
{ variant: 'B', scale: 2.8, cx: 1070, cy: 290 }, // v5 单行特写
];
// 每段起始帧:建立 49f,然后间隔 16→12→8→6→4,末段(回主画面)hold 35f
// 0–48 v0 | 49–64 v1 | 65–76 v2 | 77–84 v3 | 85–90 v4 | 91–94 v5 | 95–129 定格
const CUTS = [0, 49, 65, 77, 85, 91, 95];
const FINAL = 95; // 最后一切:戛然定格回主画面
const ViewShot: React.FC<{ view: View; extraScale?: number }> = ({ view, extraScale = 1 }) => {
const s = view.scale * extraScale;
return (
<div
style={{
width: 1920,
height: 1080,
transformOrigin: `${view.cx}px ${view.cy}px`,
transform: `translate(${960 - view.cx}px, ${540 - view.cy}px) scale(${s})`,
}}
>
<FakeDashboard variant={view.variant} />
</div>
);
};
export const BeatCutAccelerando: React.FC = () => {
const frame = useCurrentFrame();
// 当前落在哪个区间(末段 = 主画面 v0)
let seg = 0;
for (let i = 0; i < CUTS.length; i++) {
if (frame >= CUTS[i]) seg = i;
}
const isFinal = seg === CUTS.length - 1;
const view = isFinal ? VIEWS[0] : VIEWS[seg];
// 末段慢推:scale 1 → 1.06,ease-out,推 22f 后静止(结尾静止 ≥15f)
const push = isFinal
? interpolate(frame, [FINAL, FINAL + 20], [1, 1.06], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
})
: 1;
// 每次硬切的 1f 亮度跳变(约 +5%)模拟快门
const isCutFrame = CUTS.some((c, i) => i > 0 && frame === c);
const flash = isCutFrame ? 1.05 : 1;
return (
<AbsoluteFill style={{ background: G.bg, overflow: 'hidden' }}>
<div style={{ position: 'absolute', inset: 0, filter: `brightness(${flash})` }}>
<ViewShot view={view} extraScale={push} />
</div>
{/* 切帧再叠一层极薄白闪,保证肉眼可感 */}
{isCutFrame && (
<AbsoluteFill style={{ background: '#ffffff', opacity: 0.06 }} />
)}
</AbsoluteFill>
);
};
// ===== demos/rhythm/beat-cut-moves/PaparazziFlash.tsx =====
import React from 'react';
import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';
import { FakeDashboard, TitleBlock, G } from '../../_fixtures/Fixtures';
// paparazzi flash 连闪定格:高光时刻三连白闪,每闪硬切同一素材的不同裁切
// (全景→卡片特写→数字特写),每闪切入画面带 1.03→1 回落 + 半格沉降像快门
// 余韵,白闪帧加微位移抖动模拟快门震,第三闪后停在数字特写收束。
const F1 = 30; // 第一闪(间隔 22f)
const F2 = 52; // 第二闪(间隔 18f)
const F3 = 70; // 第三闪,之后 hold 60f
const SETTLE = 6; // 切入回落时长
const DECAY = 4; // 白层衰减时长
const FLASHES = [F1, F2, F3];
// seed 正弦哈希,返回 -1..1(禁 Math.random)
const hash = (i: number) => {
const s = Math.sin(i * 127.3) * 43758.5453;
return (s - Math.floor(s)) * 2 - 1;
};
// 同一"素材":全景 dashboard,顶行中间卡片上叠一个大数字,供数字特写裁切
const Footage: React.FC = () => (
<div style={{ position: 'absolute', width: 1920, height: 1080 }}>
<FakeDashboard variant="A" />
<div style={{ position: 'absolute', left: 1070, top: 350, transform: 'translate(-50%, -50%)' }}>
<TitleBlock text="84,213" size={104} />
</div>
</div>
);
// 四个视图:闪前"活素材"慢推 + 三个定格裁切(全景 / 卡片 / 数字)
type View = { scale: number; origin: string };
const VIEW_WIDE: View = { scale: 1.0, origin: '960px 540px' };
const VIEW_CARD: View = { scale: 2.3, origin: '1070px 335px' };
const VIEW_DIGIT: View = { scale: 4.0, origin: '1070px 350px' };
export const PaparazziFlash: React.FC = () => {
const frame = useCurrentFrame();
// 当前处于哪一段:-1 = 闪前活素材段
let seg = -1;
for (let i = 0; i < FLASHES.length; i++) {
if (frame >= FLASHES[i]) seg = i;
}
let scale: number;
let origin: string;
let settleScale = 1;
let settleY = 0;
if (seg === -1) {
// 闪前:中景慢推(活的),衬托闪后定格的"死"
origin = '900px 480px';
scale = interpolate(frame, [0, F1], [1.16, 1.2], {
extrapolateRight: 'clamp',
easing: Easing.inOut(Easing.quad),
});
} else {
const view = [VIEW_WIDE, VIEW_CARD, VIEW_DIGIT][seg];
scale = view.scale;
origin = view.origin;
// 切入回落:1.03→1 的 6f 收敛 + 半格沉降(-16px 落回 0)像快门余韵
const t = interpolate(frame, [FLASHES[seg], FLASHES[seg] + SETTLE], [1, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
settleScale = 1 + 0.03 * t;
settleY = -16 * t;
}
// 白闪层:每个切点 0.95→0,4f 衰减,取各闪最大值
let flash = 0;
for (const f of FLASHES) {
const o = interpolate(frame, [f, f + DECAY], [0.95, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.quad),
});
if (frame >= f && frame < f + DECAY + 1) flash = Math.max(flash, o);
}
// 白闪帧全屏微位移抖动模拟快门震(seed 哈希,非随机)
const inFlash = FLASHES.some((f) => frame >= f && frame < f + DECAY);
const jx = inFlash ? 2 * hash(frame * 7 + 1) : 0;
const jy = inFlash ? 2 * hash(frame * 13 + 5) : 0;
return (
<AbsoluteFill style={{ background: G.ink, overflow: 'hidden' }}>
<div
style={{
position: 'absolute',
inset: 0,
transform: `translate(${jx}px, ${jy + settleY}px)`,
}}
>
<div
style={{
position: 'absolute',
width: 1920,
height: 1080,
transform: `scale(${scale * settleScale})`,
transformOrigin: origin,
}}
>
<Footage />
</div>
</div>
{/* 白闪层 */}
{flash > 0 && (
<AbsoluteFill style={{ background: '#ffffff', opacity: flash }} />
)}
</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-cut-moves/BeatCutAccelerando.tsx, demos/rhythm/beat-cut-moves/PaparazziFlash.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.