// ===== demos/transition/color-block-step-wipe/ColorBlockStepWipe.tsx =====
// color-block-step-wipe —— 纯色矩形按 steps() 离散阶跃吞屏转场
// 源:notion-ai 1.5–3.5s(蓝块中央阶跃生长)+ 26–27s(红块右下角斜向吃屏,携带页面卡)
// 核心语法:无缓动、逐帧硬跳的块状生长,像素游戏手感。
import React from 'react';
import { AbsoluteFill, useCurrentFrame } from 'remotion';
import { G, Card } from '../../_fixtures/Fixtures';
const BLUE = '#2383e2';
const RED = '#e8503a';
// 离散阶跃:frame 越过阈值瞬间跳到新值,无插值
const stepVal = (frame: number, steps: Array<[number, number]>): number => {
let v = steps[0][1];
for (const [f, val] of steps) {
if (frame >= f) v = val;
}
return v;
};
// 蓝底上的圆形 AI 表情徽章
const AiBadge: React.FC<{ scale: number; opacity: number }> = ({ scale, opacity }) => (
);
// 变体 B 背景:极简灰阶页面(不动 Fixtures,只组合)
const GrayPage: React.FC = () => (
{[92, 78, 86, 60].map((w, i) => (
))}
);
export const ColorBlockStepWipe: React.FC = () => {
const frame = useCurrentFrame();
// ---------- 场景 A(0–77f):蓝块中央阶跃生长 ----------
if (frame < 78) {
// [宽, 高] 逐级硬跳:小条 → 长条 → 半屏色带 → 全屏
const w = stepVal(frame, [
[0, 0],
[8, 280],
[16, 820],
[24, 1340],
[32, 1920],
[44, 1920],
]);
const h = stepVal(frame, [
[0, 0],
[8, 96],
[16, 96],
[24, 320],
[32, 580],
[44, 1080],
]);
// 徽章:全屏接管后阶跃弹出(同样离散,两跳到位)
const badgeScale = stepVal(frame, [
[0, 0],
[52, 0.55],
[58, 1.12],
[63, 1],
]);
const badgeOpacity = frame >= 52 ? 1 : 0;
return (
);
}
// ---------- 场景 B(78–149f):红块从右下角斜向阶跃吃屏,携带灰阶页面卡 ----------
// p 控制对角线推进量:p=0 无,p=200 全覆盖
const p = stepVal(frame, [
[78, 0],
[84, 42],
[96, 106],
[108, 200],
]);
// 卡片随色块逐跳前进(同样离散跳位)
const cardPos = stepVal(frame, [
[78, 0],
[84, 1],
[96, 2],
[108, 3],
]);
const cardXY: Array<[number, number]> = [
[2100, 1180], // 画外
[1480, 800],
[980, 560],
[560, 350],
];
const [cx, cy] = cardXY[cardPos];
const clip =
p <= 0
? 'polygon(100% 100%, 100% 100%, 100% 100%)'
: `polygon(${100 - p}% 100%, 100% ${100 - p}%, 100% 100%)`;
return (
{p > 0 && (
)}
);
};