Motion · Transition
Cube Face Navigation
Content covers all six faces of a 3D cube; the camera goes from a front close-up → pulls back to an isometric view of the edges → alternates turning faces and pushing in, each face's shading computed live from its normal.
Source: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Remotion composition (React/TSX) — preview video, no in-browser player
When to use
A "face-by-face navigation" layout for multi-module products: a spatialized walkthrough of 3–6 sections like Overview/Metrics/Timeline.
Intention
Upgrade "switching pages" into "rotating a cube": modules aren't parallel tabs but six faces of the same object, and the camera alternates between close-up (reading content) and isometric (seeing the structure), so viewers always know "which face of the whole I'm on." Spatial continuity replaces transitions.
Duration & energy
- Duration: About 6.0s (180f@30fps; five camera steps, each ~0.7s plus hold).
- Energy: Medium (a steady spatial cruise, with the beat coming from the perspective shift at each face turn).
Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)
意图
把"切页面"升维成"转立方体":模块不是并列的 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)
Reference implementation (Remotion)
- demos/transition/cube-navigation/CubeNavigation.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/cube-navigation/CubeNavigation.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/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 "Cube Face Navigation" (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 |
|---|---|---|---|
| — | |||
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.