Motion · Tipografia
Gradient word charge sweep
A gradient of colored light sweeps fast left to right to "charge" a keyword in a black-scene tagline — brightest at the wavefront, decaying behind it, then settling into a steady lightning-breathing glow.
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
Charging a single verb/selling-point word in a tagline (Supercharged/faster/AI…); the typography moment of a high-energy peak segment; the slogan frame of a black-scene brand film.
Intention
Within an all-white sentence, only the keyword gets "electrified": a gradient of colored light sweeps fast left to right across the characters, like an energy injection — brightest the instant it passes (wavefront trail), then decaying to a steady state; once filled, fine lightning arcs jump between characters and the word keeps a breathing glow. Three crux points: fast (sweep-charge 15–20f; slower reads as a progress bar), brightest at the wavefront (the character that just lit up is strongest — without this gradient it's just a static clipped gradient), and restrained glow (After-Effects-style layered bloom, but don't let it smear into a blob; the lightning should be thin and sparse, a garnish not a flood).
Duration & energy
- Duration: Sweep-charge ~15–20f (must be fast) + steady lightning-breathing 1–2s; whole segment 2.5–3.5s
- Energy: High (a single-word peak; everything before and after should yield to it)
Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)
意图
整句白字里只有关键词被"通电":渐变彩光从左到右快速扫过字符, 像能量注入——扫过的瞬间最亮(波前尾迹),随后衰减到稳态; 填满后字符之间跳起勾连的细闪电,词保持泛光呼吸。三个命门: 快(扫充 15–20f,慢了读作进度条)、波前最亮(刚点亮的 字符效果最强,没有这个梯度就是静态渐变裁切)、辉光克制 (AE 式多层泛光但别糊成一团,闪电细而稀疏才是点缀)。
动效核心
- 渐变填充:关键词做双层文字(白/渐变彩),彩层用从左到右推进的
mask 揭开;斜切 mask 边缘略糊(~8% 词宽)
- 波前尾迹增亮:跟随 mask 前沿一条 ~34% 词宽的增亮带,波前处
辉光最强、向后衰减;填充结束后 ~10f 淡出到稳态
- 辉光:4 层泛光(环境大柔光+中晕+近核+亮核),稳态 opacity
0.55–0.72 档(实测:0.75–0.95 档被裁"泛光感有点强")
- 闪电:填满后字符间勾连细闪电,紫红系三层描边(外晕 6/中 2.4/
亮核 1.4),mulberry32 种子随机跳位闪烁,稀疏零星(不是持续电弧)
- 禁:填充过程不带跟随移动的光点/星光(实测判例:跟随白色光点读作廉价装饰,删)
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 扫充时长 | 15–20f | 原片"速度很快";>28f 读作进度条 | | 波前增亮带 | ~34% 词宽,波前最亮向后衰减 | 无梯度=静态渐变;带太宽=整词一起亮没有"注入"感 | | 稳态泛光 | 4 层 opacity 0.55/0.55/0.62/0.72 | 0.75+ 档被裁太强;辉光层数<3 读作纯色 | | 闪电线宽 | 外 6/中 2.4/核 1.4 @1080p | 加倍即被裁"粗";颜色偏紫红(magenta 216,60,190 系) | | 闪电频率 | 稀疏零星(同屏 ≤2 道,间歇 >8f) | 频率高读作电弧故障;实测 demo 仍略密于原片 |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验
- 判例三条(用户逐字意见沉淀):①填充过程禁跟随光点;②闪电
必须紫红+细;③泛光宁弱勿强,波前梯度补足冲击力
- 与 type-rhythm-sync/cel-flash-stomp 分工:那些是节拍/砸字;
本卡是单词"充能"的能量语义,词必须是句中卖点词
- 关键词外的正文保持纯白静止——全句都动就没有"只有它通电"的戏
参考实现
demos/typography/gradient-word-sweep/ (GradientWordSweep.tsx) 原片出处:clickup-30.mp4
Reference implementation (Remotion)
- demos/typography/gradient-word-sweep/GradientWordSweep.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/typography/gradient-word-sweep/GradientWordSweep.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/gradient-word-sweep.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/typography/gradient-word-sweep/GradientWordSweep.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("gradient-word-sweep")Prompt
Use the video-shotcraft shot recipe "Gradient word charge sweep" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/gradient-word-sweep.json Raw TSX: https://gimgs.net/motion/gradient-word-sweep/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/typography/gradient-word-sweep/GradientWordSweep.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 3.71s 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/gradient-word-sweep/GradientWordSweep.tsx =====
// gradient-word-sweep v3 —— 批次 12 按用户意见微调(v2 结构保留):
// 1) 闪电偏紫红色 + 线宽调细(约减半);
// 2) 整体泛光强度略降(各辉光层 opacity 下调);
// 3) 波前尾迹梯度:刚被点亮的字符辉光最强,随扫过距离衰减到稳态
// (trailing-window 增亮层,填充结束后淡出到稳态呼吸)。
import React from 'react';
import { AbsoluteFill, useCurrentFrame, interpolate, Easing } from 'remotion';
const mulberry32 = (a: number) => () => {
let t = (a += 0x6d2b79f5);
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
const FONT = '"Avenir Next", Futura, "Helvetica Neue", sans-serif';
// 截图 5:S 偏蓝青 → 中段紫 → 粉 → 尾部琥珀
const GRAD = 'linear-gradient(92deg, #59c2ff 0%, #9d6bff 32%, #ff6ed4 62%, #ffc46b 100%)';
const FILL_START = 12;
const FILL_END = 30; // 18 帧 ≈ 0.6s,快扫
const LIGHT_START = FILL_END + 3;
// ---------- 种子化噪声与闪电 ----------
const rand = mulberry32(20260718);
const FLICKER: number[] = Array.from({ length: 160 }, () => rand());
type Bolt = { d: string; long: boolean };
const makeLongBolt = (r: () => number): Bolt => {
// 词上方勾连长弧:横跨若干字符,锯齿折线
const x0 = 30 + r() * 220;
const x1 = x0 + 160 + r() * 320;
const yBase = 38 + r() * 42;
const n = 7 + Math.floor(r() * 4);
let d = `M ${x0.toFixed(1)} ${(yBase + 26 + r() * 20).toFixed(1)}`;
for (let i = 1; i <= n; i++) {
const x = x0 + ((x1 - x0) * i) / n + (r() - 0.5) * 22;
const arch = Math.sin((i / n) * Math.PI) * -22; // 中段拱起
const y = yBase + arch + (r() - 0.5) * 30 + (i === n ? 30 + r() * 18 : 0);
d += ` L ${x.toFixed(1)} ${y.toFixed(1)}`;
}
return { d, long: true };
};
const makeShortBolt = (r: () => number): Bolt => {
// 字符之间的短勾连:竖向小锯齿
const x = 60 + r() * 540;
const y0 = 72 + r() * 24;
const y1 = y0 + 55 + r() * 45;
const n = 4 + Math.floor(r() * 3);
let d = `M ${x.toFixed(1)} ${y0.toFixed(1)}`;
for (let i = 1; i <= n; i++) {
const y = y0 + ((y1 - y0) * i) / n;
const xx = x + (r() - 0.5) * 30;
d += ` L ${xx.toFixed(1)} ${y.toFixed(1)}`;
}
return { d, long: false };
};
const BOLTS: Bolt[] = Array.from({ length: 16 }, (_, i) =>
i % 3 === 0 ? makeShortBolt(rand) : makeLongBolt(rand),
);
// 闪烁事件:帧窗内某条闪电点亮,随机跳位
type Flash = { at: number; life: number; bolt: number };
const FLASHES: Flash[] = Array.from({ length: 36 }, () => ({
at: LIGHT_START + Math.floor(rand() * 70),
life: 2 + Math.floor(rand() * 4),
bolt: Math.floor(rand() * BOLTS.length),
}));
export const GradientWordSweep: React.FC = () => {
const frame = useCurrentFrame();
const enter = interpolate(frame, [0, 12], [0, 1], {
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
// 快速填充进度
const p = interpolate(frame, [FILL_START, FILL_END], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.quad),
});
const pPct = p * 100;
const filling = frame >= FILL_START && frame <= FILL_END + 4;
const headFade = interpolate(frame, [FILL_END, FILL_END + 6], [1, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// 波前尾迹:刚点亮字符最亮,向后衰减;填充结束后整体淡出到稳态
const trailFade = interpolate(frame, [FILL_END, FILL_END + 10], [1, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const TRAIL = 34; // 尾迹长度(% 宽度)
const trailMask =
`linear-gradient(90deg, transparent 0%, transparent ${Math.max(0, pPct - TRAIL)}%, ` +
`rgba(0,0,0,0.9) ${Math.max(0, pPct - 3)}%, rgba(0,0,0,0.9) ${Math.min(100, pPct + 1)}%, ` +
`transparent ${Math.min(100, pPct + 6)}%)`;
const noise = FLICKER[Math.min(frame, FLICKER.length - 1)];
// 当前帧活跃闪电
const active = FLASHES.filter((f) => frame >= f.at && frame < f.at + f.life);
const boltBoost = active.length > 0 ? 0.35 : 0;
// 辉光呼吸:填满后带噪声微闪 + 闪电时增亮
const glowLvl =
interpolate(frame, [FILL_START, FILL_END], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
}) *
(0.82 + 0.18 * noise) +
boltBoost;
// 软边遮罩:辉光层的填充边缘不生硬
const softMask = (soft: number): string =>
p >= 1
? 'none'
: `linear-gradient(90deg, #000 0%, #000 ${Math.max(0, pPct - soft)}%, transparent ${Math.min(100, pPct + soft * 0.6)}%)`;
const maskStyle = (soft: number): React.CSSProperties =>
p >= 1
? {}
: {
WebkitMaskImage: softMask(soft),
maskImage: softMask(soft),
};
const lineStyle: React.CSSProperties = {
fontFamily: FONT,
fontWeight: 700,
fontStyle: 'italic',
fontSize: 92,
letterSpacing: -1,
lineHeight: 1.28,
color: '#ffffff',
whiteSpace: 'nowrap',
};
const gradText: React.CSSProperties = {
position: 'absolute',
inset: 0,
backgroundImage: GRAD,
WebkitBackgroundClip: 'text',
backgroundClip: 'text',
color: 'transparent',
};
return (
<AbsoluteFill style={{ background: '#050505', justifyContent: 'center', alignItems: 'center' }}>
{/* 词后方环境泛光(大半径,随充能增强) */}
<div
style={{
position: 'absolute',
left: 500,
top: 340,
width: 820,
height: 320,
borderRadius: '50%',
background:
'radial-gradient(closest-side, rgba(180,110,255,0.6), rgba(255,110,212,0.25) 55%, transparent 78%)',
filter: 'blur(38px)',
opacity: 0.55 * glowLvl,
}}
/>
<div
style={{
textAlign: 'center',
opacity: enter,
transform: `translateY(${(1 - enter) * 36}px)`,
}}
>
<div style={lineStyle}>
<span style={{ position: 'relative', display: 'inline-block' }}>
<span>Supercharged</span>
{/* AE 式辉光:大半径柔光层(最糊) */}
<span
aria-hidden
style={{
...gradText,
...maskStyle(14),
filter: 'blur(46px) saturate(1.6)',
opacity: 0.55 * glowLvl,
transform: 'scale(1.05)',
}}
>
Supercharged
</span>
{/* 中晕层 */}
<span
aria-hidden
style={{
...gradText,
...maskStyle(10),
filter: 'blur(18px) saturate(1.4) brightness(1.15)',
opacity: 0.62 * glowLvl,
}}
>
Supercharged
</span>
{/* 近核柔光 */}
<span
aria-hidden
style={{
...gradText,
...maskStyle(7),
filter: 'blur(6px) brightness(1.25)',
opacity: 0.72 * Math.min(1, glowLvl + 0.1),
}}
>
Supercharged
</span>
{/* 清晰渐变本体 */}
<span
aria-hidden
style={{
...gradText,
clipPath: `inset(-25% ${100 - pPct}% -25% 0)`,
}}
>
Supercharged
</span>
{/* 波前尾迹增亮:刚点亮字符辉光最强,向后衰减到稳态 */}
{trailFade > 0.01 && (
<span
aria-hidden
style={{
...gradText,
WebkitMaskImage: trailMask,
maskImage: trailMask,
filter: 'blur(9px) saturate(1.7) brightness(1.7)',
opacity: 0.95 * trailFade,
}}
>
Supercharged
</span>
)}
{/* 填充头字符过曝亮核(字符形状,非独立光点;仅填充期间) */}
{filling && p < 1 && (
<span
aria-hidden
style={{
position: 'absolute',
inset: 0,
color: '#fff',
clipPath: `inset(-25% ${Math.max(0, 100 - pPct)}% -25% ${Math.max(0, pPct - 10)}%)`,
filter: 'blur(3px)',
opacity: 0.9 * headFade,
textShadow: '0 0 22px rgba(255,255,255,0.9), 0 0 55px rgba(216,150,255,0.8)',
}}
>
Supercharged
</span>
)}
{/* 勾连闪电:填充完成后,字符之间/词上方随机闪烁跳位 */}
<svg
aria-hidden
viewBox="0 0 700 240"
style={{
position: 'absolute',
left: -25,
top: -62,
width: 700,
height: 240,
overflow: 'visible',
pointerEvents: 'none',
}}
>
{active.map((f, i) => {
const b = BOLTS[f.bolt];
const decay = 1 - (frame - f.at) / f.life;
return (
<g key={`${f.at}-${i}`} opacity={Math.min(1, 1.1 * decay)}>
<path
d={b.d}
fill="none"
stroke="rgba(216,60,190,0.65)"
strokeWidth={b.long ? 6 : 4.5}
strokeLinejoin="miter"
style={{ filter: 'blur(6px)' }}
/>
<path
d={b.d}
fill="none"
stroke="rgba(235,110,215,0.9)"
strokeWidth={b.long ? 2.4 : 1.9}
strokeLinejoin="miter"
style={{ filter: 'blur(1.5px)' }}
/>
<path
d={b.d}
fill="none"
stroke="#ffd8f2"
strokeWidth={b.long ? 1.4 : 1.1}
strokeLinejoin="miter"
/>
</g>
);
})}
</svg>
</span>{' '}
<span>performance</span>
</div>
<div style={lineStyle}>with rock-solid reliability</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/gradient-word-sweep/GradientWordSweep.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.