Motion · Ánh sáng & nhấn
Chuyển động FUI/HUD
Hai kiểu FUI/HUD: line-unfold-panel — một đường thẳng mở ra thành mặt phẳng (ngữ pháp đường-thành-mặt kiểu CRT) và reticle-lock-on — khung ngắm bay vào khóa mục tiêu.
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ể
scaleX 0→1 (out poly4, rút nhanh 5f) tiếp theo scaleY 3px→đủ chiều cao (out cubic 9f), nội dung mờ hiện sớm một nhịp; khi thoát diễn ngược lại theo thứ tự gương.
Vào/ra của bảng trong đoạn nền tối; cảm giác nghi thức bật/tắt máy
Bốn góc chữ L là bốn bản sao gương của cùng một cặp hình chữ nhật; bay vào (10f out cubic) và co lại (2.2×→0.94×→1 vượt ngưỡng bật lại) tách rời nhau; ở khung hình khóa mục tiêu, mục tiêu sáng nhẹ lên và nhãn bật ra kiểu back.
Gọi tên mục tiêu; khớp nhịp hiệu ứng âm thanh
Khi nào dùng
Dùng A cho cảnh vào/ra của bảng trong đoạn nền tối, chất công nghệ; dùng B cho bất kỳ khoảnh khắc gọi tên mục tiêu kiểu "nhìn đây" (thay cho khoanh tròn/mũi tên, không cần đóng băng khung hình).
Ý đồ
Hai mô-típ từ giao diện khoa học viễn tưởng (Jarvis/Territory) có thể trung tính hóa. A là nghi thức bật/tắt của bảng: một đường mảnh 1px rút ra cực nhanh → giãn theo chiều dọc thành bảng đầy đủ → nội dung mờ hiện trước một nhịp, thoát ra theo trình tự ngược khi đường nén lại thành điểm rồi tắt (kiểu tắt CRT cũ); chỉ cần đường mảnh thang xám là đủ, không cần màu xanh công nghệ. B là bắt giữ trong lúc chuyển động: bốn góc chữ L lao vào từ ngoài khung, vượt ngưỡng rồi bật lại, "khớp" vào bốn góc của phần tử mục tiêu kèm nhãn bật ra — phân công với freeze-annotate: thẻ đó đóng băng khung hình để chú thích, thẻ này gọi tên trong lúc hình vẫn đang trôi.
Thời lượng & năng lượng
- Thời lượng: A: 3–4s (bao gồm cả lúc thoát) / B: 2–3s
- Năng lượng: A: Trung bình / B: Trung bình cao (khung khóa mục tiêu là điểm nhấ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)
意图
科幻虚构界面(Jarvis/Territory)的两个可中性化母题。A 是面板的 仪式感开关机:1px 细线极快抽出→纵向撑开成面板→内容淡入,退场反向 压线缩点熄灭(老 CRT 关机);灰阶细线即成立,不需要科技蓝。 B 是运动中的捕获:四个 L 形角标从画外冲入、超调回弹、"咔"地咬合 到目标元素四角+弹标签——与 freeze-annotate 分工:那卡冻结画面标注, 本卡在流动中点名。
两式选型
| 式 | 做法 | 适用 | |----|------|------| | A line-unfold-panel | scaleX 0→1(out poly4 急抽 5f)接 scaleY 3px→满高(out cubic 9f),内容提前一拍淡入;退场镜像反序 | 暗场面板入退场;开关机仪式感 | | B reticle-lock-on | 四 L 角=同一对矩形四份镜像,飞入(10f out cubic)与收缩(2.2×→0.94×→1 超调回弹)解耦;咬合帧目标微亮+标签 back 弹出 | 目标点名;踩音效重拍 |
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | A 两段节奏 | 抽线 5f 急 vs 撑面 9f 缓 | 节奏差是手法本体,等长就平了 | | A 线体 | 白色发光条 3px 级;线/点阶段与面板阶段条件切换 | 熄灭后必须条件卸载才有真静止 | | B 飞入行程 | 画外 ≥1000px 冲入 | 行程短读作"角标出现"不是"扑过来" | | B 超调 | 收缩过头到 0.94× 再回弹 1 | 无超调没有"咔"的咬合感 | | B 咬合帧 | 目标白 overlay 快闪 0.55→停 0.28 + 标签同帧弹出 | 微亮与标签错帧就散了 |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验
- Remotion 无
Easing.quart——用Easing.poly(4)(demo 实测报
"easing is not a function" 判例)
- B 一支片 ≤2 次:连用读作军事模板;两次锁定目标必须不同
- A 与 glow-flyline 同属暗场词汇,相邻段落别背靠背堆光效(Q4 同源)
- 声音:A 抽线一声短 whoosh、撑开一声轻 pop;B 咬合帧一声"咔"
(机械 click 而非游戏 UI 音,S1 边界内)
参考实现
demos/effects/fui-hud-moves/ (LineUnfoldPanel.tsx / ReticleLockOn.tsx)
Mã nguồn tham chiếu (Remotion)
- demos/effects/fui-hud-moves/LineUnfoldPanel.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/fui-hud-moves/LineUnfoldPanel.tsx
- demos/effects/fui-hud-moves/ReticleLockOn.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/fui-hud-moves/ReticleLockOn.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/fui-hud-moves.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/effects/fui-hud-moves/LineUnfoldPanel.tsx, demos/effects/fui-hud-moves/ReticleLockOn.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("fui-hud-moves")Prompt
Use the video-shotcraft shot recipe "Chuyển động FUI/HUD" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/fui-hud-moves.json Raw TSX: https://gimgs.net/motion/fui-hud-moves/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/effects/fui-hud-moves/LineUnfoldPanel.tsx, demos/effects/fui-hud-moves/ReticleLockOn.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 4.72s 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/fui-hud-moves/LineUnfoldPanel.tsx =====
// line-unfold-panel —— 一线展面(Jarvis/FUI 母题)
// 暗底。入场两拍:3px 细线从中点向两侧极快抽出(5f)→ 定宽后纵向
// 撑开成 Card 面板(9f,out 缓动)→ 内容延迟淡入。
// 静置展示后反向退场:压扁成线(7f)→ 线缩成点 → 熄灭,像老 CRT 关机。
// f0–12 空场静置;入场 f12–34;持面板至 f78;退场 f78–98;末静止 ≥42f(140f)。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
import { G, Card } from '../../_fixtures/Fixtures';
const PANEL_W = 760;
const PANEL_H = 460;
const CX = 960;
const CY = 540;
// —— 入场时间表 ——
const T0 = 12; // 点亮起点
const LINE_END = T0 + 5; // 线抽出完成 f17
const UNFOLD_END = LINE_END + 9; // 面板撑开完成 f26
const CONTENT_END = UNFOLD_END + 8; // 内容淡入完成 f34
// —— 退场时间表 ——
const OUT0 = 78; // 开始压扁
const COLLAPSE_END = OUT0 + 7; // 压成线 f85
const SHRINK_END = COLLAPSE_END + 6; // 线缩成点 f91
const OFF = SHRINK_END + 4; // 点熄灭 f95
const clamp = { extrapolateLeft: 'clamp' as const, extrapolateRight: 'clamp' as const };
export const LineUnfoldPanel: React.FC = () => {
const frame = useCurrentFrame();
// 入场:scaleX(线抽出)快进快停,入场后保持 1
const inSX = interpolate(frame, [T0, LINE_END], [0.004, 1], {
easing: Easing.out(Easing.poly(4)),
...clamp,
});
// 入场:scaleY(纵向撑开),线阶段压在 3px
const inSY = interpolate(frame, [LINE_END, UNFOLD_END], [3 / PANEL_H, 1], {
easing: Easing.out(Easing.cubic),
...clamp,
});
// 内容淡入(面板撑开过半才开始)
const contentOp = interpolate(frame, [UNFOLD_END - 3, CONTENT_END], [0, 1], {
easing: Easing.out(Easing.quad),
...clamp,
});
// 退场:先压 Y 回线,再缩 X 回点
const outSY = interpolate(frame, [OUT0, COLLAPSE_END], [1, 3 / PANEL_H], {
easing: Easing.in(Easing.cubic),
...clamp,
});
const outSX = interpolate(frame, [COLLAPSE_END, SHRINK_END], [1, 0.004], {
easing: Easing.in(Easing.poly(4)),
...clamp,
});
// 内容在压扁前先撤
const contentOutOp = interpolate(frame, [OUT0 - 4, OUT0 + 2], [1, 0], clamp);
const sx = frame < OUT0 ? inSX : outSX;
const sy = frame < OUT0 ? inSY : outSY;
// 末点熄灭:opacity 快落。f >= OFF 后整个元素条件卸载 → 真静止
const dotOp = interpolate(frame, [SHRINK_END, OFF], [1, 0], {
easing: Easing.in(Easing.quad),
...clamp,
});
const alive = frame >= T0 && frame < OFF;
// 面板阶段(sy 足够大)显示卡片内容;线/点阶段显示发光条
const isPanel = sy > 0.15;
return (
<div style={{ width: 1920, height: 1080, background: '#1c1c1b', overflow: 'hidden', position: 'relative' }}>
<div
style={{
position: 'absolute',
top: 120,
width: '100%',
textAlign: 'center',
fontFamily: 'Helvetica, Arial, sans-serif',
fontWeight: 800,
fontSize: 52,
color: G.mid,
letterSpacing: 2,
}}
>
LINE UNFOLD PANEL
</div>
{alive && (
<div
style={{
position: 'absolute',
left: CX - PANEL_W / 2,
top: CY - PANEL_H / 2,
width: PANEL_W,
height: PANEL_H,
transform: `scaleX(${sx}) scaleY(${sy})`,
transformOrigin: '50% 50%',
opacity: dotOp,
}}
>
{isPanel ? (
<>
<Card w={PANEL_W} h={PANEL_H} seed={3} style={{ border: `2px solid ${G.mid}`, boxShadow: '0 0 40px rgba(255,255,255,0.18)' }} />
{/* 内容层单独控 opacity:盖一层暗板模拟"内容未亮" */}
<div
style={{
position: 'absolute',
inset: 2,
borderRadius: 12,
background: G.card,
opacity: 1 - Math.min(contentOp, contentOutOp),
}}
/>
</>
) : (
// 线/点阶段:白色发光条填满整个盒(被 scale 压成线)
<div style={{ width: '100%', height: '100%', background: '#ffffff', boxShadow: '0 0 60px rgba(255,255,255,0.9)', borderRadius: 2 }} />
)}
</div>
)}
</div>
);
};
// ===== demos/effects/fui-hud-moves/ReticleLockOn.tsx =====
// reticle-lock-on —— 准星咬合(钢铁侠 HUD / 安德的游戏)
// FakeDashboard 静置。四个 L 形角标组成的取景框从画外飞入(大框),
// 超调回弹后收缩贴紧目标卡片四角"咔"地咬合定格;咬合帧卡片微亮 +
// 旁弹一行小标签。画面不冻结,是运动中的捕获。
// f0–14 面板静置;f14–24 飞入;f24–46 收缩(含过冲回弹);f46 咬合;
// 标签 f46–56 弹出;f56 后真静止 ≥84f(140f 总长)。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
import { G, FakeDashboard } from '../../_fixtures/Fixtures';
// 目标:variant A 网格第 2 张卡(第一行中间)。
// 布局推导:侧栏 220 + padding 36,网格 3 列 gap 28,
// 内容区宽 1700-72=1628 → 卡宽 (1628-56)/3 ≈ 524,卡高 (1080-72-72-28)/2 ≈ 454...
// 实测近似:目标框取第一行中间卡的外接矩形(含少量呼吸边距)。
const CARD_X = 220 + 36 + 524 + 28; // ≈ 808
const CARD_Y = 72 + 36; // 108
const CARD_W = 524;
const CARD_H = 425;
const PAD = 14; // 咬合后角标与卡的呼吸距
const TCX = CARD_X + CARD_W / 2;
const TCY = CARD_Y + CARD_H / 2;
const FLY_IN = 14; // 飞入开始
const FLY_END = 24; // 飞入结束(大框就位)
const LOCK_END = 46; // 咬合帧
const LABEL_END = 56;
const ARM = 56; // L 臂长
const THICK = 10; // L 粗
const clamp = { extrapolateLeft: 'clamp' as const, extrapolateRight: 'clamp' as const };
export const ReticleLockOn: React.FC = () => {
const frame = useCurrentFrame();
// 飞入:整框从右下画外冲进来,同时是个放大 2.2× 的大框
const flyT = interpolate(frame, [FLY_IN, FLY_END], [0, 1], {
easing: Easing.out(Easing.cubic),
...clamp,
});
const flyDX = (1 - flyT) * 1100;
const flyDY = (1 - flyT) * 620;
// 收缩咬合:半宽/半高从 2.2× 收到贴紧,带超调(收过头 6% 再弹回)
const shrink = interpolate(
frame,
[FLY_END, LOCK_END - 8, LOCK_END - 3, LOCK_END],
[2.2, 1.06, 0.94, 1],
{ easing: Easing.inOut(Easing.cubic), ...clamp },
);
const hw = (CARD_W / 2 + PAD) * shrink;
const hh = (CARD_H / 2 + PAD) * shrink;
// 咬合帧:卡片微亮(一层白色 overlay 快闪后停在 0.28)
const glow = interpolate(frame, [LOCK_END, LOCK_END + 3, LOCK_END + 10], [0, 0.55, 0.28], clamp);
// 标签:咬合后从角标右上弹出(scaleX 展开 + 淡入)
const labelT = interpolate(frame, [LOCK_END + 2, LABEL_END], [0, 1], {
easing: Easing.out(Easing.back(1.8)),
...clamp,
});
const showReticle = frame >= FLY_IN;
const locked = frame >= LOCK_END;
// 四个 L 角:位置 = 中心 ± (hw, hh),各自镜像
const corners = [
{ x: TCX - hw, y: TCY - hh, sx: 1, sy: 1 },
{ x: TCX + hw, y: TCY - hh, sx: -1, sy: 1 },
{ x: TCX - hw, y: TCY + hh, sx: 1, sy: -1 },
{ x: TCX + hw, y: TCY + hh, sx: -1, sy: -1 },
];
return (
<div style={{ width: 1920, height: 1080, background: G.bg, overflow: 'hidden', position: 'relative' }}>
<FakeDashboard variant="A" />
{/* 咬合微亮层:条件渲染,锁定前不存在 */}
{locked && (
<div
style={{
position: 'absolute',
left: CARD_X,
top: CARD_Y,
width: CARD_W,
height: CARD_H,
borderRadius: 14,
background: '#ffffff',
opacity: glow,
pointerEvents: 'none',
}}
/>
)}
{showReticle && (
<div style={{ position: 'absolute', left: 0, top: 0, transform: `translate(${flyDX}px, ${flyDY}px)` }}>
{corners.map((c, i) => (
<div
key={i}
style={{
position: 'absolute',
left: c.x,
top: c.y,
transform: `scale(${c.sx}, ${c.sy})`,
transformOrigin: '0 0',
}}
>
{/* L 形角标:横臂 + 竖臂 */}
<div style={{ position: 'absolute', left: 0, top: 0, width: ARM, height: THICK, background: G.ink }} />
<div style={{ position: 'absolute', left: 0, top: 0, width: THICK, height: ARM, background: G.ink }} />
</div>
))}
{/* 小标签:右上角标外侧弹出 */}
{frame >= LOCK_END + 2 && (
<div
style={{
position: 'absolute',
left: TCX + hw + 18,
top: TCY - hh - 6,
transform: `scale(${labelT})`,
transformOrigin: '0 50%',
background: G.ink,
color: '#fff',
fontFamily: 'Helvetica, Arial, sans-serif',
fontWeight: 700,
fontSize: 26,
padding: '10px 18px',
borderRadius: 8,
whiteSpace: 'nowrap',
opacity: Math.min(1, labelT * 1.5),
}}
>
TARGET · CARD_02
</div>
)}
</div>
)}
</div>
);
};
- 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/fui-hud-moves/LineUnfoldPanel.tsx, demos/effects/fui-hud-moves/ReticleLockOn.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.