Motion · Nhịp & montage
Đổ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).
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ể
Remap số khung hình phi tuyến đổi tốc độ — lướt nhanh đột ngột giảm còn 0.2x để nhìn kỹ điểm nhấn, rồi tăng tốc rời đi
Dòng thẻ đóng băng toàn khung giữa chừng, chú thích khoanh tròn bật ra chỉ rõ điểm nhấn, sau đó tan băng tiếp tục trôi
Khi nào dùng
Làm chậm/dừng lại một điểm nhấn trong dòng thẻ trôi/lia ngang dài; dùng freeze-annotate trong ngữ cảnh giảng giải, hướng dẫn
Ý đồ
Dòng trôi đều tốc độ đọc như PPT (R2), toàn đoạn trôi nhanh khán giả không bắt kịp điểm nhấn (R3). Speed-ramp tạo ra "tăng tốc-nhìn kỹ-tăng tốc" trong một chuyển động; freeze-annotate đi xa hơn — dừng hẳn lại, dùng bút dạ khoanh điểm nhấn rồi mới đi tiếp. Hai kiểu cùng gốc kỹ thuật (remap số khung hình) nhưng ngữ nghĩa khác: speed-ramp là "đi qua liếc thêm một cái", freeze là "dừng giờ gạch chân trọng tâm".
Thời lượng & năng lượng
- Thời lượng: Speed-ramp toàn đoạn 4–5s (cửa sổ chậm ≥40f); freeze-annotate toàn đoạn 4–5s (đoạn đóng băng ≥45f)
- Năng lượng: Trung bình cao (bản thân độ tương phản tốc độ là nhịp năng lượng)
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)
意图
匀速流读作 PPT(R2),全程快流观众抓不住重点(R3)。变速在一条 运动里制造"冲刺-凝视-冲刺";定格标注更进一步——干脆停下来, 马克笔圈出重点再走。两款同一技术根(帧号 remap),语义不同: 变速是"路过时多看一眼",定格是"停课划重点"。
动效核心
- 源动画匀速,输出帧对源帧做非线性 remap:
src = interpolate(frame, [0,40,85,135], [0,88,97,207])(斜率即速率)
- 变速款:快段斜率 ≥2、慢窗斜率 ≈0.2(≥10 倍反差才可感);
blur 联动速率开关——快段包 CameraMotionBlur、慢窗裸渲, "快糊-慢清"反差是手法成立的一半
- 定格款:定格段斜率 =0(瞬时切换,无缓入——定格要干脆);
定格期强调色马克笔 SVG 椭圆 8f 描边(stroke-dashoffset)圈住目标 + 箭头 6f 点题,feTurbulence(scale≈7)给手绘抖动;解冻段斜率 >1 补偿时长,圈注 8f 淡出
- 目标卡用高清纹理并对位屏中(凝视/圈注对象必须锐利、居中)
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 慢窗/定格时长 | ≥40f / ≥45f | 短了凝视不成立(R3 宁慢勿快) | | 快慢斜率反差 | ≥10 倍 | 反差不足读不出"慢下来了" | | 圈注描边 | 8f 划完 + 抖动 scale 7 | 太慢像加载动画;无抖动像机械标注 |
已知坑
- 变速段 SFX 钉帧跟输出帧不跟源帧(画面动作在输出时间轴上发生);
拟音槽位:定格款配马克笔沙沙声(S4),本卡转正时未配、实战补
- 定格标注一支 30s 片 ≤2 次(同 D 式克制原则)
- 参数经占位素材调校转正,非实战定稿,首次实战后回验
参考实现
demos/rhythm/speed-ramp-freeze/ (FreezeAnnotateReal.tsx / SpeedRampReal.tsx)
Mã nguồn tham chiếu (Remotion)
- demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx
- demos/rhythm/speed-ramp-freeze/SpeedRampReal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/speed-ramp-freeze/SpeedRampReal.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/speed-ramp-freeze.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx, demos/rhythm/speed-ramp-freeze/SpeedRampReal.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("speed-ramp-freeze")Prompt
Use the video-shotcraft shot recipe "Đổi tốc độ và đóng băng chú thích" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/speed-ramp-freeze.json Raw TSX: https://gimgs.net/motion/speed-ramp-freeze/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/rhythm/speed-ramp-freeze/FreezeAnnotateReal.tsx, demos/rhythm/speed-ramp-freeze/SpeedRampReal.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 4.54s 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/speed-ramp-freeze/FreezeAnnotateReal.tsx =====
// freeze-annotate 定格标注(轮 G)——真实卡片流运动中定格,
// 马克笔琥珀圈注(feTurbulence 手绘抖动)圈住目标卡 + 箭头点题,解冻继续。
// remap:0–45 流动 → 45–100 定格(斜率0)→ 100–135 解冻(1.4x 补偿)。
import { useId } from 'react';
import { AbsoluteFill, Img, interpolate, staticFile, useCurrentFrame } from 'remotion';
import layout from '../../_textures/live-layout.json';
export const FREEZEANNOTATE_DUR = 135;
const CARD_W = 460;
const GAP = 60;
const RAIL = layout.projects.cards.slice(0, 9);
const TARGET_I = 5;
const AMBER = '#b45309';
export const FreezeAnnotateReal: React.FC = () => {
const frame = useCurrentFrame();
// 滤镜 ID 按实例生成,多实例同场不串引(useId 的 «:» 在 url() 里非法,需清洗)
const roughId = `rough-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
const src = interpolate(frame, [0, 45, 100, 135], [0, 45, 45, 94], {
extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
});
const dx = src * 22;
const draw = interpolate(frame, [52, 60], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });
const arrowDraw = interpolate(frame, [60, 66], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });
const fade = interpolate(frame, [96, 104], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });
// 导轨起点 -880:定格时(dx=990)目标卡 k=5 中心 = -880+2600-990+230 = 960 屏中
const X0 = -880;
const targetX = X0 + TARGET_I * (CARD_W + GAP) - dx + CARD_W / 2;
const C = 1750; // 椭圆周长近似
return (
<AbsoluteFill style={{ backgroundColor: '#f9f6f1', overflow: 'hidden' }}>
<div style={{ position: 'absolute', top: 370, transform: `translateX(${-dx + X0}px)` }}>
{Array.from({ length: 14 }).map((_, k) => {
const c = RAIL[k % RAIL.length];
const isTarget = k === TARGET_I;
return (
<Img
key={k}
src={staticFile(`textures/live/${isTarget ? 'card4-hires.png' : c.file}`)}
style={{
position: 'absolute', left: k * (CARD_W + GAP), top: 0,
width: CARD_W, borderRadius: 12,
boxShadow: '0 4px 16px rgba(31,28,23,0.10)',
}}
/>
);
})}
</div>
<svg width={1920} height={1080} style={{ position: 'absolute', inset: 0, pointerEvents: 'none', opacity: fade }}>
<defs>
<filter id={roughId}>
<feTurbulence type="fractalNoise" baseFrequency="0.02" numOctaves="2" seed="7" result="n" />
<feDisplacementMap in="SourceGraphic" in2="n" scale="7" />
</filter>
</defs>
<ellipse
cx={targetX} cy={560} rx={290} ry={230}
fill="none" stroke={AMBER} strokeWidth={8} strokeLinecap="round"
strokeDasharray={C} strokeDashoffset={C * (1 - draw)}
transform={`rotate(-6 ${targetX} 560)`}
filter={`url(#${roughId})`}
/>
<path
d={`M ${targetX + 330} 190 Q ${targetX + 220} 240 ${targetX + 140} 320`}
fill="none" stroke={AMBER} strokeWidth={8} strokeLinecap="round"
strokeDasharray={420} strokeDashoffset={420 * (1 - arrowDraw)}
filter={`url(#${roughId})`}
/>
</svg>
</AbsoluteFill>
);
};
// ===== demos/rhythm/speed-ramp-freeze/SpeedRampReal.tsx =====
// speed-ramp 变速(轮 C)——真实卡片流帧号 remap:快(斜率2.2) →
// 0.2x 慢速展示窗 → 快。慢速窗中目标卡(card4-hires)清晰滑过屏中。
// blur 联动速率:快段包 blur、慢段不包,反差即"凝视感"。
import { AbsoluteFill, Img, interpolate, staticFile, useCurrentFrame } from 'remotion';
import { CameraMotionBlur } from '@remotion/motion-blur';
import layout from '../../_textures/live-layout.json';
export const SPEEDRAMP_DUR = 135;
const CARD_W = 460;
const GAP = 60;
const RAIL = layout.projects.cards.slice(0, 9);
const TARGET_I = 5;
const Scene: React.FC = () => {
const frame = useCurrentFrame();
// remap:0–40f 走 88 源帧(快),40–85f 走 9 源帧(0.2x),85–135f 走 110(快)
const src = interpolate(frame, [0, 40, 85, 135], [0, 88, 97, 207], {
extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
});
const dx = src * 28;
return (
<AbsoluteFill style={{ backgroundColor: '#f9f6f1', overflow: 'hidden' }}>
{/* 偏移 717:让慢速窗中点(src≈92.4,dx≈2587)时目标卡 k=5 中心落屏中 */}
<div style={{ position: 'absolute', top: 360, transform: `translateX(${-dx + 717}px)` }}>
{Array.from({ length: 16 }).map((_, k) => {
const c = RAIL[k % RAIL.length];
const isTarget = k === TARGET_I;
return (
<Img
key={k}
src={staticFile(`textures/live/${isTarget ? 'card4-hires.png' : c.file}`)}
style={{
position: 'absolute', left: k * (CARD_W + GAP), top: isTarget ? -16 : 0,
width: CARD_W, borderRadius: 12,
boxShadow: isTarget
? '0 10px 32px rgba(31,28,23,0.18)'
: '0 4px 16px rgba(31,28,23,0.10)',
}}
/>
);
})}
</div>
</AbsoluteFill>
);
};
export const SpeedRampReal: React.FC = () => {
const frame = useCurrentFrame();
const fast = frame < 42 || frame > 83;
return fast ? (
<CameraMotionBlur shutterAngle={200} samples={20}>
<Scene />
</CameraMotionBlur>
) : (
<Scene />
);
};
- 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/speed-ramp-freeze/FreezeAnnotateReal.tsx, demos/rhythm/speed-ramp-freeze/SpeedRampReal.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.

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.

Chuyển nhịp kiểu sakuga
Từ một-đập-ba sang một-đập-một — yếu tố di chuyển giật cục mỗi 3 khung hình một bước, rồi bật sang mượt từng khung hình ở cao trào.