// ===== 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 (
{/* 底层 FakeDashboard B:scale 1.06 → 1.0 迎上来 */}
{/* 左门:960×1080 视口,装完整 A 的左半 */}
{/* 右门:960×1080 视口,内层 translateX(-960) 对位拼合 */}
{/* 裂点预告:中缝 2px 细线闪现两次 */}
{crackFlash && (
)}
);
};
// ===== 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 }) => (
);
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 (
{/* blur 外包一层:>0.02 才挂 filter,落定后收尾帧完全相同 */}
0.02 ? `blur(${blur}px)` : undefined,
}}
>
{/* 面 A:正面出发,绕左而去;接缝棱在其右边 */}
{/* 面 B:从 +90° 侧面转进来;接缝棱在其左边 */}
);
};