Motion · Nhịp & montage
Bốn ô chia song song
Cắt cứng thành lưới 2×2, bốn góc chạy song song bốn cảnh nhỏ (gõ chữ, đẩy nhanh, từng từ, chuỗi tương tác), lệch nhịp 3–6 khung hình.
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 nhịp điệu kiểu montage "nhiều tính năng cùng lúc"; đỉnh mật độ ở phần giữa trailer
Ý đồ
Dùng mật độ song song thay cho trình diễn tuần tự: bốn việc diễn ra trước mắt người xem cùng lúc, không kịp đọc kỹ từng cái, nhưng cứ mỗi 3–6 khung hình lại có một góc "động đậy" — nhịp lia mắt bị ghim chặt. Đây là thẻ kỹ thuật: nội dung trong ô hoàn toàn có thể thay thế, cách sắp xếp lệch nhịp mới là công thức.
Thời lượng & năng lượng
- Thời lượng: Khoảng 2.1s (63f@30fps, không chuyển cảnh suốt đoạn)
- Năng lượng: Cao (bốn tuyến song song + va chạm lệch nhịp, vị trí điệp khúc BGM chuẩn)
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)
意图
用并行密度替代顺序展示:四件事同时在眼前发生,观众来不及逐一细读,但每 3–6 帧就有一个象限"动一下"——扫视节奏被牢牢钉住。这是手法卡:格内内容全部可替换,错拍编排才是配方。
动效核心
- 四象限独立场景(内容为示例):TL 迷你浏览器逐字符打字 + 6 个 tab 依次 outBack 弹入 + 全程 inQuad 慢推 1→1.45;TR mono 打字 + t=0.42–0.54 whip 急推 scale 1→2.1(带 sin 包络 blur 4px);BL 三词按 0.24/0.46/0.56 逐词 outBack 弹入;BR 五步交互链(pill 滑入→光标贝塞尔巡游→点击缩放→改写文字→卡片弹出)
- 错拍是灵魂:TL tab 从 0.08 起每 0.13 一枚、TR 急推 0.42、BL 三词 0.24/0.46/0.56、BR 点击 0.42/0.74——任意相邻事件间隔 3–6 帧,永远有东西在动但从不齐动
- 两处打字机光标闪烁频率错开(16 帧周期 vs 14 帧周期 +5 相位),避免同闪
- BR 光标走两段二次贝塞尔(
qBez)巡游,点击瞬间sin(π)缩放 0.7 回弹 - 四格底色深浅交替(#c3c6cc/#f2f1ef/#e7e6e3/#b8bcc3)保证格线可读,无需描边
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 错拍间隔 | 相邻事件 3–6 帧 | <2 帧读作同拍(齐动=呆板);>8 帧密度散掉,轰炸感消失 | | TL 慢推 | inQuad 1→1.45,origin 50% 78% | 全程缓慢加速的"底噪运动",让静格也不静;>1.6 会裁掉 tab 行 | | TR 急推 | 0.42–0.54,scale 2.1 + blur 峰值 4px | 这是全卡最重的一拍,压 BGM 重音;blur 包络必须 sin(进出对称)| | 打字速度 | TXT1 25 字符 / 0.02–0.95 | 换文案保持字符数近似,否则打字节奏与其他象限错拍关系漂移 | | BR 交互链 | 滑入 0.12→巡游 0.3→点击 0.42→打字 0.46→二段巡游 0.62→点击 0.74→卡片 0.8 | 五步环环相扣,改任何一步需顺延后续;卡片 outBack 弹出是链尾重音 | | 底色 | 中性灰阶四档交替 | 换项目主题色时保持相邻格明度差 ≥15%,否则格线感消失 |
已知坑
- 这是手法卡:四格内容整体可换(产品截图/录屏/其他动效卡),但错拍表必须重排——新内容的关键帧继承原时刻表,否则四格变四个独立视频拼贴
- 2.1s 是密度上限时长,拉长到 3s+ 时每象限需加第二轮事件,否则后半段"没东西动"
- 光标闪烁走
floor(t*63)帧计算,改 dur 后重新核对两个光标的错相位 - 四格同时打字/同时弹入是最常见的退化写法——检查方式:逐帧过一遍,任何一帧不应有两个象限同时发生"重事件"
参考实现
demos/rhythm/quad-split-parallel-scenes/ (QuadSplitParallelScenes.tsx)
Mã nguồn tham chiếu (Remotion)
- demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.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/quad-split-parallel-scenes.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("quad-split-parallel-scenes")Prompt
Use the video-shotcraft shot recipe "Bốn ô chia song song" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/quad-split-parallel-scenes.json Raw TSX: https://gimgs.net/motion/quad-split-parallel-scenes/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/rhythm/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 2.6s 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/quad-split-parallel-scenes/QuadSplitParallelScenes.tsx =====
// quad-split-parallel-scenes — Quad Split 四宫并行蒙太奇(motion-lab 定稿转原生 Remotion)
// 手法卡:画面硬切成 2×2 四宫格,四个象限并行跑各自独立的微场景(格内内容可任意替换,
// 此处仅为示例):TL 迷你浏览器打字 + 标签堆积 + 慢推,TR mono 打字 + whip 急推,
// BL 三词逐个弹入,BR pill 滑入 → 光标贝塞尔飞行点击 → 卡片弹出。
// 关键节拍互相错开 3-6 帧、全程无转场,靠并行密度制造信息轰炸。
// 设计坐标 480×270(DesignStage 等比放大),参数以此坐标系标定。
import React from 'react';
import { DesignStage, E, lerp, seg, useT } from '../../_fixtures/Motion';
export const QUAD_SPLIT_PARALLEL_SCENES_DURATION = 63; // 2100ms @30fps
const ACCENT = '#7c5cff';
const ACCENT_SOFT = '#c9bcff';
const F = 'system-ui,-apple-system,sans-serif';
// 四宫格底色走中性灰阶(深浅交替保证格线可读),需要主题色时换成项目色板
const BGS = ['#c3c6cc', '#f2f1ef', '#e7e6e3', '#b8bcc3'];
const TRAFFIC = ['#ff5f57', '#febc2e', '#28c840'];
const TABNAMES = ['Tab One', 'Tab Two', 'Tab Three', 'Tab Four', 'Tab Five', 'Tab Six'];
const TXT1 = 'Placeholder headline text';
const TXT2 = 'and a second line of copy';
const WORDS = ['One', 'clear', 'message'];
// 二次贝塞尔取点(BR 象限光标飞行路径,坐标单位 %)
const qBez = (a: number[], b: number[], c: number[], t: number): [number, number] => {
const u = 1 - t;
return [
u * u * a[0] + 2 * u * t * b[0] + t * t * c[0],
u * u * a[1] + 2 * u * t * b[1] + t * t * c[1],
];
};
// TL —— 迷你浏览器:逐字符打字 + tab outBack 弹入 + 全程 inQuad 慢推
const QuadTL: React.FC<{ t: number; frame: number }> = ({ t, frame }) => {
const n1 = Math.floor(seg(t, 0.02, 0.95) * TXT1.length);
return (
<div
style={{
position: 'absolute',
left: '10%',
top: '22%',
width: '80%',
height: '60%',
background: '#fff',
borderRadius: 10,
boxShadow: '0 8px 24px rgba(20,40,80,.25)',
fontFamily: F,
transform: `scale(${lerp(E.inQuad(t), 1, 1.45)})`,
transformOrigin: '50% 78%',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 5, padding: '8px 10px 4px' }}>
{TRAFFIC.map((c) => (
<i key={c} style={{ width: 7, height: 7, borderRadius: '50%', background: c }} />
))}
<div style={{ display: 'flex', flex: 1, gap: 3, marginLeft: 6, minWidth: 0 }}>
{TABNAMES.map((n, i) => {
const k = seg(t, 0.08 + i * 0.13, 0.08 + i * 0.13 + 0.09, E.outBack);
return (
<div
key={n}
style={{
flex: 1,
minWidth: 0,
overflow: 'hidden',
whiteSpace: 'nowrap',
fontSize: 8,
color: '#555',
background: '#e8eaee',
borderRadius: '5px 5px 0 0',
padding: '2px 5px',
transform: `scale(${k})`,
}}
>
{n}
</div>
);
})}
</div>
</div>
<div
style={{
margin: '6px 10px',
height: 22,
borderRadius: 11,
background: '#f0f2f5',
display: 'flex',
alignItems: 'center',
padding: '0 10px',
fontSize: 10,
color: '#333',
}}
>
<b style={{ color: '#8a8f98', marginRight: 6 }}>◆</b>
<span>{TXT1.slice(0, n1)}</span>
{/* 光标按帧闪烁:16 帧一个周期 */}
<i style={{ width: 1, height: 12, background: '#333', marginLeft: 1, opacity: frame % 16 < 8 ? 1 : 0 }} />
</div>
</div>
);
};
// TR —— mono 打字 + whip 急推(错拍:0.42-0.54,推近时带运动模糊)
const QuadTR: React.FC<{ t: number; frame: number }> = ({ t, frame }) => {
const n2 = Math.floor(seg(t, 0.06, 0.9) * TXT2.length);
const zip = seg(t, 0.42, 0.54, E.inOutCubic);
return (
<div
style={{
position: 'absolute',
inset: 0,
fontFamily: '"SF Mono",Menlo,monospace',
transform: `scale(${lerp(zip, 1, 2.1)})`,
transformOrigin: '46% 42%',
filter: `blur(${Math.sin(zip * Math.PI) * 4}px)`,
}}
>
<div
style={{
position: 'absolute',
left: '12%',
top: '24%',
width: '76%',
background: '#fbf8f1',
borderRadius: 8,
boxShadow: '0 6px 20px rgba(0,0,0,.12)',
padding: '8px 12px 14px',
// 原渲染无全局 border-box:76% 是内容宽,padding 外扩(Remotion 注入
// 了 * { box-sizing:border-box },显式还原 content-box 才对得上原片)
boxSizing: 'content-box',
}}
>
<div style={{ display: 'flex', gap: 4, marginBottom: 6 }}>
{TRAFFIC.map((c) => (
<i key={c} style={{ width: 6, height: 6, borderRadius: '50%', background: c }} />
))}
</div>
<div style={{ fontSize: 8, color: '#8a8f98', marginBottom: 5 }}>✦ Section label ›</div>
<div style={{ fontSize: 11, color: '#111' }}>
<span>{TXT2.slice(0, n2)}</span>
{/* 光标错拍闪烁:与 TL 相位差 5 帧、周期 14 帧 */}
<span style={{ opacity: (frame + 5) % 14 < 7 ? 1 : 0 }}>_</span>
</div>
</div>
</div>
);
};
// BL —— 三词逐个 outBack 弹入(0.24 / 0.46 / 0.56,与其他象限错开)
const QuadBL: React.FC<{ t: number }> = ({ t }) => (
<div
style={{
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: 7,
fontFamily: F,
fontWeight: 800,
fontSize: 19,
color: '#1a1a1a',
}}
>
{WORDS.map((w, i) => {
const k = seg(t, [0.24, 0.46, 0.56][i], [0.24, 0.46, 0.56][i] + 0.1, E.outBack);
return (
<span
key={w}
style={{
transform: `scale(${k}) translateY(${(1 - k) * 8}px)`,
opacity: Math.min(1, k * 2),
}}
>
{w}
</span>
);
})}
</div>
);
// BR —— 五步交互:pill 滑入 → 光标贝塞尔飞行 → 点击缩放 → 打字回复 → 卡片弹出
const QuadBR: React.FC<{ t: number }> = ({ t }) => {
const slide = seg(t, 0.12, 0.3, E.outBack);
const m1 = seg(t, 0.3, 0.42, E.inOutCubic);
const m2 = seg(t, 0.62, 0.74, E.inOutCubic);
// 光标路径:先飞向 pill,再二段飞向发送键
const p = m2 > 0 ? qBez([44, 66], [66, 52], [82, 68], m2) : qBez([88, 30], [50, 40], [44, 66], m1);
const c1 = seg(t, 0.42, 0.47);
const c2 = seg(t, 0.74, 0.79);
const n4 = Math.floor(seg(t, 0.46, 0.62) * 9);
const pop = seg(t, 0.8, 0.88, E.outBack);
return (
<div style={{ position: 'absolute', inset: 0, fontFamily: F }}>
{/* 评论卡片:点击发送后弹出 */}
<div
style={{
position: 'absolute',
left: '12%',
bottom: '46%',
width: '66%',
background: 'rgba(255,255,255,.92)',
borderRadius: 8,
padding: '6px 9px',
boxSizing: 'content-box', // 同上:66% 为内容宽
fontSize: 8,
color: '#222',
transform: `scale(${pop})`,
transformOrigin: '20% 100%',
boxShadow: '0 5px 16px rgba(20,40,90,.25)',
}}
>
<b>You · just now</b>
<br />
All good!
</div>
{/* 输入 pill:从右侧滑入 */}
<div
style={{
position: 'absolute',
left: '8%',
bottom: '26%',
width: '84%',
height: 26,
borderRadius: 13,
background: 'rgba(255,255,255,.55)',
backdropFilter: 'blur(6px)',
display: 'flex',
alignItems: 'center',
padding: '0 8px',
boxSizing: 'content-box', // 同上:84% 为内容宽
gap: 6,
fontSize: 8,
boxShadow: '0 4px 14px rgba(20,40,90,.2)',
transform: `translateX(${(1 - slide) * 120}%)`,
marginBottom: -pop * 4,
}}
>
<span style={{ background: ACCENT, color: '#fff', borderRadius: 8, padding: '1px 5px' }}>00:00</span>
<span style={{ flex: 1, color: t < 0.44 ? '#666' : '#111' }}>
{t < 0.44 ? 'Leave your comment...' : 'All good!'.slice(0, n4)}
</span>
<span style={{ color: n4 >= 9 ? ACCENT : ACCENT_SOFT }}>➤</span>
</div>
{/* 光标圆点:两段点击各缩一次 */}
<div
style={{
position: 'absolute',
width: 9,
height: 9,
borderRadius: '50%',
background: '#fff',
border: '1.5px solid #333',
zIndex: 5,
boxShadow: '0 1px 4px rgba(0,0,0,.3)',
left: `${p[0]}%`,
top: `${p[1]}%`,
transform: `scale(${1 - Math.sin(c1 * Math.PI) * 0.3 - Math.sin(c2 * Math.PI) * 0.3})`,
}}
/>
</div>
);
};
export const QuadSplitParallelScenes: React.FC = () => {
const t = useT();
const frame = Math.floor(t * 63); // dur 2100ms @30fps
const scenes = [
<QuadTL key={0} t={t} frame={frame} />,
<QuadTR key={1} t={t} frame={frame} />,
<QuadBL key={2} t={t} />,
<QuadBR key={3} t={t} />,
];
return (
<DesignStage bg="#000" raster="zoom">
{scenes.map((scene, i) => (
<div
key={i}
style={{
position: 'absolute',
left: `${(i % 2) * 50}%`,
top: `${(i >> 1) * 50}%`,
width: '50%',
height: '50%',
overflow: 'hidden',
background: BGS[i],
}}
>
{scene}
</div>
))}
</DesignStage>
);
};
- 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/quad-split-parallel-scenes/QuadSplitParallelScenes.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.