Motion · Tipografia
Pill chip slot cycle
In "Your `chip` Handled" on white, the word inside a dark pill scrolls like a slot reel while the pill's width smoothly stretches to fit.
Fonte: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Composição Remotion (React/TSX) — vídeo de prévia, sem player no navegador
When to use
Copy patterns like "we've got ___ handled"; a one-line close for a SaaS feature list; a hero-visual beat for light-background brand films
Intention
Word-slot cycling comes in two schools: keep the sentence out of the way of the change (a fixed-width reel), or let the sentence accept the change (the pill stretches and the surrounding text gets pushed apart). This card is the latter — the cost is you have to get the width math right, and the payoff is that the sentence reads as alive: "Your" and "Handled" get pushed along, and the viewer feels the weight of the word inside the pill. The gray ghost items peeking out above and below the pill are a second cue: they signal "this is a scrollable list," priming the viewer to expect the next word.
Duration & energy
- Duration: About 5.0s (150f@30fps: hold → 3 swaps at 0.25/0.47/0.69 → closing hold)
- Energy: Medium (a steady three-beat rhythm, width stretching is the only continuous motion)
Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)
意图
词槽轮换有两派:让句子躲开变化(宽度固定的滚轮),或者让句子接受 变化(胶囊撑宽、两侧文字被挤开)。这卡是后者,代价是必须把宽度算准, 回报是句子读起来活的——"Your" 和 "Handled" 被推着走,观众感到胶囊里 的词有分量。上下露出的灰色幽灵项是第二个记号:它明示"这是个可以滚的 列表",让观众预期还有下一个词。
动效核心
- 宽度预量:setup 期用隐藏
<span>逐词量offsetWidth,各加 74px 内边距
存成 widths[](带 o.w.length·13 兜底,防 setup 时未挂载量不到)
- 位置量
pos由 3 段窗累加:切换点[0.25, 0.47, 0.69],每窗 0.12
(≈18f),缓动 E.inOutCubic——两头都缓,对应"滚轮起步与停稳"
- 内列
colIn竖移-pos·48px(行高 48 = 胶囊高);胶囊宽度取
lerp(frac, widths[ci], widths[ci+1]),帧内插值所以伸缩是连续的, 两侧 flex:none 的文字被 flex 布局自然挤开
- 幽灵项在胶囊外(chipWrap 内)上 -38px / 下 58px,取
near = round(pos)
的前后邻居文本,随滚动微移 (pos-near)·48·0.5(半速视差,比胶囊内 的词慢一半,才像"外圈")
- 幽灵项透明度
0.13 · (0.4 + settle·0.6),settle = 1 - min(1, |pos-near|·3)
——滚动中途更淡、落定时最清楚
- 每行内是 emoji + 词的两段结构(emoji 18px、词 700 22px 白字),
胶囊 #1a1c24 底 + 0 8px 24px rgba(20,22,40,.22) 投影 + overflow:hidden
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 词表 | 4 词 + emoji(Sales/Workflow/Admin/Reports),3 次切换 | 词长差决定挤开幅度的观感:demo 最短 5 字母最长 8 字母;差 >2 倍胶囊跳变太猛,句子重心左右甩 | | 切换点 | [0.25, 0.47, 0.69],窗 0.12(≈18f) | 段间约 0.10(≈15f)静置读词;起手 0.25 前的静置让观众先认出句式 | | 缓动 | E.inOutCubic | 两头缓才像滚轮起停;outCubic(只后缓)读作被甩上来,胶囊宽度也会跟着突变 | | 胶囊 | 高 48px、圆角 99、#1a1c24、宽 = 文本宽 + 74 | +74 是左右内边距 + emoji 位;<50 词贴边,>100 胶囊胖成按钮 | | 幽灵项 | 上 -38 / 下 58px,基础透明度 0.13 | 0.13 是"看得见但不抢"的甜点;>0.25 读作三行并列文字,不是幽灵;删了列表暗示就没了 | | 幽灵视差 | (pos-near)·48·0.5(半速) | 半速是"外圈更远"的立体感来源;同速则三行像一整块滑动,幽灵失去意义 | | 句子字号 | 两侧 30px/800、胶囊内 22px/700 | 胶囊内比句子小是正确层级(徽章依附句子);等大或更大就读作两个标题打架 | | 配色 | 底 #fbfbfd、句子 #15171d、胶囊白字 | 浅底 + 单一深色块是本卡的身份证;深底版会让胶囊消失,需整套重配 |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验
- 宽度靠 setup 期
offsetWidth实测:字体没加载完时量到的是回退字体
的宽度,胶囊会偏窄或偏宽——移植时须确保 webfont 就绪后再量,或改用 离屏 canvas measureText;length·13 那条兜底只保证不崩,不保证准
- 词长差要控制在 2 倍内:宽度自适应是本卡的卖点也是风险,
Sales→Workflow 这一跳已经是可接受上限,再大句子两侧甩得晃眼
- 两侧文字必须
flex:none:不然 flex 会去压缩它们而不是移动它们,
"挤开"变成"字被压扁"
- 幽灵项文本取
round(pos)的邻居,所以在切换的正中间(pos=x.5)会
跳一次文本——被 settle 压到最淡时发生,肉眼基本不可察;但若把 透明度调高到 0.25+,这一跳就会露出来
- emoji 在不同平台字形宽度不一(尤其
⚙️带变体选择符),会让实测宽度
漂移几像素;实战建议换成项目自己的单色图标,别依赖系统 emoji
- 与 vertical-word-roll-blur-cycle 分工:那卡宽度固定、裸词、带滚筒 blur
(词是句子成分),本卡宽度自适应、深色胶囊、带幽灵项(词是徽章)—— 同片只留一个;声音上三次落定是三个节奏点,配轻质开关/胶囊贴合音, 连发交替双样本,配法纪律见 sound-design.md
参考实现
demos/typography/pill-chip-slot-cycle-handled/ (PillChipSlotCycleHandled.tsx)
Reference implementation (Remotion)
- demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx
The component imports shared fixtures from demos/_fixtures and helpers from assets/lib in the repo; SKILL.md there documents the render workflow.
Tudo nesta página também é legível por máquina. Aponte seu agente para o JSON ou adicione o servidor MCP do gimgs e peça para buscar templates de motion.
- Baixe o JSON — contém a receita do plano (timing, easing, tabela de parâmetros, armadilhas) e o código TSX completo.
- Clone o repositório video-shotcraft; o componente importa fixtures de demos/_fixtures e helpers de assets/lib.
- Registre o componente como <Composition> em um projeto Remotion e renderize com `npx remotion render`, ou reimplemente a receita no seu próprio engine.
curl -s https://gimgs.net/motion/pill-chip-slot-cycle-handled.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("pill-chip-slot-cycle-handled")Prompt
Use the video-shotcraft shot recipe "Pill chip slot cycle" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/pill-chip-slot-cycle-handled.json Raw TSX: https://gimgs.net/motion/pill-chip-slot-cycle-handled/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/typography/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 5.5s 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/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.tsx =====
// pill-chip-slot-cycle-handled — Chip Slot Cycle 胶囊滚轮挤开(motion-lab 定稿转原生 Remotion)
// 白底句式 "Your [chip] Handled":深色胶囊内词垂直滚轮轮换(Sales→Workflow→Admin→Reports),
// 上下露出灰色幽灵项,胶囊宽度随词长平滑变化,两侧文字被自然挤开收拢。
// 设计坐标 480×270(DesignStage 等比放大),参数表数值以此坐标系标定。
import React, { useLayoutEffect, useRef, useState } from 'react';
import { DesignStage, E, lerp, seg, useT } from '../../_fixtures/Motion';
export const PILL_CHIP_SLOT_CYCLE_HANDLED_DURATION = 150; // 5000ms @30fps
const WORDS = [
{ w: 'Sales', e: '⚡' },
{ w: 'Workflow', e: '📈' },
{ w: 'Admin', e: '⚙️' },
{ w: 'Reports', e: '📄' },
];
const FONT = '700 22px -apple-system,system-ui,sans-serif';
// 兜底词宽(原 effect.js 未挂载时的估算:字符数×13,再加胶囊内留白 74);
// 首帧 useLayoutEffect 用 offsetWidth 实测替换,与原渲染逐像素一致
const FALLBACK_WIDTHS = WORDS.map((o) => o.w.length * 13 + 74);
// 两侧静态文字("Your" / "Handled")
const SIDE: React.CSSProperties = {
color: '#15171d',
fontWeight: 800,
fontSize: 30,
letterSpacing: -0.5,
flex: 'none',
};
export const PillChipSlotCycleHandled: React.FC = () => {
const t = useT();
// 量宽:与原 effect.js 相同,用隐藏 span 实测每个词的 offsetWidth(+74 胶囊留白),
// 只在首次布局量一次;量到前用兜底估算,避免首帧闪动
const measRef = useRef<HTMLDivElement>(null);
const [widths, setWidths] = useState<number[]>(FALLBACK_WIDTHS);
useLayoutEffect(() => {
const spans = measRef.current?.children;
if (!spans) return;
setWidths(
WORDS.map((o, i) => ((spans[i] as HTMLElement).offsetWidth || o.w.length * 13) + 74),
);
}, []);
// 三次切换:0.25 / 0.47 / 0.69,每次用 0.12 的 inOutCubic 推进一格
let pos = 0;
for (const s0 of [0.25, 0.47, 0.69]) pos += seg(t, s0, s0 + 0.12, E.inOutCubic);
const ci = Math.min(WORDS.length - 1, Math.floor(pos));
const frac = pos - ci;
// 胶囊宽度随当前词→下一词插值,平滑挤开两侧文字
const chipW = lerp(frac, widths[ci], widths[Math.min(ci + 1, WORDS.length - 1)]);
// 幽灵项:当前词的前/后邻居,随滚动微移,落定时回到基准透明度
const near = Math.round(pos);
const roll = (pos - near) * 48 * 0.5;
const settle = 1 - Math.min(1, Math.abs(pos - near) * 3);
const ghost = (top: number): React.CSSProperties => ({
position: 'absolute',
left: '50%',
top,
font: FONT,
color: '#15171d',
whiteSpace: 'nowrap',
transform: `translateX(-50%) translateY(${-roll}px)`,
opacity: 0.13 * (0.4 + settle * 0.6),
});
return (
<DesignStage bg="#fbfbfd">
<div
style={{
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontFamily: '-apple-system,system-ui,sans-serif',
}}
>
{/* 隐藏量宽 span(与原 setup 的 meas 等价,不参与布局显示) */}
<div ref={measRef} style={{ position: 'absolute', visibility: 'hidden' }}>
{WORDS.map((o) => (
<span key={o.w} style={{ whiteSpace: 'nowrap', font: FONT }}>
{o.w}
</span>
))}
</div>
<div style={SIDE}>Your</div>
<div style={{ position: 'relative', flex: 'none' }}>
{/* 深色胶囊:overflow hidden 里放 4 行词的滚轮列 */}
<div
style={{
position: 'relative',
height: 48,
width: chipW,
margin: '0 14px',
flex: 'none',
borderRadius: 99,
background: '#1a1c24',
boxShadow: '0 8px 24px rgba(20,22,40,.22)',
overflow: 'hidden',
}}
>
<div
style={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
transform: `translateY(${-pos * 48}px)`,
}}
>
{WORDS.map(({ w, e }) => (
<div
key={w}
style={{
height: 48,
display: 'flex',
alignItems: 'center',
gap: 9,
paddingLeft: 18,
whiteSpace: 'nowrap',
}}
>
<span style={{ fontSize: 18 }}>{e}</span>
<span style={{ font: FONT, color: '#fff' }}>{w}</span>
</div>
))}
</div>
</div>
{/* 胶囊外上下的灰色幽灵项 */}
<div style={ghost(-38)}>{near > 0 ? WORDS[near - 1].w : ''}</div>
<div style={ghost(58)}>{near < WORDS.length - 1 ? WORDS[near + 1].w : ''}</div>
</div>
<div style={SIDE}>Handled</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/pill-chip-slot-cycle-handled/PillChipSlotCycleHandled.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 |
|---|---|---|---|
| — | |||
Mais nesta categoria

Word relay geometry
Three benefit words each carry their own dedicated geometry relay — a dashed circle spins and shrinks → three solid circles trim-grow in sequence → a metallic sheen sweeps then collapses to pure white; each new word outlines then fills in.

Word relay filmstrip
A left column of black-and-white page cards steps forward while a serif big word on the right relays in place (a fixed noun + a cycling verb) — the column steps only when the word changes, aligned to the current page card's midpoint.

Vertical word-roll cycle
The sentence's last word cycles on a vertical roller, 3 swaps at 0.55s each, fast then very slow with a slight overshoot; neighboring rows carry vertical blur and grayscale by distance, the centered word snapping from gray to accent color as it lands.

Two code reveals, side by side
The same syntax-highlighted code reveals two ways side by side — left: line-level stagger fading up 8px, right: character-by-character typing keeping each token's original color, with a #3a4468 block cursor on the current character.