Motion · Light & emphasis
FUI/HUD moves
Two FUI/HUD styles: line-unfold-panel, a single line unfolding into a panel (line-to-plane CRT grammar), and reticle-lock-on, a viewfinder flying in to lock onto a target.
Source: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Remotion composition (React/TSX) — preview video, no in-browser player
Variants
scaleX 0→1 (out poly4, a sharp 5f snap) followed by scaleY 3px→full height (out cubic, 9f), content fades in one beat early; exit mirrors the sequence in reverse.
Panel entrance/exit in dark segments; power-on/off ritual feel
Four L-corners are four mirrored copies of the same rectangle pair; the fly-in (10f out cubic) and the shrink (2.2×→0.94×→1 overshoot rebound) are decoupled; on the lock-on frame the target brightens slightly and a label pops in with back easing.
Target call-out; timed to hit a sound effect
When to use
Use A for panel entrances/exits in dark, tech-flavored segments; use B for any "look here" target call-out (replaces circling/arrow annotation without freezing the frame).
Intention
Two motifs from sci-fi fictional interfaces (Jarvis/Territory) that can be made neutral. A is a panel's ritual power-on/off: a 1px thin line snaps out very fast → stretches vertically into a full panel → content fades in ahead of the beat, exiting in reverse as the line compresses back to a point and dies (old-CRT power-off); a grayscale thin line already works, no tech-blue needed. B is a capture-in-motion: four L-shaped corner marks rush in from off-frame, overshoot and rebound, "clicking" onto the four corners of a target element plus a popped label — division of labor with freeze-annotate: that card freezes the frame to annotate, this one calls things out while things keep flowing.
Duration & energy
- Duration: A: 3–4s (including exit) / B: 2–3s
- Energy: A: Medium / B: Medium-high (the lock-on frame is the hit point)
Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)
意图
科幻虚构界面(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)
Reference implementation (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
The component imports shared fixtures from demos/_fixtures and helpers from assets/lib in the repo; SKILL.md there documents the render workflow.
Everything on this page is also machine-readable. Point your agent at the JSON, or add the gimgs MCP server and ask it to search motion templates.
- Fetch the JSON — it holds the shot recipe (timing, easing, parameter table, pitfalls) and the full TSX source.
- Clone the video-shotcraft repo; the component imports shared fixtures from demos/_fixtures and helpers from assets/lib.
- Register the component as a <Composition> in a Remotion project and render with `npx remotion render`, or re-implement the recipe in your own engine.
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 "FUI/HUD moves" (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 |
|---|---|---|---|
| — | |||
More in this category

Dark-stage spotlight reveals
Three dark-stage spotlight reveals — A wake-sleep sweep (light in = bright, light out = dark), B edge-hugging glow pan (violet light seeps along UI edges + steady rightward sweep), C corner reveal (radial light expands steadily from a corner to light the whole screen); light itself narrates in the dark.

Slam entrance moves
Three high-energy slam entrances: a whip-in perspective stop, a heavy KPI drop, and a shockwave that hits neighboring cards.

Scanline assemble fly-in
A scan line sweeps down an empty grid, flying each region's component in from off-screen — finishing as the scan ends.

Scanline annotate focus
A bright scan line sweeps down the page, framing each element it passes and labeling it with monospace annotations.