Motion · Chữ & title card
Thẻ tiêu đề giấy
Một câu in ép từng chữ lên giấy, một từ được đánh dấu nghiêng màu nhấn, khép lại bằng một gạch ngắn.
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
Thẻ tiêu đề chuyển chương/tuyên bố giá trị; thẻ dẫn dắt trước khi một tính năng quan trọng xuất hiện; nhịp thở của cả phim
Ý đồ
Cho người xem một câu để thở giữa hai đoạn hình ảnh sản phẩm: nói rõ "tiếp theo xem gì, nó đáng giá gì". Chất liệu in ép letterpress khiến thẻ tiêu đề và hình ảnh sản phẩm phong cách giấy/mực thuộc cùng một thế giới.
Thời lượng & năng lượng
- Thời lượng: 1.7–1.8s (50–55f)
- Năng lượng: Thấp (nhịp thở, ngăn cách hai cảnh 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)
意图
在两段产品画面之间给观众一句话的喘息:说清"接下来看什么、它值什么"。letterpress 压印质感让字卡与纸墨风格的产品画面同属一个世界。
动效核心
- 单句文案逐词入场:scale 从大压到 1 + blur→0 + opacity(letterpress 压印配方)
- 每句恰好一个重点词:italic + 强调色(模板片为琥珀)
- 强调色短下划线 scaleX 0→1 收束
- 尾部整卡淡出交棒;可挂 mono 小字副行 + DigitRoll 数字
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 逐词入场 | 第 i 词 delay = 4 + i·4、9f,bezier(0.2,0.75,0.3,1);scale 1.28→1 + blur 7px→0 + opacity | scale 起点 1.28 是"压印"读感的甜点——对照暗场版 TitleCard 用 40px 上移+blur 10,同配方明暗两版 | | 重点词 | italic + 强调色 oklch(52% 0.115 65),一句恰好一个 accent | 两个 accent 等于没有 accent;重点词选功能名/收益词(C2) | | 下划线 | 16→34f scaleX 0→1,220×6px 强调色 | 下划线是收束信号,出现即预告本卡快结束 | | 时长 | 全片 4 张字卡均 50–55f(≈1.8s) | 字卡时长定式化——观众读一句话的时间,长了拖节奏短了读不完 | | 版式 | 字号 116/serif;纸底 oklch(97.5% 0.008 82) + 中心暖光 radial;尾部 8f 淡出 | 纸底+暖光与产品画面同色系,字卡才不像广告插播 | | 副行 | mono 小字 + DigitRoll(如 "5 of 31 fetched today");digit-roll 参数见 list-stack-press 卡计数器行 | 副行给具体数字最有说服力;数字滚动必须在本卡淡出前落定(教训)——排节拍从本卡淡出帧倒推 |
声音
字卡出场统一钉 swoosh-quick(模板片四张字卡钉在 f220/565/725/885 同音)——同类元素同音是词汇表纪律(S2);字卡本身无落地重音,能量留给前后镜头。
已知坑
- 文案要具体:带产品功能名+具体收益,抽象隐喻词被用户逐字改掉(C2——"one board"→"one place to go"、加 "Paper Radar");文案在画面锁定后按最终镜头重写一遍(C1)
- 重要功能出场前要有引导字卡(C2 判例:文档镜头前应加一句引导,如 "Every project, linked to your weekly report")——字卡不是装饰,是章节路标
参考实现
demos/typography/paper-title-card/PaperTitleCard.tsx(原 template/src/aifl/PaperTitleCard.tsx,参数化:words/sub/subDigits 可换文案)
Mã nguồn tham chiếu (Remotion)
- demos/typography/paper-title-card/PaperTitleCard.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/typography/paper-title-card/PaperTitleCard.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/paper-title-card.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/typography/paper-title-card/PaperTitleCard.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("paper-title-card")Prompt
Use the video-shotcraft shot recipe "Thẻ tiêu đề giấy" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/paper-title-card.json Raw TSX: https://gimgs.net/motion/paper-title-card/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/typography/paper-title-card/PaperTitleCard.tsx; imports demos/_fixtures and assets/lib) Spec: 960x540, 30 fps, 1.83s 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/typography/paper-title-card/PaperTitleCard.tsx =====
// paper-title-card —— 一句话逐词 letterpress 压印上纸字卡
// 章节转场/价值主张呼吸位:单句文案逐词压印入场(scale 大→1 + blur→0),
// 每句恰一个强调色斜体重点词,强调色下划线 scaleX 收束,尾部整卡淡出。
// 全片多张字卡统一 50–55f;纸底+中心暖光与纸墨风格产品画面同色系。
// 参数化:words/sub/subDigits 可换成目标产品文案;subDigits 走 DigitRoll
// 数字滚动(必须在本卡淡出前落定)。渲染本 demo 前把 text 复制进项目即可。
import React from 'react';
import { AbsoluteFill, interpolate, useCurrentFrame, Easing } from 'remotion';
export const PAPER_TITLE_CARD_DURATION = 55; // ≈1.8s @30fps
const SERIF = 'ui-serif, Georgia, "Times New Roman", serif';
const MONO = 'ui-monospace, SFMono-Regular, Menlo, monospace';
// 每句恰好一个 accent:功能名/收益词(C2)
const WORDS: { text: string; accent?: boolean }[] = [
{ text: 'All' },
{ text: 'your' },
{ text: 'team’s' },
{ text: 'research,' },
{ text: 'one', accent: true },
{ text: 'place' },
{ text: 'to' },
{ text: 'go.' },
];
const SUB = 'of 31 fetched today';
const SUB_DIGITS = '5';
// 数字滚动列(odometer 风格,复制自 template DigitRoll,数字带双份拼接保证
// 任何目标位都有滚感;tabular-nums 防横向抖)
const DIGITS = '0123456789';
const DigitColumn: React.FC<{ ch: string; delay: number; lineH: number; color: string }> = ({ ch, delay, lineH, color }) => {
const frame = useCurrentFrame();
if (ch < '0' || ch > '9') {
return <span style={{ fontSize: 26, lineHeight: `${lineH}px`, color }}>{ch}</span>;
}
const target = DIGITS.indexOf(ch);
const t = interpolate(frame, [delay, delay + 22], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.bezier(0.25, 0.8, 0.25, 1),
});
const offset = (10 + target) * t * lineH;
return (
<span style={{ display: 'inline-block', height: lineH }}>
<span style={{ display: 'block', transform: `translateY(${-offset}px)` }}>
{(DIGITS + DIGITS).split('').map((d, j) => (
<span
key={j}
style={{
display: 'block',
fontSize: 26,
lineHeight: `${lineH}px`,
color,
fontVariantNumeric: 'tabular-nums',
}}
>
{d}
</span>
))}
</span>
</span>
);
};
const DigitRoll: React.FC<{ value: string; delay: number; color: string }> = ({ value, delay, color }) => {
const lineH = 26 * 1.15;
return (
<span style={{ display: 'inline-flex', overflow: 'hidden', height: lineH, verticalAlign: 'bottom' }}>
{value.split('').map((c, i) => (
<DigitColumn key={i} ch={c} delay={delay + i * 4} lineH={lineH} color={color} />
))}
</span>
);
};
export const PaperTitleCard: React.FC = () => {
const frame = useCurrentFrame();
const duration = PAPER_TITLE_CARD_DURATION;
const fadeOut = interpolate(frame, [duration - 8, duration], [1, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const underline = interpolate(frame, [16, 34], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.bezier(0.3, 0, 0.2, 1),
});
const subT = interpolate(frame, [10, 22], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
return (
<AbsoluteFill
style={{
backgroundColor: 'oklch(97.5% 0.008 82)',
justifyContent: 'center',
alignItems: 'center',
opacity: fadeOut,
backgroundImage:
'radial-gradient(1100px 750px at 50% 42%, oklch(99.3% 0.014 88 / 0.85), transparent 65%)',
}}
>
<div style={{ textAlign: 'center', maxWidth: 1500 }}>
<div
style={{
fontFamily: SERIF,
fontSize: 116,
fontWeight: 600,
lineHeight: 1.14,
color: 'oklch(18% 0.006 82)',
letterSpacing: '-0.012em',
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'center',
columnGap: '0.26em',
}}
>
{WORDS.map((w, i) => {
const delay = 4 + i * 4;
const t = interpolate(frame, [delay, delay + 9], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.bezier(0.2, 0.75, 0.3, 1),
});
return (
<span
key={i}
style={{
opacity: t,
transform: `scale(${1.28 - 0.28 * t})`,
filter: `blur(${(1 - t) * 7}px)`,
display: 'inline-block',
fontStyle: w.accent ? 'italic' : 'normal',
color: w.accent ? 'oklch(52% 0.115 65)' : undefined,
}}
>
{w.text}
</span>
);
})}
</div>
<div
style={{
height: 6,
width: 220,
margin: '38px auto 0',
borderRadius: 3,
background: 'oklch(52% 0.115 65)',
transform: `scaleX(${underline})`,
}}
/>
<div
style={{
fontFamily: MONO,
fontSize: 26,
letterSpacing: '0.12em',
color: 'oklch(50% 0.006 82)',
marginTop: 34,
opacity: subT,
textTransform: 'uppercase',
display: 'flex',
justifyContent: 'center',
alignItems: 'baseline',
gap: '0.5em',
}}
>
<DigitRoll value={SUB_DIGITS} delay={12} color="oklch(52% 0.115 65)" />
<span>{SUB}</span>
</div>
</div>
</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/typography/paper-title-card/PaperTitleCard.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

Ba từ tiếp sức bằng hình học
Ba từ lợi ích mỗi từ mang riêng một bộ hình học tiếp sức — vòng tròn nét đứt tự xoay thu nhỏ → ba vòng tròn nét liền mọc lên lần lượt → ánh kim sheen quét qua rồi thu về trắng thuần; mỗi từ mới viền nét rồi tô đầy.

Tiếp sức từ với cuộn phim trang
Cột trái các thẻ trang đen trắng cuộn theo bước, bên phải từ lớn chữ serif tiếp sức tại chỗ (danh từ cố định + động từ luân phiên) — chỉ khi đổi từ mới cuộn một bước, khớp chính xác điểm giữa thẻ trang hiện tại.

Cuộn từ dọc theo nhịp
Từ cuối câu đổi trên cuộn dọc, 3 lần đổi từ mỗi lần 0.55s, nhanh trước chậm sau kèm hơi overshoot; các dòng lân cận nhòe dọc và xám dần theo khoảng cách, từ ở giữa vừa đậu liền nhuộm từ xám sang màu nhấn.

Hai cách tiết lộ code song song
Cùng một khối code tô cú pháp được tiết lộ hai cách song song — trái: stagger cấp dòng mờ vào nổi lên 8px, phải: gõ từng ký tự nhưng giữ nguyên màu token gốc, con trỏ vuông #3a4468 đệm dưới ký tự hiện tại.