Motion · Transition
Line carry transition
Scene A's progress bar extends off-frame, the camera pans with the line, and the line turns a corner mid-move to frame scene B's card border, with no cut anywhere.
Source: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Remotion composition (React/TSX) — preview video, no in-browser player
When to use
Between two scenes with graphic kinship (a progress bar → a card frame, an underline → a chart axis); the signature transition slot of a film, the graphic-relay opening of Catch Me If You Can.
Intention
The three transition families in the library all answer "how does the scene change"; this card's answer is it fundamentally doesn't change — one line leads you across. Once the progress bar fills, its end extends into a long line that shoots off the card's edge, the camera pans with the line into the new world, the line turns right angles mid-move to frame scene B's card, the frame closes, content fades in — the audience's eye never leaves the line from start to finish. Graphic continuity is itself the transition. The difference from a match-cut: that aligns shapes on either side of a cut — it's still a cut; this is continuous drawing — there is no cut. The line needs a narrative logic for its identity; a line with no justification is mere decoration.
Duration & energy
- Duration: ~5.3s (progress bar fills + pan 60f + framing + content fade-in + 36f hold)
- Energy: Medium
Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)
意图
转场库三大族(穿越/藏切/体块)都在回答"场景怎么换";本卡的回答是 根本不换——一条线牵着你走过去。进度条走满后末端延伸成长线冲出 卡缘,镜头跟着线横移 1920px 进入新世界,线在移动中拐直角围出 B 场景 的卡框,框闭合、内容淡入,从头到尾观众的眼睛没离开过这条线。图形 连续性本身就是转场。与 match-cut 的区别:那是切点两侧形状对位—— 终究还是切;这是连续绘制——没有切。线的身份要有叙事逻辑(进度条 "走完了"所以延伸出去),无来由的线只是装饰。
动效核心
- 3840 宽世界容器 translateX 当镜头,34–94f 移 1920px,
Easing.inOut(cubic)
- 一条手写折线 path(进度条 560 + 冲出段 + 直角 + 560×330 矩形框,
总长 3980)全程 dasharray/dashoffset evolve,分段驱动 drawn 长度
- 命门:横移段 drawn = 1100 + cam,线生长与镜头位移同速——笔头
钉在画面 x≈1500 偏右,永不出画也永不落后;一旦失速,观众跟丢线 就等于跟丢了转场
- 笔头 r=11 墨点随线走,框闭合后 112–118f 线性消散并条件卸载
(摘罩判例);B 内容 124f 前淡入完,124–160f 真静止 36f
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 线体 | 6px ink 实线 | 太细横移中读丢;虚线读作未完成态 | | 横移配比 | 1920px / 60f(32px/f) | 更远的横移等比拉长时长,别提速——>40px/f 笔头开始拖影跟丢 | | 笔头位置 | 画面 x≈1500(约 78% 处偏右) | 居中读作"线追镜头";贴边一颤就出画 | | 拐角 | 直角硬拐,不倒圆 | 圆角软拐丢掉制图感,Saul Bass 味就没了 | | 框闭合 | 闭合帧起 B 内容再淡入 | 框没闭合内容先出,线的悬念白搭 | | 收尾 | 淡入完 ≥36f 真静止 | 笔头墨点必须条件卸载,残留即毁静止 |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验
- 与 draw-svg-trace 的关系:那是元素级描边点名(线属于元素),这是
场景级线条接力(线属于镜头)——同片可共存,但线的语义要一致: 同为 ink 6px 手绘感,或同为几何硬线,混用读作两套美术
- 全片 ≤1 次——这是招牌转场不是常规转场,第二次出现魔法就穿帮
- 起点必须是已有图形构件的自然延伸(进度条走满、下划线画完),
凭空起线读作屏保
- 声音:线生长一声持续笔擦、框闭合一声轻叩(sound-design §4.5)
参考实现
demos/transition/line-carry-transition/ (LineCarryTransition.tsx)
Reference implementation (Remotion)
- demos/transition/line-carry-transition/LineCarryTransition.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/line-carry-transition/LineCarryTransition.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/line-carry-transition.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/transition/line-carry-transition/LineCarryTransition.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("line-carry-transition")Prompt
Use the video-shotcraft shot recipe "Line carry transition" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/line-carry-transition.json Raw TSX: https://gimgs.net/motion/line-carry-transition/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/transition/line-carry-transition/LineCarryTransition.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/transition/line-carry-transition/LineCarryTransition.tsx =====
// line-carry-transition|线条接力横移转场(Catch Me If You Can 图形接力)
// 世界宽 3840(A 左半 / B 右半)。0–24f 卡 A 底部 6px ink 进度条走满;
// 24–34f 进度条末端延伸成横线冲出卡右缘;34–94f 镜头整体左移 1920px
// (Easing.inOut(cubic),60f),线与镜头同速延伸,笔头始终在画面偏右;
// 94–112f 线拐直角围出 560×330 卡框(一条 path 全程 evolve,dashoffset 生长);
// 112–124f 框闭合后 B 卡内容淡入 12f。124–160f 真静止 36f ≥ 35f。
// 帧确定,无随机;笔头墨点 118f 起条件卸载(摘罩判例)。
import React from 'react';
import { AbsoluteFill, useCurrentFrame, interpolate, Easing } from 'remotion';
import { G, Card, TitleBlock } from '../../_fixtures/Fixtures';
// ---- 世界几何(一条折线:进度条 + 横线 + 直角 + 矩形框)----
// M 400,705 → 2600,705(进度 560 + 冲出 1640)→ 上 2600,375 → 右 3160,375
// → 下 3160,705 → 左回 2600,705 闭合。总长 2200+330+560+330+560 = 3980。
const PATH = 'M 400 705 L 2600 705 L 2600 375 L 3160 375 L 3160 705 L 2600 705';
const SEGS: Array<[number, number, number, number, number]> = [
[400, 705, 2600, 705, 2200],
[2600, 705, 2600, 375, 330],
[2600, 375, 3160, 375, 560],
[3160, 375, 3160, 705, 330],
[3160, 705, 2600, 705, 560],
];
const TOTAL = 3980;
// 笔头坐标:按已画长度沿折线取点
const tipAt = (drawn: number): [number, number] => {
let d = Math.max(0, Math.min(drawn, TOTAL));
for (const [x1, y1, x2, y2, len] of SEGS) {
if (d <= len) {
const t = d / len;
return [x1 + (x2 - x1) * t, y1 + (y2 - y1) * t];
}
d -= len;
}
return [2600, 705];
};
export const LineCarryTransition: React.FC = () => {
const frame = useCurrentFrame();
// 镜头:34–94f 左移 1920px,inOut cubic
const cam = interpolate(frame, [34, 94], [0, 1920], {
easing: Easing.inOut(Easing.cubic),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// 已画长度:三段接力(进度条 → 冲出 → 与镜头同速 → 收框)
let drawn: number;
if (frame < 24) {
drawn = interpolate(frame, [0, 24], [0, 560], {
easing: Easing.out(Easing.cubic),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
} else if (frame < 34) {
drawn = interpolate(frame, [24, 34], [560, 1100], {
extrapolateRight: 'clamp',
});
} else if (frame < 94) {
drawn = 1100 + cam; // 与镜头同速延伸,笔头稳在画面偏右
} else {
drawn = interpolate(frame, [94, 112], [3020, TOTAL], {
easing: Easing.out(Easing.cubic),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
}
// B 卡内容:框闭合(112f)后淡入 12f
const contentOpacity = interpolate(frame, [112, 124], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// 笔头墨点:全程随笔走,112–118f 线性消散,118f 起条件卸载
const tipMounted = frame < 118;
const tipOpacity = interpolate(frame, [112, 118], [1, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const [tx, ty] = tipAt(drawn);
return (
<AbsoluteFill style={{ background: G.bg, overflow: 'hidden' }}>
{/* 世界容器:3840 宽,整体横移 = 镜头跟线走 */}
<div
style={{
position: 'absolute',
width: 3840,
height: 1080,
transform: `translateX(${-cam}px)`,
}}
>
{/* B 半世界底色略浅,卖出"新世界" */}
<div style={{ position: 'absolute', left: 1920, top: 0, width: 1920, height: 1080, background: G.panel }} />
{/* 场景 A:标题 + 卡 + 进度条轨道(ink 填充即 path 本体) */}
<div style={{ position: 'absolute', left: 400, top: 250 }}>
<TitleBlock text="Scene A" size={56} />
</div>
<Card w={560} h={330} seed={2} style={{ position: 'absolute', left: 400, top: 350 }} />
<div style={{ position: 'absolute', left: 400, top: 702, width: 560, height: 6, borderRadius: 3, background: G.line }} />
{/* 一条线全程 evolve:dasharray/dashoffset 生长 */}
<svg width={3840} height={1080} style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>
<path
d={PATH}
fill="none"
stroke={G.ink}
strokeWidth={6}
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray={TOTAL}
strokeDashoffset={TOTAL - drawn}
/>
{tipMounted && <circle cx={tx} cy={ty} r={11} fill={G.ink} opacity={tipOpacity} />}
</svg>
{/* 场景 B:框(2600,375–3160,705)由线画成,内容淡入 */}
<div
style={{
position: 'absolute',
left: 2600,
top: 375,
width: 560,
height: 330,
boxSizing: 'border-box',
padding: 26,
display: 'flex',
flexDirection: 'column',
gap: 14,
opacity: contentOpacity,
}}
>
<div style={{ height: 18, width: '58%', background: G.bar, borderRadius: 9 }} />
<div style={{ height: 11, width: '86%', background: G.line, borderRadius: 5 }} />
<div style={{ height: 11, width: '72%', background: G.line, borderRadius: 5 }} />
<div style={{ height: 11, width: '64%', background: G.line, borderRadius: 5 }} />
<div style={{ marginTop: 'auto', display: 'flex', gap: 10, alignItems: 'center' }}>
<div style={{ width: 30, height: 30, borderRadius: 15, background: G.mid }} />
<div style={{ height: 11, width: 90, background: G.line, borderRadius: 5 }} />
</div>
</div>
<div style={{ position: 'absolute', left: 2600, top: 275, opacity: contentOpacity }}>
<TitleBlock text="Scene B" size={56} />
</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/transition/line-carry-transition/LineCarryTransition.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

White flash: logo simplifies
A colorful liquid-gradient wordmark holds with flowing light, then the frame flashes white and overexposes; a flat wordmark fades in on white and settles — one flash completes the downgrade to simplicity.

Geometric wipe transitions
Two geometric wipe transitions — clock-wipe, a radar-hand sweep changing the page, and blinds-slice, 12 vertical strips flipping in staggered waves.

Hidden-cut transitions
Three hidden-cut transitions — foreground occlusion invisible cut, versus-slam opener, and warm light-leak burn; the hard cut hides inside 1-3 frames of occlusion/impact/light peak, so the viewer never sees the scissors.

Travel-through transitions
Two travel-through transitions — shared-element homecoming and letter-cavity pass-through; the camera drills through a real element inside the frame to complete the scene change.