Motion · Ánh sáng & nhấn
Chuyển động chơi đùa ánh sáng
Ba kiểu ánh sáng: spotlight-sweep — quét đèn spotlight qua chữ, sheen — một lần quét ánh sáng phản chiếu, và halation-bloom — quầng sáng bừng lên khi va dừng.
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
Biến thể
Hai lớp cùng một đoạn chữ: bản tối luôn ở 0.07 opacity, bản sáng dùng radial mask theo quầng sáng; hình nón ánh sáng lắc qua lại hai lần rồi đông cứng ở độ sáng tối đa.
Mở đầu reveal tiêu đề lớn trong cảnh tối
Một dải sáng 45° translateX quét một lần qua thẻ nền mực đậm, bị cắt bởi góc bo tròn overflow hidden.
Một lần đăng quang trong đoạn tĩnh của thẻ chính
Bản sao chữ ở lớp dưới, làm mờ và tăng sáng làm lớp quầng; ở khung hình va dừng nó bừng lên thành một vòng rồi lắng xuống thành ánh sáng mềm ổn định.
Tăng cường va chạm cho khung hình crash-zoom va dừng
Khi nào dùng
Coi ánh sáng như nét bút thứ tư (quét/lau/bừng sáng): reveal tiêu đề trong cảnh tối (A), tôn vinh thẻ chính (B), cú va chạm ở khung hình va dừng (D).
Ý đồ
Trước đây thư viện chỉ có một kỹ thuật "ánh sáng" duy nhất; ba kiểu này xây dựng ánh sáng thành một hệ thống, ba nét bút ba nhiệm vụ: A là quét — tiêu đề gần như vô hình trong cảnh tối được quét sáng bởi một hình nón ánh sáng dao động; B là lau — một dải sáng 45° quét một lần qua thẻ chính nền mực đậm, bị bo góc cắt gọn, một lễ đăng quang lặng lẽ; D là bừng sáng — trên khung hình va dừng chữ trắng, một vòng quầng sáng mềm bừng lên rồi nổ ra, sau đó lắng xuống thành ánh sáng thở đều thường trực. Tất cả đều sống trên nền tối hoặc chủ thể chính tối màu — trên nền trắng ánh sáng vô hình chính là nền tảng của cả thẻ này.
Thời lượng & năng lượng
- Thời lượng: A: ~5.3s / B: ~4.7s / D: ~4.8s
- Năng lượng: A: Trung bình / B: Thấp / D: 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)
意图
库内"光"一直只有 spotlight-hero-card 一个点射手法;这三式把光做成 体系,三种笔触三种职责:A 是扫——暗场里几乎不可见的标题被锥形 光摆动扫亮,光到哪哪亮,与 spotlight-hero-card 的区别是那个打卡片、 这个扫文字;B 是擦——深墨主角卡上一道 45° 高光带缓扫一次,被 圆角裁住,无声的加冕;D 是晕——白字撞停帧一圈柔晕猛涨炸开再 回落成驻留呼吸光,与 B 的区别是 B 移动扫过、D 驻留在高光边缘。 全部深底或深主角——白底上提亮不可见(判例)是这整张卡的地基。
三式选型
| 式 | 做法 | 适用 | |----|------|------| | A spotlight-sweep | 双层同文本:暗版 0.07 常驻,亮版 radial mask 随光斑走,锥形光摆两个来回后全亮定格 | 暗场大标题揭示开场 | | B sheen-sweep | 深墨卡上 45° 高光带 translateX 扫一次,overflow hidden 圆角裁剪 | 主角卡静止段的一次加冕 | | D halation-bloom | 文字复制底层 blur+brightness 当晕层,撞停帧起猛涨一圈回落成稳态柔光 | crash-zoom 撞停帧的冲击加成 |
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | A 反差 | 暗版 0.07 vs 全亮 1.0,白 #f5f5f3 对 #2a2a28 暗场 | 反差是本式全部;暗版 >0.15 就没有"点亮"了 | | A 光斑 | circle 380px(0.95 硬芯+柔边),x = 960+560·sin(2πf/55),两来回至 f110 | f110–125 全亮 out-cubic、光效线性消散(扩散/消散解耦判例) | | B 高光带 | 宽 1.6×卡宽,115° 渐变峰值 rgba(255,255,255,0.32),40–68f inOut cubic 扫一次 | 0.32 在深墨底上足够响;双向 clamp 保证只扫一次 | | D 晕层 | blur 22px + brightness 1.8;scale 1→1.3 用 6f out-cubic 猛涨,opacity 20f 线性回落 0.35 再 15f 缓收 0.22 稳态 | 扩散 out-cubic/消散线性解耦(判例);同快涨快消读作闪光灯 | | 摘罩 | 三式光效层全部条件挂载,扫完/消散完即卸载 | opacity 0 残留 = 永不真静止(feTurbulence 摘罩判例同族) | | 收尾 | A 35f / B 72f / D 89f 真静止 | — |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验
- 全片光效合计 ≤2 次——光是最快掉价的手法,每个卡都闪一下
等于廉价模板
- B 式是高标准重试通过:旧否决(2026-07-13 判例)针对当时实现不好看,
非路径不可行;四约束 = 单点+主角+圆角裁剪+只扫一次,违反任何 一条回到否决区
- 深底是地基:三式全部依赖深底/深主角,白底上做光效直接跳过本卡
- D 与 crash-zoom-punch 撞停帧天然联动(撞停即起晕);A 与
spotlight-hero-card 同片二选一,都是"聚光"语汇
参考实现
demos/effects/light-play-moves/ (HalationBloom.tsx / SheenSweepRetry.tsx / SpotlightSweepReveal.tsx)
Mã nguồn tham chiếu (Remotion)
- demos/effects/light-play-moves/HalationBloom.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/light-play-moves/HalationBloom.tsx
- demos/effects/light-play-moves/SheenSweepRetry.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/light-play-moves/SheenSweepRetry.tsx
- demos/effects/light-play-moves/SpotlightSweepReveal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/light-play-moves/SpotlightSweepReveal.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/light-play-moves.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/effects/light-play-moves/HalationBloom.tsx, demos/effects/light-play-moves/SheenSweepRetry.tsx, demos/effects/light-play-moves/SpotlightSweepReveal.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("light-play-moves")Prompt
Use the video-shotcraft shot recipe "Chuyển động chơi đùa ánh sáng" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/light-play-moves.json Raw TSX: https://gimgs.net/motion/light-play-moves/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/effects/light-play-moves/HalationBloom.tsx, demos/effects/light-play-moves/SheenSweepRetry.tsx, demos/effects/light-play-moves/SpotlightSweepReveal.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 5.38s 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/effects/light-play-moves/HalationBloom.tsx =====
// halation-bloom —— 高光晕染
// 深灰底大白字 "10x" crash-zoom 急停入场(2.4→1,7f in-quad + 2f 回弹)。
// 撞停帧起:底层复制文字(blur+提亮的白色晕层)猛涨一圈(scale 1→1.3,6f out-cubic 扩散),
// 再 20f 线性回落到 0.35 驻留柔晕(扩散/消散解耦判例),随后 15f 缓收到 0.22 稳态。
// 深底白字:白底上提亮不可见判例。收尾真静止 ≥40f。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
const BG = '#2a2a28';
const WHITE = '#f7f7f5';
const MID = '#8f8f8d';
// —— 时间轴(30fps)——
const ZOOM_START = 8; // 入场起始
const IMPACT = 15; // 撞停帧(7f 急速缩入)
const REBOUND_END = 17; // 2f 回弹
const POP_END = IMPACT + 6; // 晕层猛涨 6f → f21
const FALL_END = POP_END + 20; // 20f 线性回落 → f41
const SETTLE_END = FALL_END + 15; // 15f 缓收 → f56,此后全静止
// 总时长 145f → 静止 89f ≥ 40f
const TextBlock: React.FC<{ color: string }> = ({ color }) => (
<div
style={{
fontFamily: 'Helvetica, Arial, sans-serif',
fontSize: 260,
fontWeight: 800,
color,
letterSpacing: '-0.02em',
lineHeight: 1,
whiteSpace: 'nowrap',
}}
>
10x
</div>
);
export const HalationBloom: React.FC = () => {
const frame = useCurrentFrame();
// —— crash-zoom 入场:scale 2.4 → 0.94(7f in-quad 加速撞停)→ 1(2f 回弹)——
const zoomIn = interpolate(frame, [ZOOM_START, IMPACT], [2.4, 0.94], {
easing: Easing.in(Easing.quad),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const rebound = interpolate(frame, [IMPACT, REBOUND_END], [0.94, 1], {
easing: Easing.out(Easing.quad),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const textScale = frame < IMPACT ? zoomIn : rebound;
const textOpacity = interpolate(frame, [ZOOM_START, ZOOM_START + 3], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// —— 晕层:撞停帧起条件挂载 ——
// 扩散(scale):out-cubic,6f 猛涨 1 → 1.3
const bloomScale = interpolate(frame, [IMPACT, POP_END], [1, 1.3], {
easing: Easing.out(Easing.cubic),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// 消散(opacity):与扩散解耦——猛涨段保持全亮,随后 20f 线性回落到 0.35
const bloomFall = interpolate(frame, [POP_END, FALL_END], [1, 0.35], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// 驻留晕 15f 缓收到 0.22 稳态
const bloomSettle = interpolate(frame, [FALL_END, SETTLE_END], [0.35, 0.22], {
easing: Easing.out(Easing.quad),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const bloomOpacity = frame < FALL_END ? bloomFall : bloomSettle;
return (
<div
style={{
width: 1920,
height: 1080,
background: BG,
position: 'relative',
overflow: 'hidden',
}}
>
{/* 小标签,全程静态 */}
<div
style={{
position: 'absolute',
top: 110,
width: '100%',
textAlign: 'center',
fontFamily: 'Helvetica, Arial, sans-serif',
fontSize: 30,
fontWeight: 700,
letterSpacing: '0.35em',
color: MID,
}}
>
HALATION BLOOM
</div>
{/* 晕层:文字复制底层,blur + 提亮,撞停帧起条件挂载 */}
{frame >= IMPACT && (
<div
style={{
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transform: `scale(${bloomScale})`,
opacity: bloomOpacity,
filter: 'blur(22px) brightness(1.8)',
}}
>
<TextBlock color={WHITE} />
</div>
)}
{/* 本体文字:crash-zoom 急停 */}
<div
style={{
position: 'absolute',
inset: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transform: `scale(${textScale})`,
opacity: textOpacity,
}}
>
<TextBlock color={WHITE} />
</div>
</div>
);
};
// ===== demos/effects/light-play-moves/SheenSweepRetry.tsx =====
// sheen-sweep-retry —— 单点扫光(高标准重试)
// 深墨大卡居中,一道 45° 高光带在 40–68f 从左外扫到右外,仅此一次。
// 约束:单点(只扫主角卡)、圆角裁剪(overflow hidden)、扫前扫后完全静止。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
import { G } from '../../_fixtures/Fixtures';
const CARD_W = 760;
const CARD_H = 420;
const SHEEN_W = CARD_W * 1.6; // 1216
export const SheenSweepRetry: React.FC = () => {
const frame = useCurrentFrame();
// 扫光:40–68f,从卡左外(-SHEEN_W)扫到卡右外(CARD_W),inOut(cubic),只一次
const sweepActive = frame >= 40 && frame <= 68;
const x = interpolate(frame, [40, 68], [-SHEEN_W, CARD_W], {
easing: Easing.inOut(Easing.cubic),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
return (
<div
style={{
width: 1920,
height: 1080,
background: G.bg,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontFamily: 'Helvetica, Arial, sans-serif',
}}
>
<div
style={{
width: CARD_W,
height: CARD_H,
background: G.side,
borderRadius: 24,
overflow: 'hidden', // 圆角裁剪:高光带被卡的圆角裁住
position: 'relative',
boxShadow: '0 12px 40px rgba(0,0,0,0.22)',
boxSizing: 'border-box',
padding: '64px 72px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
gap: 28,
}}
>
<div
style={{
fontSize: 130,
fontWeight: 800,
color: '#f5f5f3',
letterSpacing: 2,
lineHeight: 1,
}}
>
PRO
</div>
<div style={{ height: 16, width: 380, background: G.sideBar, borderRadius: 8 }} />
<div style={{ height: 16, width: 260, background: G.sideBar, borderRadius: 8 }} />
{/* 高光带:条件挂载,扫完即摘罩,收尾真静止 */}
{sweepActive && (
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: SHEEN_W,
height: CARD_H,
transform: `translateX(${x}px)`,
background:
'linear-gradient(115deg, transparent 42%, rgba(255,255,255,0.32) 50%, transparent 58%)',
pointerEvents: 'none',
}}
/>
)}
</div>
</div>
);
};
// ===== demos/effects/light-play-moves/SpotlightSweepReveal.tsx =====
import React from 'react';
import { AbsoluteFill, useCurrentFrame, interpolate, Easing } from 'remotion';
// spotlight-sweep-reveal: 聚光灯摆动扫字
// 暗场 #2a2a28,标题两行常驻 opacity 0.07;亮版同文本用 radial-gradient mask,
// 光斑 x 按 sin 摆动扫两个来回(周期 ~55f),f110 后全字提亮定格,
// 光斑/光锥线性消散并在 f>=125 条件卸载,125–160 真静止 35f。
const BG = '#2a2a28';
const INKW = '#f5f5f3';
const CX = 960;
const CY = 540;
const AMP = 560; // 摆动幅度
const PERIOD = 55; // 单个来回帧数
const SWEEP_END = 110; // 两个来回结束
const REVEAL_END = 125; // 提亮完成,之后真静止
const textStyle: React.CSSProperties = {
fontFamily: 'Helvetica Neue, Helvetica, Arial, sans-serif',
fontSize: 150,
fontWeight: 800,
lineHeight: 1.15,
letterSpacing: '0.04em',
color: INKW,
textAlign: 'center',
whiteSpace: 'pre',
};
const TitleText: React.FC = () => (
<AbsoluteFill style={{ justifyContent: 'center', alignItems: 'center' }}>
<div style={textStyle}>{'SPOTLIGHT\nON'}</div>
</AbsoluteFill>
);
export const SpotlightSweepReveal: React.FC = () => {
const f = useCurrentFrame();
// 光斑 x:sin 摆动,两个来回后停在中心
const sweepF = Math.min(f, SWEEP_END);
const x = CX + AMP * Math.sin((2 * Math.PI * sweepF) / PERIOD);
// 全字提亮(扩散感 out-cubic)
const brighten = interpolate(f, [SWEEP_END, REVEAL_END], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
// 光斑/光锥消散(线性)
const fadeOut = interpolate(f, [SWEEP_END, REVEAL_END], [1, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const sweepAlive = f < REVEAL_END; // 条件卸载,保证末段真静止
const maskGrad = `radial-gradient(circle 380px at ${x}px ${CY}px, rgba(0,0,0,1) 0%, rgba(0,0,0,0.95) 45%, rgba(0,0,0,0) 100%)`;
return (
<AbsoluteFill style={{ backgroundColor: BG }}>
{/* 暗版文字常驻 */}
<AbsoluteFill style={{ opacity: 0.07 }}>
<TitleText />
</AbsoluteFill>
{sweepAlive && (
<>
{/* 光锥:从顶部收拢到光斑 */}
<AbsoluteFill
style={{
opacity: fadeOut,
clipPath: `polygon(${CX - 70}px -40px, ${CX + 70}px -40px, ${x + 420}px ${CY + 230}px, ${x - 420}px ${CY + 230}px)`,
background:
'linear-gradient(to bottom, rgba(245,245,243,0.16), rgba(245,245,243,0.03) 85%, rgba(245,245,243,0) 100%)',
}}
/>
{/* 落在暗墙上的柔光斑 */}
<AbsoluteFill
style={{
opacity: fadeOut,
background: `radial-gradient(circle 460px at ${x}px ${CY}px, rgba(245,245,243,0.22) 0%, rgba(245,245,243,0.08) 55%, rgba(245,245,243,0) 100%)`,
}}
/>
{/* 亮版文字,按帧移动的 radial mask */}
<AbsoluteFill
style={{
opacity: fadeOut === 1 ? 1 : Math.max(fadeOut, 0),
WebkitMaskImage: maskGrad,
maskImage: maskGrad,
}}
>
<TitleText />
</AbsoluteFill>
</>
)}
{/* 全亮定格层 */}
<AbsoluteFill style={{ opacity: brighten }}>
<TitleText />
</AbsoluteFill>
</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/effects/light-play-moves/HalationBloom.tsx, demos/effects/light-play-moves/SheenSweepRetry.tsx, demos/effects/light-play-moves/SpotlightSweepReveal.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

Đèn rọi hiện hình trên nền tối
Ba kiểu đèn rọi hiện hình trên nền tối — A quét thức-ngủ (đèn tới thì sáng, đèn đi thì tối), B quét ngang rìa viền (ánh tím bám mép UI thấm vào + đèn quét phải đều), C hiện hình đều từ góc (đèn hình tròn nở đều từ góc sáng cả màn hình); ánh sáng chính là lời kể trên nền đen.

Đập vào cảnh năng lượng cao
Ba kiểu đập vào cảnh năng lượng cao: dừng phối cảnh kiểu Kanada, đập rơi tỷ số, hoặc lan chấn động sang thẻ lân cận.

Vệt sáng quét lắp ráp bay vào
Vệt sáng quét từ trên xuống lưới nền tối trống, tới đâu thành phần vùng đó bay vào khớp vị trí — quét xong là lắp ráp xong.

Vệt sáng quét chú thích tiêu điểm
Một vệt sáng quét từ trên xuống dưới trang, khoanh khung ngắm kiểu máy ảnh vào từng vùng nó đi qua rồi gắn chú thích chữ mono.