Motion · Chuyển cảnh
Chuyển cảnh xuyên qua
Hai kiểu chuyển cảnh xuyên qua — trở về vị trí bằng phần tử chung và xuyên khoang chữ, máy quay khoan xuyên phần tử thực trong khung hình để hoàn tất đổi cảnh.
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ể
Thẻ cận cảnh toàn màn hình co lại + dịch chuyển + mọc góc bo tròn, bay khít vào đúng ô lưới của nó trong dashboard, đậu xuống với 3% overshoot
Ngữ nghĩa "trở về vị trí" của chi tiết→tổng quan; dùng cặp với kiểu F xuyên cửa sổ (vào cửa sổ/về vị trí)
Khoang chữ của tiêu đề khổng lồ (khoét bằng SVG mask) để lộ trang mới qua đó, cú đẩy theo hàm mũ khoan xuyên khoang chữ, khoảnh khắc khoang đầy khung thì tiếp quản, nét chữ còn lại văng ra ngoài khung
Chuyển từ tiêu đề chương sang nội dung; phương án năng lượng cao thay cho thẻ chữ nền đen của kiểu D
Khi nào dùng
Mối nối nơi hai cảnh có quan hệ không gian cấp "phần tử/khung chứa" (thẻ kỹ thuật, dùng bổ trợ với sáu kiểu shot-transitions)
Ý đồ
Sáu kiểu của shot-transitions thuộc dạng "trao tay" — cảnh trước kết thúc, cảnh sau vào, mối nối được che bằng chớp trắng/giữ tối/lấy nét/màn đen/khung mờ/cửa sổ. Hai kiểu của thẻ này thuộc dạng "xuyên qua" — hậu cảnh đã "có mặt sẵn" như phần tử thực trong khung tiền cảnh (ô lưới/khoang hình chữ), máy quay thực hiện một chuyển động không gian liên tục khoan vào đó, khán giả bám theo cùng một vật thể từ đầu đến cuối, không có khoảnh khắc trao tay. Nguyên tắc chọn: hậu cảnh có thể "có mặt sẵn" trong tiền cảnh → chọn xuyên qua; hai cảnh chỉ có quan hệ trước sau, không lồng không gian → chọn trao tay. Hai kiểu còn chia nhỏ theo ngữ nghĩa — A là "đặt thẻ này trở lại vị trí" (chi tiết→tổng quan, chiều ngược của kiểu F xuyên cửa sổ); C là "tiêu đề chính là cánh cửa" (thẻ chữ chương và chuyển cảnh gộp làm một).
Thời lượng & năng lượng
- Thời lượng: n/a (thẻ kỹ thuật; đoạn động tác mỗi kiểu 25–60f, giữ khung trước/sau tính riêng, số khung trích từ ngân sách khung cảnh lân cận)
- Năng lượng: n/a (thẻ kỹ thuật, không chiếm ô năng lượng)
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)
意图
shot-transitions 六式是"交棒"型——前镜收尾、后镜入场,接缝靠白闪/暗场/ 焦点/黑场/糊帧/窗口盖住换页瞬间。本卡两式是"穿越"型——后景在前景画面里 以真实元素存在(网格槽位/字形洞),镜头一条连续空间运动钻进去, 观众从头到尾跟着同一个物体,没有交棒瞬间。选型口径:后景能在前景里"预先 在场"→穿越;两景只是先后关系、无空间嵌套→交棒。两式内部再按语义分—— A 是"把这张卡放回去"(详情→总览,F 式穿窗的逆向);C 是"标题即是门" (章节字卡与转场二合一)。
两式选型表
| 式 | 做法 | 适用接缝 | |----|------|----------| | A 共享元素归位 shared-element-morph | 全屏特写卡收缩+位移+长出圆角,严丝合缝飞落进 dashboard 网格所属槽位,3% 过冲落座 | 详情→总览的"归位"语义;与 F 式穿窗成对使用(进窗/回位)| | C 字腔穿越 letterform-zoom | 巨型标题字腔(SVG mask 挖洞)透出新页,指数推进穿洞,洞撑满瞬间接管、残余笔画甩出画外 | 章节标题→正文;替代 D 式黑场字卡的高能量款 |
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | A morph 主曲线 | 25f bezier(0.4,0,0.2,1) 冲到 1.03 过冲 + 10f Easing.out(cubic) 弹回 1;位置/尺寸/圆角共用同一条进度 p | 三属性各走各的曲线立刻读作"变形"而非"同一物体";过冲 >5% 读作果冻 | | A 同一物体感 | 内容按槽位尺寸渲染、随容器等比缩放(scale 3.66→1,origin 左上);投影随 p 从 0/36/110/0.32 收敛到 0/2/8/0.06 | 内容重排版=换了张卡,穿越感断;投影不收敛则卡永远悬空、落不了座 | | A 落座仪式 | 背景 dashboard 0.9 透明度待命,落座瞬间 5f 提到 1 | 背景全程满亮则飞落没有"到家"的一拍;提亮窗口跨骑落座帧前后 | | C 穿腔主推 | 盖板 SVG mask(白底+黑字 900 字重)挖洞,transform-origin 对准腔心(demo:"DASH" A 腔 ≈773,508);scale=28^t,t 60f bezier(0.6,0,0.85,0.5) 慢起陡收 | ZOOM_MAX 按"洞撑满画面"配,字腔越小倍数越大;慢起段留给观众看清"洞里有东西" | | C 撤场与呼应 | scale∈[15,24] 盖板 opacity 1→0(残余笔画边飞边撤);期望模糊量 0→16px 除以 scale 写进 blur 补偿;洞内 B 层微 dolly 1→1.1、接管后 25f 落回 1 | blur 不除 scale 会被 transform 放大成糊墙;B 不跟着 dolly 则洞里是张死图,"穿进活页面"的感知断掉 |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验;灰阶测不出纹理级缺陷(轮 A 判例),真实素材 上先出静帧自检
- C 式腔心坐标是字体度量估算(字宽 em 值手推),换字体/换词/渲染器字距
不同都会漂——渲后看第一帧推进方向,洞不居中就微调 origin,别信算出来的数
- A 式槽位坐标必须精确到像素(demo 里从 fixtures 布局逐项推算 808,590,
524x454,r14)——差几像素落座后再对齐,观众读作"飞到附近再吸附", "归位"的严丝合缝感全无
- 穿越两式与交棒六式同一接缝不叠加,一缝一式;穿越式已自带高能量,
再包白闪/震屏读作穿帮(规矩同 shot-transitions)
参考实现
demos/transition/transition-travel/ (LetterformZoom.tsx / SharedElementMorph.tsx)
Mã nguồn tham chiếu (Remotion)
- demos/transition/transition-travel/LetterformZoom.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/transition-travel/LetterformZoom.tsx
- demos/transition/transition-travel/SharedElementMorph.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/transition-travel/SharedElementMorph.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/transition-travel.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/transition/transition-travel/LetterformZoom.tsx, demos/transition/transition-travel/SharedElementMorph.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("transition-travel")Prompt
Use the video-shotcraft shot recipe "Chuyển cảnh xuyên qua" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/transition-travel.json Raw TSX: https://gimgs.net/motion/transition-travel/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/transition/transition-travel/LetterformZoom.tsx, demos/transition/transition-travel/SharedElementMorph.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 4.39s 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/transition-travel/LetterformZoom.tsx =====
import React, { useId } from 'react';
import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';
import { FakeDashboard, G } from '../../_fixtures/Fixtures';
// letterform-zoom〔转场〕:巨型标题 "DASH" 字腔透出新页面,镜头急速推进
// 字母 A 的三角字腔,洞被撑满全屏的瞬间新页面接管,残余笔画滑出画外。
// 结构:底层 FakeDashboard B 静置全屏(微 dolly);上层"米灰盖板"用
// SVG <mask>(白底 + 黑字)在字形处挖洞——洞里即透出 B;对盖板整组做
// transform-origin 对准 A 字腔中心的指数 scale 1→28(60f,前 20f 慢
// 后 40f 陡)。scale 过临界值后盖板快速退场 = 撤掉 mask,B 全屏接管。
const FS = 560; // 标题字号
// A 字腔(三角洞)中心:Helvetica Bold 度量估算——
// "DASH" 总宽 ≈ 2.833em,居中起点 x≈167,A 占 571–975 → 腔心 x≈773;
// 基线 y=741(大写居中),腔心约在基线上方 0.42em → y≈508。
const ORIGIN = { x: 773, y: 508 };
const BASELINE = 741;
const ZOOM_MAX = 28;
const titleFont: React.CSSProperties = {
fontFamily: 'Helvetica Neue, Helvetica, Arial, sans-serif',
fontWeight: 900,
fontSize: FS,
};
export const LetterformZoom: React.FC = () => {
const frame = useCurrentFrame();
// mask ID 按实例生成,多实例同场不串引(useId 的 «:» 在 url() 里非法,需清洗)
const cutId = `lfz-cut-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
// 0–25f 建立 hold;25–85f 推进。慢起 bezier 叠指数尺度 = 前段慢、后段陡
const t = interpolate(frame, [25, 85], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.bezier(0.6, 0, 0.85, 0.5),
});
const scale = Math.pow(ZOOM_MAX, t); // 指数推进:等比的"穿越"速度感
// scale 过临界值(洞已撑满画面中部)→ 盖板残余笔画边外飞边撤场(撤 mask)
const plateOpacity = interpolate(scale, [15, 24], [1, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// 速度模糊:期望视觉模糊量随推进升高;CSS filter 会被 transform 放大,
// 故除以 scale 补偿
const visBlur = interpolate(t, [0, 0.45, 1], [0, 1.5, 16], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const blurCss = visBlur / scale;
// 新页面微 dolly:推进期跟着轻推 1→1.1,接管后 25f 内落定回 1(收势)
const bScale =
frame < 85
? interpolate(t, [0, 1], [1, 1.1])
: interpolate(frame, [85, 110], [1.1, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
return (
<AbsoluteFill style={{ background: G.bg, overflow: 'hidden' }}>
{/* 新页面:先只在字腔里透出,接管后全屏(110–140f 静止收尾) */}
<div
style={{
position: 'absolute',
inset: 0,
transform: `scale(${bScale})`,
transformOrigin: `${ORIGIN.x}px ${ORIGIN.y}px`,
}}
>
<FakeDashboard variant="B" />
</div>
{/* 米灰盖板(字形挖洞)+ 字缘描边 + 副标灰条:整组指数推进后飞出画外 */}
{plateOpacity > 0 && (
<div
style={{
position: 'absolute',
inset: 0,
transform: `scale(${scale})`,
transformOrigin: `${ORIGIN.x}px ${ORIGIN.y}px`,
opacity: plateOpacity,
filter: blurCss > 0.02 ? `blur(${blurCss}px)` : undefined,
}}
>
<svg
width={1920}
height={1080}
viewBox="0 0 1920 1080"
style={{ position: 'absolute', inset: 0, display: 'block' }}
>
<defs>
<mask id={cutId}>
<rect width={1920} height={1080} fill="#fff" />
<text
x={960}
y={BASELINE}
textAnchor="middle"
fill="#000"
style={titleFont}
>
DASH
</text>
</mask>
</defs>
<rect width={1920} height={1080} fill={G.bg} mask={`url(#${cutId})`} />
{/* 字缘细描边:让"洞"的轮廓在米灰上读得清 */}
<text
x={960}
y={BASELINE}
textAnchor="middle"
fill="none"
stroke={G.ink}
strokeWidth={3}
opacity={0.3}
style={titleFont}
>
DASH
</text>
</svg>
{/* 副标灰条:随盖板一起被甩出画外,强化"页面元素残余"感 */}
<div
style={{
position: 'absolute',
left: 0,
right: 0,
top: 812,
display: 'flex',
justifyContent: 'center',
gap: 16,
}}
>
<div style={{ width: 240, height: 16, background: G.bar, borderRadius: 8 }} />
<div style={{ width: 130, height: 16, background: G.line, borderRadius: 8 }} />
</div>
</div>
)}
</AbsoluteFill>
);
};
// ===== demos/transition/transition-travel/SharedElementMorph.tsx =====
import React from 'react';
import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';
import { FakeDashboard, Card, G } from '../../_fixtures/Fixtures';
// shared-element-morph〔转场〕:全屏特写面板收缩、位移、长出圆角,
// 严丝合缝飞落进 dashboard 网格里它所属的卡片槽位,落座带 3% 过冲。
// 观众感觉是同一个物体被镜头送回原位(FLIP 共享元素转场)。
//
// 目标槽位 = FakeDashboard A 中列第二行那格(seed 5),按 fixtures 布局精确推算:
// 侧栏 220 + 网格 padding 36 → 列宽 (1920-220-72-56)/3 = 524
// 头部 72 + padding 36 → 行高 (1008-72-28)/2 = 454
// 中列 x = 220+36+524+28 = 808;第二行 y = 72+36+454+28 = 590
const SLOT = { x: 808, y: 590, w: 524, h: 454, r: 14, seed: 5 };
const FULL = { x: 0, y: 0, w: 1920, h: 1080, r: 0 };
// 节拍:0–35 全屏特写 hold | 35–60 morph 25f(bezier 0.4,0,0.2,1 → 1.03)
// 60–70 过冲回弹落座 | 70–130 静止收尾
const MORPH_START = 35;
const MORPH_END = 60;
const SETTLE_END = 70;
const lerp = (a: number, b: number, t: number) => a + (b - a) * t;
export const SharedElementMorph: React.FC = () => {
const frame = useCurrentFrame();
// 位置/尺寸/圆角共用同一条进度曲线:25f 冲到 1.03,再 10f 弹回 1
const drive = interpolate(frame, [MORPH_START, MORPH_END], [0, 1.03], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.bezier(0.4, 0, 0.2, 1),
});
const settle = interpolate(frame, [MORPH_END, SETTLE_END], [1.03, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
const p = frame <= MORPH_END ? drive : settle;
const x = lerp(FULL.x, SLOT.x, p);
const y = lerp(FULL.y, SLOT.y, p);
const w = lerp(FULL.w, SLOT.w, p);
const h = lerp(FULL.h, SLOT.h, p);
const r = lerp(FULL.r, SLOT.r, p);
// 内容是同一张卡:按目标尺寸渲染,等比放大铺满全屏(全屏 = 这张卡的特写),
// 收缩过程中内容随容器一起缩小,强化"同一个物体"的感知
const contentScale = w / SLOT.w;
// 投影随尺寸缩小:悬空时大而深,落座后收敛到 fixtures 卡片原生投影量级
const ps = Math.min(Math.max(p, 0), 1);
const shadowY = lerp(36, 2, ps);
const shadowBlur = lerp(110, 8, ps);
const shadowAlpha = lerp(0.32, 0.06, ps);
// 背景 dashboard 先 0.9 透明度待命,落座瞬间提到 1
const bgOpacity = interpolate(frame, [MORPH_END - 2, MORPH_END + 3], [0.9, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.quad),
});
return (
<AbsoluteFill style={{ background: G.bg, overflow: 'hidden' }}>
<div style={{ opacity: bgOpacity }}>
<FakeDashboard variant="A" />
</div>
{/* 共享元素:全屏特写 → 精确飞落进槽位 */}
<div
style={{
position: 'absolute',
left: x,
top: y,
width: w,
height: h,
borderRadius: r,
overflow: 'hidden',
background: G.card,
boxShadow: `0 ${shadowY}px ${shadowBlur}px rgba(0,0,0,${shadowAlpha})`,
}}
>
<div
style={{
position: 'absolute',
left: 0,
top: 0,
transform: `scale(${contentScale})`,
transformOrigin: 'top left',
}}
>
<Card
w={SLOT.w}
h={SLOT.h}
seed={SLOT.seed}
style={{ boxShadow: 'none', borderRadius: r / Math.max(contentScale, 0.0001) }}
/>
</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/transition-travel/LetterformZoom.tsx, demos/transition/transition-travel/SharedElementMorph.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

Chớp trắng: logo giản lược
Chữ hiệu gradient lỏng nhiều màu đứng yên chảy sáng, màn hình chớp trắng cháy sáng một nhịp, chữ hiệu phiên bản phẳng trên nền trắng mờ vào định hình — một lần chớp trắng hoàn tất bước giản lược chất liệu.

Chuyển cảnh quét hình học
Hai kiểu chuyển cảnh quét hình học — clock-wipe kim đồng hồ radar quét đổi trang và blinds-slice 12 dải dọc lật lệch nhịp thành sóng.

Chuyển cảnh giấu điểm cắt
Ba kiểu chuyển cảnh giấu điểm cắt — che khuất tiền cảnh cắt vô hình, đối đầu va chạm mở màn, và cháy sáng rò rỉ ánh sáng ấm; cắt cứng giấu trong 1-3 khung che khuất/va chạm/đỉnh sáng, khán giả không thấy kéo cắt.

Xé dải chuyển cảnh
Chuyển cảnh xé rách — glitch-displace xé nhiễu (16 dải ngang lệch rung rồi cắt cứng giữa chừng), xé cấp dải với ngữ nghĩa lỗi kỹ thuật số.