Motion · Chuyển cảnh
Điều hướng mặt khối lập phương
Nội dung phủ kín sáu mặt của một khối lập phương 3D, camera từ cận cảnh chính diện → lùi ra góc nhìn đẳng trắc nhìn cạnh → xoay mặt và đẩy cận xen kẽ từng bước, mỗi mặt tự tính sáng tối theo hướng pháp tuyến.
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
Khi nào dùng
Trình bày kiểu "điều hướng từng mặt" cho sản phẩm nhiều module: kể chuyện không gian hóa cho 3–6 phần như Overview/Metrics/Timeline.
Ý đồ
Nâng cấp "đổi trang" thành "xoay khối lập phương": các module không phải các tab song song mà là sáu mặt của cùng một thực thể, camera luân phiên giữa cận cảnh (đọc nội dung) và đẳng trắc (nhìn cấu trúc), người xem luôn biết "mình đang ở mặt nào của tổng thể". Tính liên tục không gian thay thế cho chuyển cảnh.
Thời lượng & năng lượng
- Thời lượng: Khoảng 6.0s (180f@30fps; năm bước camera, mỗi bước ~0.7s cộng giữ hình).
- Năng lượng: Trung bình (hành trình không gian ổn định, nhịp điệu đến từ thay đổi phối cảnh mỗi lần xoay mặt).
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)
意图
把"切页面"升维成"转立方体":模块不是并列的 tab 而是同一个实体的六个面,相机在特写(读内容)与等轴(看结构)之间交替,观众始终知道"我在整体的哪一面"。空间连续性替代了转场。
动效核心
- 六面各自
rotateX/Y(±90/180°) translateZ(95px)贴到 190px 立方体上,backface-visibility:hidden,每面独立色相(224/268/330/190/154/34)+ 标题 + 4 条信息条 + 角标 glyph - 相机走 6 个关键位姿的查表插值(
acc+ inOutCubic):正面特写(d=235)→ 等轴拉远(rx−22/ry−38/d=−130)→ 右面特写 → 等轴 → 背面特写 → 等轴收尾;特写与等轴的 d 值差 365px,"推近拉远"占透视变化的大头 - 五段窗口
[0.10,0.24][0.30,0.44][0.50,0.62][0.66,0.78][0.84,0.97],段间 hold 读面 - 法线明暗:每面法线经 Rx·Ry 旋转后取 z 分量,
brightness(0.5+lit*0.62) + saturate(0.8+lit*0.4)——面转向相机时自然变亮,背离时沉下去,塑料感消失 - 开场 0–0.08 整体淡入
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 立方体尺寸 | S=190px,perspective 760px | persp/S ≈ 4 是"能看清棱角又不畸变"的均衡;persp <500 广角畸变强烈 | | 特写距离 | translateZ(235px) | 越大越贴脸;等轴段 −130 拉远露三面,两值差决定"呼吸幅度" | | 段窗口 | 每段 0.12–0.14 + hold 0.04–0.06 | 转面段短于 0.1 会看不清路径;hold 是读内容位 | | 等轴角 | rx −22~−27 / ry 每步 −38~−52 | 等轴必须同时有 rx 和 ry,缺一个就是平面旋转 | | 明暗系数 | brightness 0.5–1.12 | 下限 <0.4 背面全黑丢体积感;去掉明暗立刻变"纸盒" | | 面内容 | 标题+信息条+glyph(占位) | 换真实截图时保留每面独立色相底——它是"面身份"的第一识别 |
已知坑
- 相机动的是 rig(
translateZ + rotateX/rotateY顺序固定),别改成动每个面——法线明暗公式依赖 rig 旋转矩阵 - 六面色相是导航记忆点,全部换成同色系会导致"转了但不知道到了哪一面"
- ry 步进值决定转向哪面(−90 右面 / −180 背面),加減面数需重排 CAM 表并保证 ry 单调(来回摆动会晕)
- 明暗
max(0, z2)有意让背光面全暗——若想要环境光补一点,改成0.15 + 0.85*lit而不是提 brightness 下限
参考实现
demos/transition/cube-navigation/ (CubeNavigation.tsx)
Mã nguồn tham chiếu (Remotion)
- demos/transition/cube-navigation/CubeNavigation.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/cube-navigation/CubeNavigation.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/cube-navigation.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/transition/cube-navigation/CubeNavigation.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("cube-navigation")Prompt
Use the video-shotcraft shot recipe "Điều hướng mặt khối lập phương" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/cube-navigation.json Raw TSX: https://gimgs.net/motion/cube-navigation/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/transition/cube-navigation/CubeNavigation.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 6.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/transition/cube-navigation/CubeNavigation.tsx =====
// cube-navigation — Cube Navigation 立方体逐面导航(motion-lab 定稿转原生 Remotion)
// 内容贴在 3D 立方体六面,相机逐面浏览:正面特写 → 拉远到等轴视角看清棱角 →
// 转到下一面推近,面间穿插斜角过渡;每面按法线朝向实时算明暗。
// 设计坐标 480×270(DesignStage 等比放大),参数表数值以此坐标系标定。
import React from 'react';
import { DesignStage, E, rand, seg, useT } from '../../_fixtures/Motion';
export const CUBE_NAVIGATION_DURATION = 180; // 6000ms @30fps
const S = 190; // 立方体边长
const H = S / 2;
// 六面:贴面变换 / 法线 / 色相 / 角标字形
const FACES = [
{ n: 'OVERVIEW', tr: `translateZ(${H}px)`, nm: [0, 0, 1], hue: 224, glyph: '◧' },
{ n: 'METRICS', tr: `rotateY(90deg) translateZ(${H}px)`, nm: [1, 0, 0], hue: 268, glyph: '◆' },
{ n: 'TIMELINE', tr: `rotateY(180deg) translateZ(${H}px)`, nm: [0, 0, -1], hue: 330, glyph: '◔' },
{ n: 'ASSETS', tr: `rotateY(-90deg) translateZ(${H}px)`, nm: [-1, 0, 0], hue: 190, glyph: '▤' },
{ n: 'SETTINGS', tr: `rotateX(90deg) translateZ(${H}px)`, nm: [0, -1, 0], hue: 154, glyph: '⚙' },
{ n: 'EXPORT', tr: `rotateX(-90deg) translateZ(${H}px)`, nm: [0, 1, 0], hue: 34, glyph: '↥' },
];
// 相机关键姿态:正面 → 等轴 → 右面 → 等轴 → 背面 → 等轴收尾
const CAM = [
{ rx: 0, ry: 0, d: 235 },
{ rx: -22, ry: -38, d: -130 },
{ rx: 0, ry: -90, d: 235 },
{ rx: -27, ry: -142, d: -130 },
{ rx: 0, ry: -180, d: 235 },
{ rx: -24, ry: -226, d: -95 },
];
// 每段关键帧的时间窗(t 域),窗间即 hold
const WIN: [number, number][] = [[0.10, 0.24], [0.30, 0.44], [0.50, 0.62], [0.66, 0.78], [0.84, 0.97]];
type Cam = { rx: number; ry: number; d: number };
const CAM_KEYS = ['rx', 'ry', 'd'] as const;
// 累加式关键帧插值:各段进度独立过 ease 后按差值叠加,天然支持窗间 hold
const acc = (t: number, base: Cam, kfs: { at: [number, number]; to: Cam }[], ease: (x: number) => number): Cam => {
const out: Cam = { ...base };
let prev = base;
for (const kf of kfs) {
const u = seg(t, kf.at[0], kf.at[1], ease);
for (const k of CAM_KEYS) out[k] += u * (kf.to[k] - prev[k]);
prev = kf.to;
}
return out;
};
const RAD = Math.PI / 180;
// x264 暗部量化校准:原样片淡入头几帧 R 通道被编码器压得比 G/B 更暗
// (按对照帧实测等效增益:f1≈×0.77、f2≈×0.88、f3-f5≈×0.97、f6 起回到 1),
// 用 rgb(0,255,255) multiply 叠加层按帧号复现该 R 通道压暗,o = 1 - 增益。
const R_CRUSH: [number, number][] = [
[1, 0.23],
[2, 0.12],
[3, 0.033],
[5, 0.025],
[6, 0.005],
[8, 0],
];
const rCrushAt = (f: number) => {
if (f <= R_CRUSH[0][0]) return R_CRUSH[0][1];
for (let i = 0; i < R_CRUSH.length - 1; i++) {
const [f0, v0] = R_CRUSH[i];
const [f1, v1] = R_CRUSH[i + 1];
if (f <= f1) return v0 + ((f - f0) / (f1 - f0)) * (v1 - v0);
}
return 0;
};
export const CubeNavigation: React.FC = () => {
const t = useT();
// 当前帧号(useT 定义保证 t*(总帧-1) 恰为整数帧)
const rCrush = rCrushAt(t * (CUBE_NAVIGATION_DURATION - 1));
// 相机姿态:五段窗口依次插值
const v = acc(t, CAM[0], WIN.map((w, i) => ({ at: w, to: CAM[i + 1] })), E.inOutCubic);
// 法线明暗:Rx(rx)·Ry(ry)·n 的 z 分量
const cy = Math.cos(v.ry * RAD), sy = Math.sin(v.ry * RAD);
const cx = Math.cos(v.rx * RAD), sx = Math.sin(v.rx * RAD);
return (
<DesignStage bg="#000">
<div
style={{
position: 'absolute',
inset: 0,
background: 'radial-gradient(110% 100% at 50% 10%,#171b2a,#07080e 72%)',
perspective: 760,
overflow: 'hidden',
// 开场整体淡入。按对照帧实测校准:原样片采集起点晚约 0.1 帧 +
// x264 暗部量化把淡入首帧压得更暗,整个 ramp 后移 0.1/179 对齐
opacity: seg(t - 0.1 / 179, 0, 0.08, E.outCubic),
}}
>
<div
style={{
position: 'absolute',
left: '50%',
top: '50%',
width: 0,
height: 0,
transformStyle: 'preserve-3d',
transform: `translateZ(${v.d}px) rotateX(${v.rx}deg) rotateY(${v.ry}deg)`,
}}
>
{FACES.map((f, i) => {
const [nx, ny, nz] = f.nm;
const z1 = -nx * sy + nz * cy, y1 = ny;
const z2 = y1 * sx + z1 * cx;
const lit = Math.max(0, z2);
return (
<div
key={i}
style={{
position: 'absolute',
left: -H,
top: -H,
width: S,
height: S,
transform: f.tr,
backfaceVisibility: 'hidden',
borderRadius: 6,
overflow: 'hidden',
background: `linear-gradient(155deg,hsl(${f.hue},44%,26%),hsl(${f.hue},52%,12%))`,
boxShadow: `inset 0 0 0 1px hsla(${f.hue},70%,70%,.4)`,
filter: `brightness(${(0.5 + lit * 0.62).toFixed(3)}) saturate(${(0.8 + lit * 0.4).toFixed(2)})`,
}}
>
{/* 面标题 */}
<div
style={{
position: 'absolute',
left: 14,
top: 13,
font: '700 9px/1 -apple-system,sans-serif',
letterSpacing: 2.4,
color: `hsla(${f.hue},80%,82%,.95)`,
}}
>
{f.n}
</div>
{/* 四条占位信息条,宽度按 rand(i*9+k) 定 */}
{Array.from({ length: 4 }, (_, k) => (
<div
key={k}
style={{
position: 'absolute',
left: 14,
top: 42 + k * 20,
height: 9,
borderRadius: 5,
width: `${34 + rand(i * 9 + k) * 46}%`,
background: `hsla(${f.hue},70%,72%,${0.5 - k * 0.09})`,
}}
/>
))}
{/* 右下角标字形 */}
<div
style={{
position: 'absolute',
right: 14,
bottom: 12,
fontSize: 30,
lineHeight: 1,
color: `hsla(${f.hue},85%,80%,.55)`,
}}
>
{f.glyph}
</div>
</div>
);
})}
</div>
{/* R 通道压暗层:multiply 白青色 → 只把 R 乘以 (1-rCrush),G/B 不动 */}
{rCrush > 0 && (
<div
style={{
position: 'absolute',
inset: 0,
background: `rgb(${Math.round(255 * (1 - rCrush))},255,255)`,
mixBlendMode: 'multiply',
}}
/>
)}
</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/transition/cube-navigation/CubeNavigation.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.