# Lead word zoom assemble

The lead word fills the frame at 2.3x, still pushing in during the hold, then shrinks to final size while the line slides left into place as the remaining words are pushed in from their slots.

- Category: typography · Tags: shotcraft, remotion, typography, opening
- Spec: 1920×1080 @ 30 fps · 2.8s (16:9)
- License: Apache-2.0 · Tier: free
- JSON: https://gimgs.net/motion/lead-word-zoom-assemble.json
- Kind: remotion
- Source (TSX): https://gimgs.net/motion/lead-word-zoom-assemble/template.html
- Repo: https://github.com/Vincentwei1021/video-shotcraft
- Credits: video-shotcraft (Vincent Wei) — https://github.com/Vincentwei1021/video-shotcraft
- Page: https://gimgs.net/motion/lead-word-zoom-assemble.html

## Params

_No params._

Default params (base64url for `?p=`): `e30`

## Prompt

## When to use

An "Introducing" title card for a brand/product name; the second shot of a launch-event-style opener; any one-liner that needs "one word fills the frame first, then the full sentence completes around it".

## Intention

The most common mistake in a title card is fading the whole sentence in at once — the audience receives 5 words simultaneously and remembers none of them. This card turns reading order into **physical order**: give just one word first, and make it big enough to fill the frame and still pushing toward the viewer, forcing it to be read first; then that word shrinks back to its own size and position within the sentence, and the full sentence grows around it. By the time it settles, the audience has already read the lead word twice. The real crux isn't the scale curve, it's the **pivot point**. The lead word travels from "dead center of the frame" to its position within the line — if the pivot is the line box's center, the scaling and the leftward slide each drag the characters in different directions, and the baseline visibly jitters in the final frames. Pinning `transform-origin` horizontally to the lead word's center and vertically to the baseline keeps both points still throughout, so the whole line simply "shrinks" with no drift at all.

## Duration & energy

