// ===== demos/outro/edit-hook-moves/LogoStingButton.tsx ===== // logo-sting-button —— 收尾按钮镜头(button ending) // 上一镜收黑 → 黑场 → LOGO 入场定住(观众以为结束)→ 12f UI 特写彩蛋硬切 → // 硬切回黑底 LOGO 定格。节奏是全部:彩蛋段短促像眨眼。收尾真静止 ≥40f。 import React from 'react'; import { useCurrentFrame, interpolate, Easing } from 'remotion'; import { FakeDashboard } from '../../_fixtures/Fixtures'; // 时间轴(30fps,共 142f) const T = { shotAEnd: 24, // 0–24f 上一镜(B),f14–24 压暗到纯黑 darkenStart: 14, blackEnd: 30, // 24–30f 黑场 6f logoInEnd: 40, // 30–40f LOGO 入场 10f holdEnd: 70, // 40–70f 定住 30f(观众以为结束) eggEnd: 82, // 70–82f 彩蛋硬切 12f total: 142, // 82–142f 黑底 LOGO 真静止 60f }; const LogoLockup: React.FC<{ opacity: number; scale: number }> = ({ opacity, scale }) => (
{/* 方块内一个黑色小标记,避免纯白块太空 */}
ACME
); export const LogoStingButton: React.FC = () => { const frame = useCurrentFrame(); // —— 段 1:上一镜(FakeDashboard B)压暗收黑 —— if (frame < T.shotAEnd) { const dark = interpolate(frame, [T.darkenStart, T.shotAEnd - 1], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.in(Easing.cubic), }); return (
); } // —— 段 2:黑场 6f —— if (frame < T.blackEnd) { return
; } // —— 段 4:彩蛋硬切 12f(variant A 按钮区 2.4x 裁切 + 角落小圆点 tick 闪 2f)—— if (frame >= T.holdEnd && frame < T.eggEnd) { const egg = frame - T.holdEnd; // 0..11 // tick 圆点:第 4–5f 亮 2f,像眨眼 const tickOn = egg >= 4 && egg < 6; return (
{tickOn && (
)}
); } // —— 段 3 + 段 5:黑底 LOGO(入场 → 定住 → 彩蛋后定格收尾,真静止)—— const opacity = interpolate(frame, [T.blackEnd, T.logoInEnd], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic), }); const scale = interpolate(frame, [T.blackEnd, T.logoInEnd], [0.96, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic), }); return ; };