// 套印错位冲击帧(riso-misregistration-hit)——标题撞停瞬间裂成两份单色"印版"
// (浅灰琥珀版 G.mid + 深墨版 G.ink,mix-blend-mode: multiply 叠加),像 riso 印刷
// 没对准版;两版反向错位(x 为主 y 少量)做衰减震荡 offset = A*cos(ωt)*exp(-t/τ)
// 抖两下,帧 72 啪地硬切回单一正体(套准合一),带 4f scale 1.03→1 脉冲收束。
// 结构:0–19f 空场 hold(只有底部装饰线);20–28f 标题从右画外 Easing.in(cubic)
// 撞入屏心(帧 28 命中);28–71f 双版错位震荡;72–75f 套准脉冲;76–119f 真静止 44f。
import React from 'react';
import { useCurrentFrame, interpolate, Easing } from 'remotion';
import { G, TitleBlock } from '../../_fixtures/Fixtures';
const HIT = 28; // 撞停命中帧
const SNAP = 72; // 套准合一帧
const AX = 16; // 单版 x 错位振幅(两版反向 → 总分离 ~32px,肉眼明显)
const AY = 7; // 单版 y 错位振幅(少量,更像没对准版)
const OMEGA = (2 * Math.PI) / 18; // 震荡周期 18f,44f 内抖两下半
const TAU = 60; // 缓衰减:帧 72 前仍余 ~14px 总分离,被"啪地"硬切归零
// 与 TitleBlock 同字形的可调色标题(错位印版需要单色副本)
const Plate: React.FC<{ color: string; dx: number; dy: number }> = ({ color, dx, dy }) => (
);
export const RisoMisregistrationHit: React.FC = () => {
const frame = useCurrentFrame();
// 阶段判定
const entering = frame >= 20 && frame < HIT; // 撞入
const split = frame >= HIT && frame < SNAP; // 双版错位震荡
const showSingle = frame < HIT || frame >= SNAP; // 正体(画外/撞入/套准后)
// 撞入位移:右画外 1400px → 0,8f Easing.in(cubic)(加速撞停)
const slideX = interpolate(frame, [20, HIT], [1400, 0], {
easing: Easing.in(Easing.cubic),
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// 错位震荡包络:t 自命中起,衰减余弦(帧 72 前仍有可见残余,硬切归零成"啪")
const t = frame - HIT;
const m = split ? Math.cos(OMEGA * t) * Math.exp(-t / TAU) : 0;
const dx = AX * m;
const dy = AY * m;
// 套准合一脉冲:帧 72 起 4f scale 1.03 → 1,之后精确 1(保证结尾真静止)
const pulse =
frame >= SNAP && frame < SNAP + 4 ? 1 + 0.03 * (1 - (frame - SNAP) / 4) : 1;
return (
{/* 底部装饰线:全程静止的布景锚点 */}
{/* 正体:画外等待 / 撞入 / 套准后(撞入前 frame<20 时在画外,视觉等同空场) */}
{showSingle && (
)}
{/* 双版错位:浅灰版与深墨版反向偏移,multiply 叠加出"重影套印" */}
{split && (
<>
>
)}
);
};