- Duration: About 2.8s (84f@30fps; the sentence assembles by f32, the shift-up and subhead run f34–50, a 22f hold, a final 12f crash)
- Energy: Medium-high (starts with a full-frame big word, but there's only one motion throughout, no second impact)

## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)

## 意图
字卡最容易犯的错是"整句一起淡入"——观众同时收到 5 个词，一个都没记住。这张卡把
阅读顺序做成**物理顺序**：先只给一个词，而且大到占满画面、还在继续朝观众推近，观众
被迫先读它；然后这个词退回自己在句子里的尺寸和位置，整句围绕它长出来。落定时观众
已经读过两遍主词。

真正的手感命门不是缩放曲线，而是**支点**。首词从"画面正中"走到"行内第三个字符的
位置"，如果支点取行盒中心，缩放和左滑会各自把字往两边拽，末尾几帧基线抖得肉眼可见。
把 `transform-origin` 横向钉在首词中心、纵向钉在基线，这两点全程不动，整行就只是
"收小"，没有任何漂移。

## 动效核心
- **两段缩放叠加成一条 scale**：
  `scale = interpolate(f, [0,12], [2.3, 2.3·1.06], PUSH_EASE)
         + interpolate(f, [12,24], [0, 1-2.3·1.06], ZOOM_EASE)`。
  第一段是 hold 期间的推近（`cubic-bezier(.25,1,.5,1)`，减速后挂在峰值），第二段是缩回
  （从峰值一路减到终字号 1）。写成相加而不是分段判断，两段之间没有速度断点
- **缩回与左滑共用 ZOOM_EASE**（`cubic-bezier(.5,0,.05,1)`，重 ease-in-out、零回弹）：
  `translate = interpolate(f, [12,36], [slideDistance, 0], ZOOM_EASE)`。两个动作必须读作
  **一次**运动；各走各的曲线时会读成"先缩小、再滑走"两拍
- **偏心距怎么来**：`slideDistance = lineWidth · (0.5 - leadRatio)`，`leadRatio` =
  首词中心在整行里的占比。首词要停在画面正中，整行就得先向右偏这么多
- **支点实测**：挂载时 `delayRender` 挂起渲染，量三个值——整行 `offsetWidth`、
  首词 `offsetLeft + offsetWidth/2`、基线。基线用一个**零尺寸 inline-block 尺子**读出
  （`<span style={{display:'inline-block',width:0,height:0}}>` 的 `offsetTop` 就是基线），
  比 `lineHeight × 0.8` 之类的估算准得多。量到之后 `continueRender`，`transform-origin`
  写成 `${leadRatio*100}% ${baseline}px`
- **后续词是被推进来的，不是淡进来的**：第 i 词（i≥1）在
  `pushStart = 12 + 6 + (i-1)·4` 起，`translate` 从 `0.5em → 0`
  （`cubic-bezier(.22,.8,.36,1)`，起步快、落地长），透明度只用 2f 淡完。淡入必须极短，
  否则词会"浮现"而不是"被塞进槽位"
- **词间空格必须放在 inline-block 之外**。跟在词 span 内部尾随时会被行盒裁掉，
  落定后所有词黏成一团（上游原组件的 bug，这里已修）
- **场景层的三拍收尾**：f34–50 整行 `translateY 0→−56px`（outCubic），副行在**同一时窗**
  淡入 + 上移 16px——上移让出的空间当帧被副行填掉，没有空当；f50–72 完全静止 22f；
  f72–83 整幕 `scale 1→1.2` + `blur 0→9px` + `opacity 1→0.45`（ease-in-quad），推近失焦
  交棒下一镜
- **强调词**：精确匹配某个词换强调色（demo 里是 `Lumen`）。品牌词推入落位那一下自带
  高亮，不用再加第二个动作

## 参数表
| 参数 | 典型值 | 调节手感 |
|------|--------|----------|
| 首词起手倍数 | initialScale 2.3 | 2.3 让首词横向溢出画面一点点（读作"太大了装不下"）；<1.8 只是"稍大的字"，冲击消失 |
| 推近 | pushScale 1.06，占满 hold 12f | 6% 是"还在朝观众来"的最小可感量；>1.15 会在缩回时读作反向弹跳 |
| hold | 12f | 首词独占画面的时长；<8f 观众读不完一个长单词，>20f 整句节奏塌 |
| 缩回 | 12f，ZOOM_EASE | 与左滑同曲线；单独给缩回一条更快的曲线会让整行"先缩完再滑"，断成两拍 |
| 左滑 | assembleDuration 24f | 比缩回长一倍：字号已经落定、位置还在走，这段"滑行尾巴"是电影感的来源 |
| 后续词延迟 | wordDelay 6f（缩回起点之后） | 首词开始缩回才放后续词；同帧放会挤在一起看不清谁在动 |
| 词间错峰 | wordStagger 4f | 4f 读作"依次被推进"；0 则整段后缀一起进，回到"整句淡入" |
| 单词推入 | wordDuration 12f，wordPush 0.5em | 0.5em 是"刚从槽位外面"；>1em 会读作从画外飞入，与首词的收束方向打架 |
| 词淡入 | wordFade 2f | 刻意极短。>6f 词会读作"浮现"，推入的力学感消失 |
| 字号/字重/字距 | 96px / 600 / −0.03em | 负字距在放大 2.3 倍时才不会散架；正字距的大字会读作"标语横幅" |
| 上移 + 副行 | f34–50，−56px，副行同窗 | 两件事必须同窗：先上移再出副行会多出一个空当拍 |
| 静止 | 22f | 落定后的可读时间，低于 15f 观众来不及读副行 |
| crash | 末 12f，scale→1.2，blur→9px | 这是转场的**前半**，动机要和下一镜的第一帧对上；单独播时看起来像"没结束"是正常的 |

## 已知坑
- **支点错了就抖基线**。`transform-origin` 必须是「首词中心 % + 基线 px」这一对。
  取行盒中心（默认 `50% 50%`）时缩放与左滑互相拉扯，末尾几帧基线肉眼可见地跳
- 基线别估算。`lineHeight × 0.8` 这类经验值在不同字体上差几像素，放大 2.3 倍后就是
  十几像素的抖动。零尺寸 inline-block 尺子的 `offsetTop` 是唯一可靠的读法
- `delayRender` 期间整行 `opacity: 0`——**必须**如此。否则未量到支点的第一帧会闪一下
  错位的大字，成片里就是一帧脏帧
- 布局是挂载时实测的，所以**结果随渲染环境的字体而变**：跨平台字体回退不同时整行宽度
  会整体漂移（首词占比也跟着变）。介意的话把量到的三个值写死成常量
- `scale` / `translate` 用的是独立 CSS 属性（不是 `transform` 简写）。混用会让其中一个
  被覆盖；要合并成 `transform` 就必须自己保证 `translate → scale` 的顺序
- 词间空格放进词 span 内部会被行盒裁掉（见上）。这个 bug 在小字号下不明显，96px 时
  所有词会明显黏连
- 首词必须是**短词**。"Introducing" 这类 10 字符左右刚好；首词超过 14 字符时 2.3 倍
  放大会溢出画面两侧，观众读不到词尾
- 强调色只给一个词。两个词都上色时观众不知道该记哪个，而且推入落位的高亮变成了
  "彩色标题"，动作的意义被抹掉
- 末段 crash 是转场的前半式，不是收尾。单独当片尾用会显得"断了"——要收尾就把
  crash 换成静止 hold 或淡出

## 参考实现
demos/typography/lead-word-zoom-assemble/
（LeadWordZoomAssemble.tsx）

## Reference implementation (Remotion)

- demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx

The component imports shared fixtures from `demos/_fixtures` and helpers from `assets/lib` in the repo; SKILL.md there documents the render workflow.


## Flow

1. **Read the recipe**
   Prompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
2. **Get the code** — tool: git
   `git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install`
3. **Register + render** — tool: remotion
   `npx remotion render <CompositionId> out.mp4   # component: demos/typography/lead-word-zoom-assemble/LeadWordZoomAssemble.tsx`
4. **Or re-implement**
   Port the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.

## Render

Clone https://github.com/Vincentwei1021/video-shotcraft (npm install). The component lives under demos/{category}/{name}/ and imports shared fixtures from demos/_fixtures and helpers from assets/lib. Copy it (with those imports) into a Remotion project, register a <Composition> with the template's width/height/fps/duration in Root.tsx, then `npx remotion render <CompositionId> out.mp4`. SKILL.md in the repo documents the full Claude Code / Codex workflow. The prompt below is the shot recipe (timing, easing, parameter table, pitfalls) — enough to re-implement the shot in HTML/WAAPI or another engine if you prefer.

## Contract

React/Remotion composition (TSX) from the open-source video-shotcraft library (Apache-2.0). It is not a browser-playable template: clone the repo, register the component in a Remotion project and render with `npx remotion render`. The recipe (prompt) describes timing, easing and parameters so you can also re-implement the shot in any engine.
