// blinds-slice — staggered vertical-blind wipe
// FakeDashboard A → B. 12 vertical strips of 160px, left to right with delay = column × 2f,
// each strip completes its flip in 10f: inside a strip, A's scaleX 1→0 (origin left
// edge) and B's scaleX 0→1 (origin right edge) share the same progress p
// (Easing.in(cubic)); the seam between them is always at x+160(1-p), so there's no
// mathematical gap. A bright seam line (soft glow + dark outline + white core)
// sweeps along with the wave.
// Wave 20–52f; from 52f the wrapper is removed (full-page B renders directly, all
// strip structure and seam lines unmount), 52–150f true stillness 98f ≥ 40f. Frame-deterministic, no random source.
import React from 'react';
import { AbsoluteFill, useCurrentFrame, interpolate, Easing } from 'remotion';
import { FakeDashboard } from '../../_fixtures/Fixtures';
const STRIPS = 12;
const W = 160; // strip width, 12×160 = 1920
const WAVE_START = 20;
const STAGGER = 2; // column × 2f
const FLIP = 10; // each strip completes its flip in 10f
const WAVE_END = WAVE_START + (STRIPS - 1) * STAGGER + FLIP; // 52
// A page's slice inside a strip: outer 160-wide crop, inner full-page 1920 aligned with negative margin
const Slice: React.FC<{ x: number; variant: 'A' | 'B' }> = ({ x, variant }) => (
);
export const BlindsSlice: React.FC = () => {
const frame = useCurrentFrame();
// Wrapper removal: once the wave completes all strip structure unmounts and B renders full-page directly
if (frame >= WAVE_END) {
return (
);
}
const seams: { x: number; opacity: number }[] = [];
const strips = Array.from({ length: STRIPS }).map((_, i) => {
const x = i * W;
const start = WAVE_START + i * STAGGER;
const end = start + FLIP;
// Not started: pure A slice; completed: pure B slice
if (frame < start) {
return (
);
}
if (frame >= end) {
return (
);
}
// Flipping: A and B share the same progress p — A's width 160(1-p) sits left,
// B's width 160p sits right, the junction is always x+160(1-p), no gap
const p = interpolate(frame, [start, end], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.in(Easing.cubic),
});
// Seam bright line: 2f linear fade in/out on each side
const seamOpacity = Math.min(
interpolate(frame, [start, start + 2], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }),
interpolate(frame, [end - 2, end], [1, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }),
);
seams.push({ x: x + W * (1 - p), opacity: seamOpacity });
return (
{/* A: shrinks toward the left edge */}
{/* B: expands from the right edge */}
);
});
return (
{strips}
{/* Seam bright line: white-background precedent — pure brightening is invisible, so soft glow + dark outline + white core in three layers */}
{seams.length > 0 && (
)}
);
};