// terminal-typewriter —— 终端打字机触发 // 深色终端窗居中,"$ acme deploy --prod" 逐字符敲出(2f/字符,帧确定 // substring),方块光标 12f 方波闪 → 敲完停 12f → 回车帧:整场景 6f // Easing.in(cubic) 急推 scale 1→3.2 向命令行推入(末 2f 加 blur)硬切到 // FakeDashboard A 全屏,1.06→1 回稳 4f 落定。收尾真静止 ≥40f。 import React from 'react'; import { useCurrentFrame, interpolate, Easing } from 'remotion'; import { G, FakeDashboard } from '../../_fixtures/Fixtures'; const CMD = 'acme deploy --prod'; // 时间轴(30fps,共 145f) const T = { typeStart: 10, // 开始敲字 typeEnd: 10 + CMD.length * 2, // 46:18 字符 × 2f enter: 58, // 敲完停 12f 后回车 pushEnd: 64, // 6f 急推结束,硬切帧 settleEnd: 68, // dashboard 1.06→1 回稳 4f total: 145, // f68 起真静止 77f }; // 终端窗几何 const TW = 1100; const TH = 620; const TL = (1920 - TW) / 2; // 410 const TT = (1080 - TH) / 2; // 230 const TITLEBAR = 52; const PAD = 34; // 命令行基线(推入焦点):标题栏下第二行文字中心 const FOCUS_X = 960; const FOCUS_Y = TT + TITLEBAR + PAD + 92; // ≈ 408 const TerminalWindow: React.FC<{ chars: number; cursorOn: boolean }> = ({ chars, cursorOn }) => (
{/* 标题栏:三圆点窗控(灰阶) */}
{['#6a6a68', '#8f8f8d', '#b5b5b3'].map((c, i) => (
))}
{/* 内容区 */}
{/* 一行历史输出做上下文 */}
~/acme-app (main)
{'$ '} {CMD.substring(0, chars)}
); export const TerminalTypewriter: React.FC = () => { const frame = useCurrentFrame(); // 帧确定打字:2f/字符 const chars = Math.min(CMD.length, Math.max(0, Math.floor((frame - T.typeStart) / 2))); // 方块光标 12f 周期方波闪(全程) const cursorOn = frame % 12 < 6; // 回车急推:整场景 scale 1→3.2,6f Easing.in(cubic),向命令行推入 const pushScale = interpolate(frame, [T.enter, T.pushEnd], [1, 3.2], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.in(Easing.cubic), }); // 末 2f 运动模糊(f62–f64),硬切后摘罩=条件挂载 const pushBlur = interpolate(frame, [T.pushEnd - 2, T.pushEnd], [0, 10], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); // 硬切:f64 起终端场景整体卸载,dashboard 挂载 const cut = frame >= T.pushEnd; // dashboard 落定:1.06→1 回稳 4f const dashScale = interpolate(frame, [T.pushEnd, T.settleEnd], [1.06, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic), }); return (
{!cut ? (
0 ? { filter: `blur(${pushBlur}px)` } : {}), }}>
) : (
)}
); };