import React from 'react'; import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion'; import { CameraMotionBlur } from '@remotion/motion-blur'; import { FakeDashboard, Card, G } from '../../_fixtures/Fixtures'; // invisible-cut:前景遮挡隐形切——一张放大到超出画幅的卡片带重运动模糊 // 从左侧贴脸扫过,糊满屏幕的瞬间背景从 A 无痕换成 B,卡片飞出右侧时 // 观众以为还是同一镜。(invisible-cut + foreground-occlusion-swipe) const SW_START = 40; // 卡片入场 const SW_END = 54; // 卡片出场(14f 横扫) const CUT = 47; // 硬切点:扫掠中点,卡片完全糊满画面的那一帧 // 卡片中扫掠曲线上的水平位置(容器 left,未缩放坐标) const xAt = (f: number) => // 卡片 scale(1.6) 后半宽 1280:起点右缘 -120 / 终点左缘 2120,均完全出画; // 中点 f47 覆盖 -280..2280,糊满整个 1920 画幅 interpolate(f, [SW_START, SW_END], [-2200, 2600], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.3, 0, 0.7, 1), }); const Scene: React.FC = () => { const frame = useCurrentFrame(); const x = xAt(frame); // 瞬时速度(px/帧),驱动斜切与残影强度 const v = xAt(frame + 0.5) - xAt(frame - 0.5); const sweeping = frame > SW_START - 2 && frame < SW_END + 3; // 背景被"带风"轻推:A 被拖向左,切成 B 后从右侧回稳——卖同一镜错觉 const shove = frame < CUT ? interpolate(frame, [SW_START, CUT], [0, -40], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.in(Easing.quad), }) : interpolate(frame, [CUT, CUT + 13], [40, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic), }); return ( {/* 背景层:硬切藏在遮挡帧内 */}
{frame < CUT ? : }
{/* 手动残影:4 层拖尾(在主卡身后),保证遮挡窗口糊满全屏 */} {sweeping && [4, 3, 2, 1].map((i) => (
))} {/* 主卡:1600x1000 放大 1.6 倍(2560x1600 超出画幅),自带 blur 加强糊感 */} {sweeping && (
)}
); }; export const InvisibleCut: React.FC = () => ( );