Motion · Chuyển cảnh
Chuyển cảnh lật trang
Hai kiểu chuyển cảnh lật cả trang: cube-rotate xoay hai trang như hai mặt hộp kề nhau, barn-door-split tách trang cũ ra như mở cửa.
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ể
Hai trang gắn vào hai mặt kề nhau của khối lập phương (rotateY 0/90° + translateZ W/2), lớp bối cảnh xoay -90°; mặt cũ tối dần khi xoay ra, mặt mới sáng dần khi xoay vào, ở góc 45° hai mặt kẹp một cạnh tối.
Lật chương song song; chuyển đổi giữa hai khu vực tính năng lớn của sản phẩm
Trang cũ tách thành hai khối overflow 960px ghép khớp, cùng trượt ra ngoài khung hình với Easing.in(cubic); mép trong vết nứt có đường sáng + đổ bóng, trang mới bên dưới scale từ 1.06→1 trồi lên đón.
Thay cũ đổi mới: trước/sau khi đổi giao diện, phương án A → phương án B
Khi nào dùng
Đổi trang cấp chương: nghi thức "lật trang" giữa hai đại đoạn song song; phân công với dòng shot-transitions (bàn giao ống kính) — đó là "máy bay quay di chuyển qua", đây là "trang tự nó là một thực thể vật lý"
Ý đồ
Sáu kiểu shot-transitions và transition-travel/hidden-cut đều coi trang là "bối cảnh" — camera di chuyển giữa các bối cảnh. Cảnh này coi trang là một thực thể: nó có độ dày, có trọng lượng, có thể lật mặt, có thể tách ra. Ẩn dụ thực thể mang nghi thức mạnh hơn, phù hợp cho đổi trang lớn cấp chương (tính năng A kết thúc → tính năng B bắt đầu) hơn là bàn giao nhỏ cấp cảnh quay. A, khối lập phương, là "song song trong không gian" — hai trang là hai mặt của một hộp, ngụ ý quan hệ ngang hàng; B, cửa mở đôi, là "cái cũ nhường chỗ" — trang cũ xé từ chính giữa và trượt đi, trang mới trồi lên từ dưới đón vào, ngụ ý thay thế. Chọn theo ngữ nghĩa: lật trang song song dùng A, thay cũ đổi mới dùng B.
Thời lượng & năng lượng
- Thời lượng: Mỗi kiểu: thiết lập trạng thái trước 30f + chuyển cảnh 20–38f + kết 40f trở lên, khoảng 4.4–4.7s
- Năng lượng: Trung bình cao
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 六式和 transition-travel/hidden-cut 都把页面当"场景" ——相机在场景间移动。本卡把页面当实体:它有厚度、有重量、会翻面 会裂开。体块隐喻的仪式感更强,适合章节级大换页(功能 A 讲完→功能 B) 而非镜头级小交棒。A 立方体是"空间上并列"——两页是盒子的两面, 暗示同级关系;B 对开门是"旧的让位"——旧页从正中撕开滑走、新页 从底下迎上来,暗示取代关系。按语义选:并列翻篇用 A,新旧交替用 B。
两式选型
| 式 | 做法 | 适用 | |----|------|------| | A cube-rotate | 两页贴立方体相邻面(rotateY 0/90° + translateZ W/2),场景层转 -90°;旧面转出压暗、新面转进变亮,45° 时两面夹一条暗棱 | 并列章节翻篇;产品两大功能区切换 | | B barn-door-split | 旧页两个 960px overflow 容器对位拼合,同时向外 Easing.in(cubic) 滑出画外;裂缝内边缘亮线+投影,新页底层 scale 1.06→1 迎上 | 新旧交替:改版前后、方案 A→方案 B |
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | A 结构命门 | 场景层 translateZ(−W/2) rotateY(θ) 把正面拉回 z=0 | 少了这层 hold 段页面尺度不对(被 perspective 推远) | | A 转速 | θ 0→−90° / 38f inOut cubic;中段叠 blur 1.5px 落定摘 | <28f 读不出体块感;>50f 读作慢慢挪 | | A 亮度对切 | 旧面 1→0.55、新面 0.55→1 + 接缝竖向渐变阴影 sin(pπ) | 无明暗对切两面读作同一张图在转 | | B 预告 | 裂前中缝 2px 细线闪现两次(18–22/25–29f) | 直接裂开观众没看到裂点在哪 | | B 滑出 | 各 translateX ∓980px / 20f Easing.in(cubic) 加速 | ease-out 滑出是"被推走",ease-in 才是"让位" | | B 撕裂边 | 内边缘 3px G.ink 亮线 + 8px 投影 | 无边线两半读作普通左右擦除 | | 收尾 | 新页落定真静止 ≥40f | R1 |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验
- 一缝一式规矩同 shot-transitions;体块转场比镜头交棒更重,
全片 ≤2 次,连续两缝都用体块读作 PPT 翻页
- A 式官方
@remotion/transitionscube() 是付费件——本卡是 CSS 自实现,
backlog 已有"评估官方转场原语"待选项,若引入官方件可对照
- B 式两半必须对位拼合无缝(右半内层 translateX(−960px))——
静止时看出接缝就穿帮;真实截图注意奇数宽度取整
- 转场前后两页都要有 ≥30f 静止建立/收尾——两端都在动,体块感被
运动淹没
参考实现
demos/transition/page-turn-transitions/ (BarnDoorSplit.tsx / CubeRotate.tsx)
Mã nguồn tham chiếu (Remotion)
- demos/transition/page-turn-transitions/BarnDoorSplit.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/page-turn-transitions/BarnDoorSplit.tsx
- demos/transition/page-turn-transitions/CubeRotate.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/page-turn-transitions/CubeRotate.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/page-turn-transitions.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/transition/page-turn-transitions/BarnDoorSplit.tsx, demos/transition/page-turn-transitions/CubeRotate.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("page-turn-transitions")Prompt
Use the video-shotcraft shot recipe "Chuyển cảnh lật trang" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/page-turn-transitions.json Raw TSX: https://gimgs.net/motion/page-turn-transitions/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/transition/page-turn-transitions/BarnDoorSplit.tsx, demos/transition/page-turn-transitions/CubeRotate.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/transition/page-turn-transitions/BarnDoorSplit.tsx =====
// 对开门裂幕(barn-door-split-reveal)——剪映"开门式"转场。
// FakeDashboard A 从画面正中垂直裂成左右两半:两个 960×1080 overflow:hidden
// 容器各装一份完整 A(右半内层 translateX(-960) 对位拼合),同时向外加速滑
// 出画外,露出底层 FakeDashboard B 从 scale 1.06 轻推到 1.0 迎上来。
// 裂缝各自内边缘 2px G.ink 亮线 + 8px 投影强调"撕开"。
// 关键帧:0–30 静止展示 A(18–22 / 25–29 中缝细线两次闪现预告裂点)→
// 30–50 两半各 translateX ∓980(Easing.in cubic 加速滑出)→
// 30–55 底层 B scale 1.06→1.0(out cubic)→ 55–130 全静止(75f)。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
import { G, FakeDashboard, TitleBlock } from '../../_fixtures/Fixtures';
export const BarnDoorSplit: React.FC = () => {
const frame = useCurrentFrame();
// 两半外滑位移:30–50f,0 → 980px,加速离场
const slide = interpolate(frame, [30, 50], [0, 980], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.in(Easing.cubic),
});
// 底层 B:30–55f 从 1.06 轻推到 1.0 迎上来
const bScale = interpolate(frame, [30, 55], [1.06, 1.0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
// 裂前预告:中缝细线两次闪现(帧确定的开关,无随机)
const crackFlash =
(frame >= 18 && frame < 22) || (frame >= 25 && frame < 29);
// 裂开后内边缘亮线 + 阴影常驻(随门一起滑出画外)
const tornEdge = frame >= 30;
const edgeLine = (side: 'left' | 'right'): React.CSSProperties => ({
position: 'absolute',
top: 0,
[side]: 0,
width: 3,
height: 1080,
background: G.ink,
boxShadow:
side === 'right'
? '-8px 0 14px rgba(0,0,0,0.4)'
: '8px 0 14px rgba(0,0,0,0.4)',
});
return (
<div
style={{
width: 1920,
height: 1080,
background: G.bg,
position: 'relative',
overflow: 'hidden',
}}
>
{/* 底层 FakeDashboard B:scale 1.06 → 1.0 迎上来 */}
<div
style={{
position: 'absolute',
left: 0,
top: 0,
width: 1920,
height: 1080,
transform: `scale(${bScale})`,
transformOrigin: '50% 50%',
}}
>
<FakeDashboard variant="B" />
</div>
{/* 左门:960×1080 视口,装完整 A 的左半 */}
<div
style={{
position: 'absolute',
left: 0,
top: 0,
width: 960,
height: 1080,
overflow: 'hidden',
transform: `translateX(${-slide}px)`,
}}
>
<div style={{ position: 'absolute', left: 0, top: 0, width: 1920, height: 1080 }}>
<FakeDashboard variant="A" />
</div>
{tornEdge && <div style={edgeLine('right')} />}
</div>
{/* 右门:960×1080 视口,内层 translateX(-960) 对位拼合 */}
<div
style={{
position: 'absolute',
left: 960,
top: 0,
width: 960,
height: 1080,
overflow: 'hidden',
transform: `translateX(${slide}px)`,
}}
>
<div
style={{
position: 'absolute',
left: 0,
top: 0,
width: 1920,
height: 1080,
transform: 'translateX(-960px)',
}}
>
<FakeDashboard variant="A" />
</div>
{tornEdge && <div style={edgeLine('left')} />}
</div>
{/* 裂点预告:中缝 2px 细线闪现两次 */}
{crackFlash && (
<div
style={{
position: 'absolute',
left: 959,
top: 0,
width: 2,
height: 1080,
background: G.ink,
}}
/>
)}
<div style={{ position: 'absolute', left: 120, top: 96 }}>
<TitleBlock text="BARN DOOR SPLIT" size={54} />
</div>
</div>
);
};
// ===== demos/transition/page-turn-transitions/CubeRotate.tsx =====
// 立方体空间翻转(cube-rotate)——cube transition 转场自实现。
// FakeDashboard A / B 各缩放 0.82 居中,当立方体相邻两面:perspective 1400px,
// 面宽 W=1920*0.82,两面各 rotateY(面角) translateZ(W/2),场景整体先
// translateZ(-W/2) 再 rotateY(θ)(把正面拉回 z=0,保证 hold 时精确 0.82 尺度)。
// 旧面 A 转出画面 brightness 1→0.55 压暗,新面 B 从 +90° 侧转入 0.55→1 亮起;
// 接缝两侧各一条竖向渐变阴影,随角度 sin(pπ) 加深再消失;转动中段叠 blur(1.5px),
// 落定摘掉(filter 条件挂载,保证收尾逐帧完全相同)。
// 关键帧:0–30 静止展示 A → 30–68 θ 0→-90°(inOut cubic)翻转 → 68–140 B 真静止 72f。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
import { G, FakeDashboard, TitleBlock } from '../../_fixtures/Fixtures';
const S = 0.82;
const W = 1920 * S; // 面宽 = 立方体棱长
const H = 1080 * S;
// 单个立方体面:W×H 视口内放缩放 0.82 的整幅 dashboard
const Face: React.FC<{
variant: 'A' | 'B';
rot: number; // 面自身的 rotateY(A=0,B=90)
brightness: number;
seam: number; // 接缝阴影不透明度 0–1
seamSide: 'right' | 'left'; // 阴影贴在哪条竖边(即共享棱一侧)
}> = ({ variant, rot, brightness, seam, seamSide }) => (
<div
style={{
position: 'absolute',
width: W,
height: H,
overflow: 'hidden',
backfaceVisibility: 'hidden',
transform: `rotateY(${rot}deg) translateZ(${W / 2}px)`,
filter: `brightness(${brightness})`,
background: G.bg,
}}
>
<div style={{ transform: `scale(${S})`, transformOrigin: '0 0' }}>
<FakeDashboard variant={variant} />
</div>
{seam > 0.01 && (
<div
style={{
position: 'absolute',
top: 0,
[seamSide]: 0,
width: 150,
height: H,
opacity: seam,
background:
seamSide === 'right'
? 'linear-gradient(to right, rgba(0,0,0,0), rgba(0,0,0,0.55))'
: 'linear-gradient(to left, rgba(0,0,0,0), rgba(0,0,0,0.55))',
}}
/>
)}
</div>
);
export const CubeRotate: React.FC = () => {
const frame = useCurrentFrame();
// 翻转进度:30–68f,inOut cubic;两端 clamp 保证 hold 段逐帧恒定
const p = interpolate(frame, [30, 68], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.inOut(Easing.cubic),
});
const theta = -90 * p;
const brightA = 1 - 0.45 * p; // 旧面转出压暗 1→0.55
const brightB = 0.55 + 0.45 * p; // 新面转入亮起 0.55→1
const mid = Math.sin(p * Math.PI); // 0→1→0,45° 时最深
const seam = mid * 0.9;
const blur = mid * 1.5;
return (
<div style={{ width: 1920, height: 1080, background: G.bg, position: 'relative', overflow: 'hidden' }}>
{/* blur 外包一层:>0.02 才挂 filter,落定后收尾帧完全相同 */}
<div
style={{
position: 'absolute',
inset: 0,
filter: blur > 0.02 ? `blur(${blur}px)` : undefined,
}}
>
<div
style={{
position: 'absolute',
left: (1920 - W) / 2,
top: (1080 - H) / 2,
width: W,
height: H,
perspective: 1400,
}}
>
<div
style={{
position: 'absolute',
inset: 0,
transformStyle: 'preserve-3d',
transform: `translateZ(${-W / 2}px) rotateY(${theta}deg)`,
}}
>
{/* 面 A:正面出发,绕左而去;接缝棱在其右边 */}
<Face variant="A" rot={0} brightness={brightA} seam={seam} seamSide="right" />
{/* 面 B:从 +90° 侧面转进来;接缝棱在其左边 */}
<Face variant="B" rot={90} brightness={brightB} seam={seam} seamSide="left" />
</div>
</div>
</div>
<div style={{ position: 'absolute', left: 120, top: 60 }}>
<TitleBlock text="CUBE ROTATE" size={54} />
</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/transition/page-turn-transitions/BarnDoorSplit.tsx, demos/transition/page-turn-transitions/CubeRotate.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.

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.