Motion · Light & emphasis
Scan bracket sweep
A skeleton document pops in, viewfinder brackets frame it, and a scan line sweeps it 5 times while it stays completely still.
Source: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Remotion composition (React/TSX) — preview video, no in-browser player
When to use
A process shot for "analyzing/validating this content"; a capability demo for document products; the middle beat of an upload → analyze pipeline
Intention
Let the viewer read "this thing is being read through line by line by a machine." The viewfinder brackets frame the target first (announcing the inspection subject), then the scan line sweeps back and forth across the whole document (announcing the inspection process). The critical rule is that the document itself stays completely still — the moment the document also moves, the viewer can no longer tell if it's "being inspected" or "loading."
Duration & energy
- Duration: About 5.0s (150f@30fps)
- Energy: Medium-low (mechanical, restrained, the rhythm lives entirely in the breathing of the back-and-forth sweep)
Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)
意图
让观众看懂"这份东西正在被机器逐行读过"。取景括号先把目标框住(宣布检查 对象),扫描光带再往复走完全篇(宣布检查过程)。命门是文档本身完全静止: 一旦文档也在动,观众就分不清是"被检查"还是"在加载"。
动效核心
- 文档
seg(0→0.11, outCubic)从 scale 0.86 弹到 1,opacity 走dp*3
提前满亮,落位比缩放早——先"在了"再"稳了"
- 四角 34px 的 L 形括号按
i*0.022stagger 依次落位,各 0.055 走完,
沿内外对角带 8px 位移收敛((1-p)*8*dx/dy)
- 扫描窗口
t 0.17→0.95严格 linear 切成 5 趟,趟内inOutSine
两端慢中间快,末尾留 12% 完全停顿(local=(raw-pi)/0.88)
- 拖尾 82px、
rgba(20,20,22,.5)→0,方向随dir翻转:下行时挂在光带
上方(top:-82px)、上行时挂在下方(top:2.5px),渐变同步反向
- 整条光带裁在与文档同 10px 圆角的
overflow:hidden层里,
0.16→0.20 淡入、0.93→0.99 淡出
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 文档弹入 | scale 0.86→1,t 0→0.11 outCubic | 起始值低于 0.8 就成了"从远处飞来",弹入要小幅度才像 UI | | 取景括号 | 臂长 34px、描边 2px、外扩 −7px;stagger 0.022 / 各 0.055 | 四角同时出现读作静态边框;差 0.022 就够读出"依次落下" | | 扫描趟数 | PASSES=5,窗口 t 0.17→0.95 linear | 3 趟不够读作"往复",7 趟以上在 5s 内每趟太快 | | 趟内缓动 | inOutSine + 末尾 12% 停顿 | 停顿是"看清了这一趟"的换气位;去掉就成了匀速来回摆 | | 光带 | 2.5px 实线 + 82px 拖尾 rgba(20,20,22,.5)→0 | 线太粗(>4px)读作遮罩边缘而不是光 | | 拖尾方向 | 下行 top:−82px / 上行 top:2.5px,渐变一并翻转 | 只翻其一 → 拖尾跑到运动前方,读作"光在往回吸" | | 裁切层 | 与文档同 10px 圆角 overflow:hidden | 漏到文档外就是"扫画面"不是"扫文档" |
已知坑
- 光带必须裁在文档 bbox 内,且圆角与文档一致——漏出一点就丢掉"扫描
这份文档"的语义
- 拖尾的
top和 gradient 方向是两处独立写法,改扫描方向时必须同改,
漏一处就出现"尾迹在前"的反物理观感
- 括号的 34px 臂长与 −7px 外扩是按 300×178 的文档尺寸配的;换真实截图
要按新尺寸重算,臂长超过短边 1/4 就压住内容
- INK 是唯一的颜色变量(括号 + 光带 + 拖尾共用),换肤只改它
参考实现
demos/effects/scan-bracket-sweep/ (ScanBracketSweep.tsx)
Reference implementation (Remotion)
- demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/scan-bracket-sweep/ScanBracketSweep.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/scan-bracket-sweep.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/effects/scan-bracket-sweep/ScanBracketSweep.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("scan-bracket-sweep")Prompt
Use the video-shotcraft shot recipe "Scan bracket 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/scan-bracket-sweep.json Raw TSX: https://gimgs.net/motion/scan-bracket-sweep/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/effects/scan-bracket-sweep/ScanBracketSweep.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/effects/scan-bracket-sweep/ScanBracketSweep.tsx =====
// scan-bracket-sweep — Scan Bracket 取景括号扫描光带(motion-lab 定稿转原生 Remotion)
// 骨架文档弹到画面中央,四角落下黑色 L 形取景括号(向内位移 8px),随后一条 2.5px
// 黑实线带着 80px 深灰→透明拖尾在文档上往复扫 5 趟,两端慢中间快,尾迹方向始终朝
// 运动来向,文档本身完全静止。设计坐标 480×270(DesignStage 等比放大)。
import React from 'react';
import { DesignStage, E, lerp, rand, seg, useT } from '../../_fixtures/Motion';
export const SCAN_BRACKET_SWEEP_DURATION = 150; // 5000ms @30fps
// —— b09 批共用量(源自 motion-lab/fx/b09.js,按规则内联,不 export)——
const BG = '#F1F1F3'; // 页面浅灰
const INK = '#0B0B0C'; // 纯黑
const LINE = '#E6E6EA'; // 描边
const SKEL = '#DEDEE3'; // 骨架灰
const clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);
const inOutSin = (x: number) => 0.5 - Math.cos(Math.PI * clamp01(x)) / 2;
// 文档几何:440×240 定尺画布内居中的 300×178 骨架文档
const DW = 300;
const DH = 178;
const DX = (440 - DW) / 2;
const DY = (240 - DH) / 2;
// 骨架列参数(与 effect.js mkDoc 完全一致:4 列 ×7 行,rand 种子相同可逐条复现)
const COLS = 4;
const COL_W = (DW - 28 - (COLS - 1) * 10) / COLS;
const SKEL_COLS = Array.from({ length: COLS }, (_, c) => ({
x: 14 + c * (COL_W + 10),
rows: Array.from({ length: 7 }, (_, r) => ({
top: 58 + r * 13,
w: Number((COL_W * (0.55 + rand(c * 13 + r * 7) * 0.45)).toFixed(1)),
})),
}));
// 四角取景括号:L 形两边 2px 黑边,落位时从外侧 8px 位移收进来
const CS = 34;
const CB = `2px solid ${INK}`;
const CORNERS: { pos: React.CSSProperties; dx: number; dy: number }[] = [
{ pos: { left: -7, top: -7, borderLeft: CB, borderTop: CB }, dx: 1, dy: 1 },
{ pos: { right: -7, top: -7, borderRight: CB, borderTop: CB }, dx: -1, dy: 1 },
{ pos: { right: -7, bottom: -7, borderRight: CB, borderBottom: CB }, dx: -1, dy: -1 },
{ pos: { left: -7, bottom: -7, borderLeft: CB, borderBottom: CB }, dx: 1, dy: -1 },
];
const PASSES = 5;
export const ScanBracketSweep: React.FC = () => {
const t = useT();
// 文档弹入:scale 0.86→1 + 快速淡入
const dp = seg(t, 0, 0.11, E.outCubic);
// 扫描往复:5 趟,每趟末尾留 12% 停顿,两端慢中间快(inOutSine)
const sp = seg(t, 0.17, 0.95, E.linear);
const raw = sp * PASSES;
const pi = Math.min(PASSES - 1, Math.floor(raw));
const local = clamp01((raw - pi) / 0.88);
const dir = pi % 2 === 0 ? 1 : -1;
const prog = inOutSin(local);
const y = dir > 0 ? prog * DH : DH - prog * DH;
// 光带整体淡入(0.16–0.2)淡出(0.93–0.99)
const clipOp = Number((seg(t, 0.16, 0.2) * (1 - seg(t, 0.93, 0.99))).toFixed(3));
return (
<DesignStage bg={BG}>
{/* 440×240 定尺画布(mkSheet),居中于 480×270 设计坐标 */}
<div
style={{
position: 'absolute',
left: '50%',
top: '50%',
width: 440,
height: 240,
margin: '-120px 0 0 -220px',
}}
>
{/* holder:文档 + 光带 + 括号共用的定位容器 */}
<div style={{ position: 'absolute', left: DX, top: DY, width: DW, height: DH }}>
{/* 骨架文档卡片(原采集页为 content-box:1px 边框长在 300×178 之外) */}
<div
style={{
position: 'absolute',
left: 0,
top: 0,
width: DW,
height: DH,
boxSizing: 'content-box',
background: '#fff',
border: `1px solid ${LINE}`,
borderRadius: 10,
boxShadow: '0 8px 26px rgba(0,0,0,.06)',
overflow: 'hidden',
transformOrigin: '50% 50%',
transform: `scale(${lerp(dp, 0.86, 1)})`,
opacity: clamp01(dp * 3),
}}
>
<div style={{ position: 'absolute', left: 0, top: 0, right: 0 }}>
{/* 标题条 + 副标题条 */}
<div
style={{
position: 'absolute',
left: 14,
top: 12,
width: Math.round(DW * 0.34),
height: 7,
borderRadius: 3,
background: INK,
opacity: 0.85,
}}
/>
<div
style={{
position: 'absolute',
left: 14,
top: 25,
width: Math.round(DW * 0.2),
height: 5,
borderRadius: 3,
background: SKEL,
}}
/>
{/* 4 列骨架:列头 + 7 行随机宽占位条 */}
{SKEL_COLS.map((col, c) => (
<React.Fragment key={c}>
<div
style={{
position: 'absolute',
left: col.x,
top: 44,
width: Number((COL_W * 0.72).toFixed(1)),
height: 6,
borderRadius: 3,
background: '#9A9AA2',
}}
/>
{col.rows.map((row, r) => (
<div
key={r}
style={{
position: 'absolute',
left: col.x,
top: row.top,
width: row.w,
height: 5,
borderRadius: 2.5,
background: SKEL,
}}
/>
))}
</React.Fragment>
))}
</div>
</div>
{/* 扫描光带(裁在文档圆角内):2.5px 黑实线 + 82px 朝来向的渐变拖尾 */}
<div
style={{
position: 'absolute',
left: 0,
top: 0,
width: DW,
height: DH,
borderRadius: 10,
overflow: 'hidden',
pointerEvents: 'none',
opacity: clipOp,
}}
>
<div
style={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
height: 0,
transform: `translateY(${y.toFixed(2)}px)`,
}}
>
<div
style={{
position: 'absolute',
left: 0,
right: 0,
height: 82,
top: dir > 0 ? -82 : 2.5,
background:
dir > 0
? 'linear-gradient(180deg,rgba(20,20,22,0),rgba(20,20,22,.5))'
: 'linear-gradient(180deg,rgba(20,20,22,.5),rgba(20,20,22,0))',
}}
/>
<div
style={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
height: 2.5,
background: INK,
}}
/>
</div>
</div>
{/* 四角取景括号:交错落位,向内收 8px(原页 content-box,2px 边长在 34×34 之外) */}
{CORNERS.map((c, i) => {
const p = seg(t, 0.08 + i * 0.022, 0.08 + i * 0.022 + 0.055, E.outCubic);
return (
<div
key={i}
style={{
position: 'absolute',
width: CS,
height: CS,
boxSizing: 'content-box',
opacity: p,
transform: `translate(${(1 - p) * 8 * c.dx}px,${(1 - p) * 8 * c.dy}px)`,
...c.pos,
}}
/>
);
})}
</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/effects/scan-bracket-sweep/ScanBracketSweep.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.