// ===== demos/ui-entrance/wall-reveal-moves/BentoLightUp.tsx ===== import React from 'react'; import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion'; import { Card, TitleBlock } from '../../_fixtures/Fixtures'; // bento-light-up:暗场里 3×2 bento 墙压暗待命,随节拍逐格点亮—— // 边框流光先描一圈(琥珀),格内内容随后提亮上浮弹出;六格全亮后整体微推收住。 // 节拍:0–20 建立(hold) → 每格间隔 12f 依次激活(描边 8f + 内容弹出 8f) // → ~96f 全亮 → 96–121 scale 1→1.04 缓推 → 121–150 静止收尾。 const BG = '#2a2a28'; const AMBER = '#e8b45e'; const FIRST = 20; // 首格激活帧 const GAP = 12; // 格间节拍 const CELL_W = 480; const CELL_H = 330; const GUT = 44; const LEFT = (1920 - (CELL_W * 3 + GUT * 2)) / 2; const TOP = (1080 - (CELL_H * 2 + GUT)) / 2 + 30; const Cell: React.FC<{ i: number; frame: number }> = ({ i, frame }) => { const start = FIRST + i * GAP; const col = i % 3; const row = Math.floor(i / 3); const x = LEFT + col * (CELL_W + GUT); const y = TOP + row * (CELL_H + GUT); // ① 边框流光:pathLength=100 的 dashoffset 描边,8f 走完一圈 const draw = interpolate(frame, [start, start + 8], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic), }); // 描完后流光退火:琥珀亮边 → 弱化成常亮细边 const strokeFade = interpolate(frame, [start + 12, start + 26], [1, 0.4], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.quad), }); // ② 内容提亮 + 上浮弹出:描边过半后接力,8f 弹出(back-out 带一点过冲) const lit = interpolate(frame, [start + 6, start + 14], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic), }); const rise = interpolate(frame, [start + 6, start + 14], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.3, 1.4, 0.5, 1), }); const opacity = 0.18 + 0.82 * lit; const ty = 20 * (1 - rise); // seed 正弦哈希做每格微差(点亮瞬间的辉光强度略有随机感) const jitter = Math.abs(Math.sin(i * 127.3) * 43758.5453 % 1); const glow = lit * (1 - lit) * 4 * (14 + jitter * 6); // 点亮中段最亮的辉光脉冲 return (
{/* 暗态卡 + 点亮后的内容(同一张卡,靠 opacity/translateY 提亮浮出) */}
0.5 ? `0 0 ${glow}px rgba(232,180,94,${0.35 * lit * (1 - lit) * 4})` : 'none', borderRadius: 14, }} >
{/* 边框流光:SVG rect 描边一圈 */} {draw > 0 && ( )}
); }; export const BentoLightUp: React.FC = () => { const frame = useCurrentFrame(); // ③ 六格全亮(~96f)后整体缓推 scale 1→1.04,25f 收住,之后真静止 const push = interpolate(frame, [96, 121], [1, 1.04], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.33, 0, 0.2, 1), }); // 标题随首格点亮微微提亮,交代场景 const titleLit = interpolate(frame, [FIRST, FIRST + 20], [0.25, 0.75], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.quad), }); return (
{Array.from({ length: 6 }).map((_, i) => ( ))}
); }; // ===== demos/ui-entrance/wall-reveal-moves/GridWaveFlip.tsx ===== import React from 'react'; import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion'; import { Card, G } from '../../_fixtures/Fixtures'; // grid-wave-flip〔入场退场〕:3×3 灰背卡片墙沿对角线波前依次 rotateX 翻转 180°, // 灰背翻成正面内容卡;波浪约一秒扫完全屏,最后一张落定带轻微过冲。 // 结构:hold 20f → delay=(row+col)*6f、每张 14f bezier(0.35,0,0.25,1) → 尾张过冲回弹 → 静止收尾。 const COLS = 3; const ROWS = 3; const CELL_W = 520; const CELL_H = 280; const GAP = 36; const HOLD = 20; // 开头建立 const STAGGER = 6; // 对角线波前步进 const FLIP = 14; // 单张翻转时长 const flipEase = Easing.bezier(0.35, 0, 0.25, 1); // 单张卡的翻转角度:普通卡 0→180;最后一张(波前最末)过冲到 ~190 再回落 180 const angleAt = (frame: number, row: number, col: number): number => { const delay = HOLD + (row + col) * STAGGER; const isLast = row === ROWS - 1 && col === COLS - 1; if (!isLast) { return interpolate(frame, [delay, delay + FLIP], [0, 180], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: flipEase, }); } // 过冲:14f 冲到 190°,再 8f 弹回 180° const main = interpolate(frame, [delay, delay + FLIP], [0, 190], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: flipEase, }); const settle = interpolate(frame, [delay + FLIP, delay + FLIP + 8], [0, -10], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic), }); return main + settle; }; export const GridWaveFlip: React.FC = () => { const frame = useCurrentFrame(); const wallW = COLS * CELL_W + (COLS - 1) * GAP; const wallH = ROWS * CELL_H + (ROWS - 1) * GAP; return ( {/* 卡片墙:共享 perspective 1200px 容器,整墙一个消失点 */}
{Array.from({ length: ROWS * COLS }).map((_, i) => { const row = Math.floor(i / COLS); const col = i % COLS; const angle = angleAt(frame, row, col); // 高光线:翻到 90°(最薄处)时最亮,位置随角度从上缘扫向下缘 const glow = Math.max(0, 1 - Math.abs(angle - 90) / 45); const glowTop = interpolate(angle, [45, 135], [8, 92], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // 翻转中抬起阴影,落定后收回 const lift = Math.sin(Math.min(Math.max(angle, 0), 180) * (Math.PI / 180)); return (
{/* 灰背面(初始朝外) */}
{/* 背面只留一个哑光圆点标记,强调"灰背" */}
{/* 正面内容卡(预转 180°,翻过来正好朝外) */}
{/* 最薄处高光线:不随卡旋转,贴在格位上随角度纵向移动 */} {glow > 0.01 && (
)}
); })}
); }; // ===== demos/ui-entrance/wall-reveal-moves/WireframeDrawOn.tsx ===== import React from 'react'; import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion'; import { FakeDashboard, G } from '../../_fixtures/Fixtures'; // wireframe-draw-on〔入场退场〕:界面先以蓝图细线描画成形(stroke-dashoffset // 分组错峰),随后一条发光竖线从左向右扫过,扫过之处线框实体化为真实灰阶界面。 // 节拍:0–20 hold 空底 → 20–82 分组描线 → 88–118 光线扫描实体化 → 118–150 hold。 // FakeDashboard variant A 的几何(1920×1080): // 侧栏 220 宽;顶栏 x220–1920 h72;内容区 padding36 gap28,3×2 卡片 // 卡 w524 h454,x = 256/808/1360,y = 108/590。 const CARD_W = 524; const CARD_H = 454; const CARD_X = [256, 808, 1360]; const CARD_Y = [108, 590]; const seedFrac = (i: number) => { const v = Math.sin(i * 127.3) * 43758.5453; return v - Math.floor(v); }; export const WireframeDrawOn: React.FC = () => { const frame = useCurrentFrame(); // 描线进度:每组 30f 画完,start 错峰 const draw = (start: number) => interpolate(frame, [start, start + 30], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.4, 0, 0.3, 1), }); const tSide = draw(20); // 侧栏 const tTop = draw(30); // 顶栏 const tCards = Array.from({ length: 6 }, (_, i) => draw(40 + i * 3)); // 卡片错峰 const tChart = draw(52); // 折线图 // 实体化扫描:88–118 从左向右 const scan = interpolate(frame, [88, 118], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.bezier(0.55, 0, 0.25, 1), }); const scanX = scan * 1920; const lineOpacity = interpolate(frame, [86, 92, 112, 120], [0, 1, 1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // 描线小工具:pathLength=1 + dashoffset 从 1 → 0 const stroke = (t: number): React.SVGAttributes => ({ fill: 'none', stroke: G.mid, strokeWidth: 2.5, pathLength: 1, strokeDasharray: 1, strokeDashoffset: 1 - t, strokeLinecap: 'round', opacity: t > 0 ? 1 : 0, }); // 折线图数据(seed 正弦哈希,落在卡 (1,1) 内部) const chartCardX = CARD_X[1]; const chartCardY = CARD_Y[1]; const chartPts = Array.from({ length: 8 }, (_, i) => { const px = chartCardX + 50 + (i * (CARD_W - 100)) / 7; const py = chartCardY + 130 + seedFrac(i + 3) * 230; return `${px},${py}`; }).join(' '); return ( {/* —— 线框层(蓝图描线) —— */} {/* 侧栏 */} {Array.from({ length: 7 }).map((_, i) => { const w = (216 - 44) * (0.6 + ((i * 29) % 35) / 100); return ( ); })} {/* 顶栏 */} {/* 3×2 卡片:轮廓 + 内部占位线 */} {tCards.map((t, i) => { const cx = CARD_X[i % 3]; const cy = CARD_Y[Math.floor(i / 3)]; const titleW = (CARD_W - 36) * (0.45 + (((i + 1) * 37) % 40) / 100); return ( ); })} {/* 折线图(画在中列下排卡内) */} {/* —— 实体层:clip-path inset 从左向右展开 —— */}
{/* —— 4px 发光扫描竖线(琥珀微光) —— */}
); };