// ===== 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 的左半 */}
{tornEdge &&
}
{/* 右门:960×1080 视口,内层 translateX(-960) 对位拼合 */}
{tornEdge &&
}
{/* 裂点预告:中缝 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 }) => (
{seam > 0.01 && (
)}
); 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° 侧面转进来;接缝棱在其左边 */}
); };