// ===== demos/ui-entrance/neon-frame-orbit-drop/NeonFrameForerunOrbit.tsx ===== // neon-frame-forerun-orbit v5(批次 14 #1)。用户意见(逐字): // "这个应该是所有组件和文字同时从空中往下贴合" // ——单点修正:v4 的错峰贴落改为**所有组件和文字同时**从空中往下贴合 // (同帧起落、同帧贴合,同形软影同步收敛)。判例:整体登场镜=同时贴合, // 错峰是巡礼镜的语法。其余全部保留:同款霓虹渐变框+灰面板+背景霓虹管 // 框群;镜头视角 rotateY 从左侧(+38°) 连续弧线旋到右侧(-26°)。 import React, { useId } from 'react'; import { AbsoluteFill, useCurrentFrame, interpolate, Easing } from 'remotion'; const easeFall = Easing.bezier(0.5, 0.05, 0.6, 1); // 加速下落、末端软着陆 /* 悬浮 + 同形软影(对标批次11 GrazeFaceTour FloatWrap): * h=悬浮高度(px)。本体向左上抬起,原位留模糊压暗同形影,h→0 重合影消失 */ const FloatWrap: React.FC<{ h: number; children: React.ReactNode }> = ({ h, children }) => (
{h > 1 && (
{children}
)}
{children}
); /* v5:所有组件和文字**同时**从空中往下贴合——统一 land 时刻 LAND, * 同帧起落(t=LAND-FALL)、同帧贴合(t=LAND),软影同步收敛; * 各组件仅悬浮高度 H 略有差异(同窗下落,速度随高度自然区分) */ const LAND = 0.52; const liftOf = (t: number, land: number, H: number) => { const FALL = 0.3; const p = Math.min(1, Math.max(0, (t - (LAND - FALL)) / FALL)); return (1 - easeFall(p)) * H; }; const mulberry32 = (a: number) => () => { let t = (a += 0x6d2b79f5); t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; const ink = '#3c3c3a'; const mid = '#9a9a98'; const line = '#e3e3e1'; const PW = 1330; const PH = 900; const PL = 1000; const FRAME_D = `M 0 ${PH / 2} L 0 0 L ${PW} 0 L ${PW} ${PH} L 0 ${PH} Z`; const Chip: React.FC<{ w: number }> = ({ w }) => (
); /* 与 v3 同款灰面板;t=贴落进程,各组件错峰空中贴落(重叠并行) */ const GrayHome: React.FC<{ t?: number }> = ({ t = 1 }) => { const L = (land: number, H = 72) => liftOf(t, land, H * 1.7); return (
{[76, 56, 96, 110, 66, 60].map((w, i) => (
))}
{[104, 126, 96, 118, 88].map((w, i) => (
))}
{[0, 1, 2, 3, 4, 5, 6].map((i) => ( ))}
{[54, 84, 50, 82].map((w, i) => (
))}
{[0, 1, 2, 3].map((i) => (
{[0, 1, 2].map((k) =>
)}
))}
); }; type BgFrame = { x: number; y: number; w: number; h: number; hue: string; phase: number; period: number; skew: number }; const rng = mulberry32(20260718); const HUES = ['#b06af0', '#e879c9', '#f0a35c', '#6a7df0', '#e0679a', '#8a5cf0', '#c06af0']; const BG_FRAMES: BgFrame[] = Array.from({ length: 18 }).map(() => ({ x: rng() * 2000 - 120, y: rng() * 1100 - 60, w: 160 + rng() * 480, h: 70 + rng() * 220, hue: HUES[Math.floor(rng() * HUES.length)], phase: rng() * 90, period: 55 + rng() * 70, skew: -14 + rng() * 10, })); export const NeonFrameForerunOrbit: React.FC = () => { const frame = useCurrentFrame(); // 滤镜/渐变 ID 按实例生成,多实例同场不串引(useId 的 «:» 在 url() 里非法,需清洗) const uid = useId().replace(/[^a-zA-Z0-9]/g, ''); // 开场快速描框(同款左缘中点两头奔画,14 帧成型——样式与 v3 一致) const trace = interpolate(frame, [0, 14], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.3, 0.1, 0.3, 1), }); // 面板早亮:本变体主角是旋转+贴落,不复刻暗转亮长过程 const lit = interpolate(frame, [8, 30], [0.25, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.35, 0, 0.3, 1), }); const frameLine = interpolate(frame, [96, 130], [1, 0.35], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); const rimGlow = interpolate(frame, [0, 20, 108, 138], [0.7, 1, 0.75, 0.5], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); const bgLit = interpolate(frame, [0, 30, 100, 136], [0.3, 1, 0.85, 0.1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // 视角弧线:从页面左侧(+38°)连续旋到右侧(-26°),全程不停—— // ease-in-out 起止柔和、中段持续转动 const orbit = interpolate(frame, [0, 128], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.42, 0.05, 0.32, 1), }); const rotY = 38 - 64 * orbit; // +38° → -26° const rotX = 6 - 2.5 * orbit; const rotZ = 3 - 7.5 * orbit; // 左视角微仰 → 右视角微俯(对齐 v3 落定姿态) const scale = 0.9 + 0.1 * Math.sin(orbit * Math.PI) + 0.02 * orbit; // 弧线中段略推近 // 透视原点随视角横移:镜头从左绕到右 const pOrigin = 30 + 34 * orbit; const headP = trace * (PL / 2); // 贴落进程:与旋转同时进行(帧 10–118),错峰起点+重叠下落 const drop = interpolate(frame, [10, 118], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); return ( {/* 背景霓虹管框群(同款) */} {BG_FRAMES.map((b, i) => { const breath = 0.5 + 0.5 * Math.sin(((frame + b.phase) / b.period) * Math.PI * 2); const op = bgLit * (0.18 + 0.4 * breath); return ( ); })} {/* 主体:视角弧线旋转中的框+面板 */}
0.4 ? 1 : 0, filter: `brightness(${Math.max(0.05, lit)})` }}>
{[1, -1].map((dir) => ( {trace < 1 && ( )} ))}
); };