Motion · Chữ & title card
Chữ rỗng lấp đầy
Chữ viền rỗng thu nhanh từ 3.2 lần về vị trí, vòng tròn nét đứt và đường dẫn khép lại, rồi viền lấp đặc trắng tức thì.
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
Cú búa gõ một tiếng cho lợi ích/khẩu hiệu một từ; cảnh "đóng đinh" tại điểm nhịp; khung hình nhấn mạnh trong phim thương hiệu nền tối
Ý đồ
Đóng đinh một từ không phải bằng cách mờ dần chậm rãi, mà bằng hai lần an vị: trước tiên chữ rỗng khổng lồ thu nhanh về đúng vị trí (an vị về kích thước), sau đó viền chuyển thành trắng đặc trong một khung hình (an vị về chất liệu). 1.5s ở giữa, khi vòng tròn nét đứt thu lại và các đường ngang nét đứt kéo vào, là quá trình "ngắm" — ngôn ngữ bản vẽ kỹ thuật khiến khoảnh khắc sáng lên đọc thành "xác nhận". Việc lấp đầy phải tức thời — quét lấp chậm sẽ biến cây đinh thành một hoạt ảnh làm đẹp chậm rãi.
Thời lượng & năng lượng
- Thời lượng: Khoảng 2.5s (75f@30fps, một nhịp ngắn gọn)
- Năng lượng: Cao (thu 3.2 lần + sáng tức thì, va chạm mạnh trong thời gian ngắ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)
意图
一个词要打成钉子,靠的不是慢慢淡入而是两次落定:先是巨大空心字 急速收缩到位(这是尺寸的落定),再是描边在一帧内变实心白(这是质感的 落定)。中间那 1.5s 的虚线圆收缩与水平虚线内伸是"瞄准"过程——技术 制图语汇,让点亮那一下读作"确认"。填充必须是瞬时的:慢扫填充会把 钉子变成缓慢的美化动画。
动效核心
- 空心字急缓收缩:
zoom = seg(t, 0.05, 0.19, E.outCubic)(约 4f→14f,
仅 10f),scale(lerp(zoom, 3.2, 1));同期 born = seg(t, 0.05, 0.14) 淡入——收缩窗只 10f,快到几乎是一次冲击
- 双层字:
outline(color:transparent+-webkit-text-stroke:1px #565b63)
与 solid(纯白,初始 opacity 0)叠放,48px / 500 字重 (中等字重,不是标题的 800——空心字太粗会糊成块)
- 虚线大圆(SVG r=88、
dasharray:6 8):出现cin = seg(0.16, 0.3),
收缩 shrink = seg(0.16, 0.76, E.outCubic) 从 2.8 倍收到 1—— 收缩窗 45f,横跨大半片,是全片最慢的一条曲线;同时 rotate(t·18) 全程缓慢自转
- 左右水平虚线(
dasharray:5 7):ext = seg(0.5, 0.72, E.outCubic),x2 从
±240 向内推进 144(画框边缘 → 圆边),透明度同步等于 ext;随后 瞬时填充 pop = seg(t, 0.742, 0.762)——窗仅 0.02(约 0.6 帧), solid 与 outline 透明度硬交叉,视觉上是硬切
- 一闪即逝的辉光:
flash = pop · (1 - seg(0.762, 0.86, E.outQuad)),
0 0 flash·16px rgba(255,255,255,.45),约 7f 内衰完后定格纯白
- 描边预热:
bright = seg(0.66, 0.73, E.outQuad),灰度 86→145 —— 点亮前
先亮一档,给那 0.6 帧的硬切一个"充能"前奏;底层 14 颗静态微尘 (不动,随机位置 + 0.06–0.2 透明度)
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 收缩 | scale 3.2 → 1,窗 10f,outCubic | 3.2 倍是"从脸前退开"的量;<2 倍读作普通缩放;窗 >20f 就成了缓慢 zoom,冲击全失 | | 字重字号 | 48px / 500 / letter-spacing:.5px,占画宽约三成 | 空心字上 700+ 会让 1px 描边包住一大块空白,读作气球字;500 是空心字的甜点 | | 描边 | 1px #565b63,点亮前预热到灰度 145 | 1px 细描边是"制图"语汇;2px+ 变涂鸦;预热那一档删了硬切显得没由来 | | 虚线圆 | r=88、dash 6 8、scale 2.8→1(45f)、自转 t·18° | 45f 慢收缩是全片的"呼吸",与 10f 的字收缩形成快慢对比;窗压到 20f 两者撞在一起 | | 水平虚线 | dash 5 7,从 ±240 内伸 144,窗 0.5→0.72 | 内伸终点要停在圆边(不进圆内);伸满全宽读作十字准星,过头了 | | 瞬时填充 | seg(0.742, 0.762)(≈0.6f) | 这一下是全卡的目的:任何形式的慢扫(>3f)都会把钉子降级成美化动画 | | 辉光 | 峰值 16px,rgba(255,255,255,.45),7f 衰完 | 一闪即逝才像"点亮";持续辉光读作发光字体,质感立刻廉价 | | 微尘 | 14 颗,静态不动 | 静态是对的(本卡只 2.5s,动尘会抢戏);数量 >25 底噪太脏 |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验
- 只适合单个短词(demo
Faster,6 字母):字号与虚线圆半径是写死的
一组坐标,字一长就顶穿圆、水平虚线内伸终点也会压到字上——换成 两个词或中文四字须同步调 font-size / r / 内伸距离三处
-webkit-text-stroke是前缀属性,Chromium/WebKit 才生效;Firefox 下
outline 层是全透明(词直到 0.742 才突然出现)。Remotion 走 Chromium 无碍
- 瞬时填充窗
0.02在 75f 片长下不足一帧:这是故意的(要的就是硬切),
但如果把本卡拉长到 150f+,这 0.02 会变成 3 帧的可见渐变——拉长时长 必须把窗按比例压回去
- 时间窗注释里的秒数(4.83→5.15s 等)是原片时间轴的残留,与本卡
0–2.5s 的 t 无关,读代码时别被误导
- 深底专用:
#050505底 + 白描边 + 辉光是效果的全部;浅底上空心字
几乎不可见,需整套重配
- 声音强依赖:点亮那一帧必须有一记短促重音(金属/开关/低频撞击),
这是本卡"钉子"感的一半,配法纪律见 sound-design.md;与 word-relay-geometry 分工:那卡是三词接力(本卡是它第一段的独立化 + 升级版),同片二者不并置
参考实现
demos/typography/outline-word-fill/ (OutlineWordFill.tsx)
Mã nguồn tham chiếu (Remotion)
- demos/typography/outline-word-fill/OutlineWordFill.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/typography/outline-word-fill/OutlineWordFill.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/outline-word-fill.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/typography/outline-word-fill/OutlineWordFill.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("outline-word-fill")Prompt
Use the video-shotcraft shot recipe "Chữ rỗng lấp đầ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/outline-word-fill.json Raw TSX: https://gimgs.net/motion/outline-word-fill/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/typography/outline-word-fill/OutlineWordFill.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 3s 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/outline-word-fill/OutlineWordFill.tsx =====
// outline-word-fill — Outline→Solid Fill 空心字辉光填充(motion-lab 定稿转原生 Remotion)
// 空心 "Faster"(细灰描边、中等字重)从约 3.2 倍巨大尺寸急缓收缩落位居中;
// 背后巨大虚线圆随后从画外持续收缩到字周围并缓慢自转,两侧水平虚线从画框边缘向内伸向圆;
// 描边先微微增亮,随即实心白色一帧内瞬间点亮(无慢扫),带一闪即逝的微辉光后定格纯白。
// 设计坐标 480×270(DesignStage 等比放大),参数表数值以此坐标系标定。
import React from 'react';
import { DesignStage, E, lerp, rand, seg, useT } from '../../_fixtures/Motion';
export const OUTLINE_WORD_FILL_DURATION = 75; // 2500ms @30fps
// 背景微尘:位置/尺寸/透明度全部种子驱动,跨帧静止
const DUST = Array.from({ length: 14 }, (_, i) => ({
size: 1 + rand(i * 7 + 2) * 1.2,
opacity: 0.06 + rand(i + 55) * 0.14,
left: `${rand(i + 13) * 100}%`,
top: `${rand(i + 29) * 100}%`,
}));
export const OutlineWordFill: React.FC = () => {
const t = useT();
// 空心字从 ~3.2x 急缓收缩落位(原片 4.83→5.15s)
const born = seg(t, 0.05, 0.14, E.outCubic);
const zoom = seg(t, 0.05, 0.19, E.outCubic);
// 描边临近点亮前微微增亮(6.4→6.55s)
const bright = seg(t, 0.66, 0.73, E.outQuad);
const gv = Math.round(lerp(bright, 86, 145));
// 虚线圆:字落位后出现,从画外一路收缩到字周围(5.1→6.6s)
const cin = seg(t, 0.16, 0.3, E.outQuad);
const shrink = seg(t, 0.16, 0.76, E.outCubic);
// 水平虚线从画框边缘向内伸向圆(5.9→6.5s)
const ext = seg(t, 0.5, 0.72, E.outCubic);
// 实心白一帧内瞬间点亮(原片 6.54→6.56s 硬切,无慢扫)
const pop = seg(t, 0.742, 0.762);
// 一闪即逝的微辉光,随后定格纯白
const flash = pop * (1 - seg(t, 0.762, 0.86, E.outQuad));
// 注:原 effect.js 背景为 #050505,但样片 mp4 经 x264 编码后底色实测为 rgb(3,3,3)
// (暗部量化损失),此处按样片实际电平取 #030303 保证帧一致性。
return (
<DesignStage bg="#030303">
<div style={{ position: 'absolute', inset: 0, background: '#030303', overflow: 'hidden' }}>
{/* 背景微尘 */}
{DUST.map(({ size, opacity, left, top }, i) => (
<div
key={i}
style={{
position: 'absolute',
width: size,
height: size,
borderRadius: '50%',
background: '#fff',
opacity,
left,
top,
}}
/>
))}
{/* 虚线大圆(SVG,从画外收缩落位 + 缓慢自转)+ 左右水平虚线 */}
<svg
viewBox="-240 -135 480 270"
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}
>
<g transform={`scale(${lerp(shrink, 2.8, 1)}) rotate(${t * 18})`}>
<circle
cx={0}
cy={0}
r={88}
fill="none"
stroke="#7d838e"
strokeWidth={1}
strokeDasharray="6 8"
opacity={cin * 0.85}
/>
</g>
<line
y1={0}
y2={0}
x1={-240}
x2={-240 + ext * 144}
stroke="#7d838e"
strokeWidth={1}
strokeDasharray="5 7"
opacity={ext}
/>
<line
y1={0}
y2={0}
x1={240}
x2={240 - ext * 144}
stroke="#7d838e"
strokeWidth={1}
strokeDasharray="5 7"
opacity={ext}
/>
</svg>
{/* 双层文字(中等字重,占画宽约三成):底层描边、顶层实心白 */}
<div
style={{
position: 'absolute',
left: '50%',
top: '50%',
fontFamily: '-apple-system,"Helvetica Neue",sans-serif',
fontSize: 48,
fontWeight: 500,
letterSpacing: 0.5,
lineHeight: 1,
opacity: born,
transform: `translate(-50%,-53%) scale(${lerp(zoom, 3.2, 1)})`,
}}
>
<div
style={{
color: 'transparent',
WebkitTextStroke: `1px rgb(${gv - 4},${gv},${gv + 8})`,
opacity: 1 - pop,
}}
>
Faster
</div>
<div
style={{
position: 'absolute',
inset: 0,
color: '#fff',
opacity: pop,
textShadow:
flash > 0.01 ? `0 0 ${flash * 16}px rgba(255,255,255,${flash * 0.45})` : 'none',
}}
>
Faster
</div>
</div>
</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/typography/outline-word-fill/OutlineWordFill.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.