import React from 'react';
import { AbsoluteFill, Sequence, interpolate, spring, useCurrentFrame, useVideoConfig } from 'remotion';
import { palette } from './contract';
const FONT = 'Archivo';
const CLAMP = { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } as const;
const rgba = (hex: string, alpha: number): string => {
const n = parseInt(hex.replace('#', ''), 16);
return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${alpha})`;
};
const ease = (frame: number, from: number, to: number, start: number, end: number): number =>
interpolate(frame, [start, end], [from, to], CLAMP);
const fade = (frame: number, inStart: number, inEnd: number, outStart: number, outEnd: number): number =>
Math.min(ease(frame, 0, 1, inStart, inEnd), ease(frame, 1, 0, outStart, outEnd));
const captions = [
{ text: 'Plan together', start: 0, end: 60 },
{ text: 'Protect focus time', start: 60, end: 120 },
{ text: 'See progress', start: 120, end: 180 },
];
const days = ['MON', 'TUE', 'WED', 'THU', 'FRI'];
const blocks = [
{ c: 0, r: 0, t: 'Kickoff' },
{ c: 0, r: 1, t: 'Roadmap' },
{ c: 1, r: 0, t: 'Design review' },
{ c: 2, r: 0, t: 'Build sprint' },
{ c: 3, r: 0, t: 'Client sync' },
{ c: 4, r: 0, t: 'Retro' },
{ c: 1, r: 1, t: 'Handoff' },
{ c: 3, r: 1, t: 'Hiring' },
];
const meters = [
{ label: 'Scope', value: 72, color: palette.accent },
{ label: 'Reviews', value: 48, color: palette.secondary },
{ label: 'Launch', value: 90, color: palette.accent },
];
const Backdrop: React.FC = () => {
const frame = useCurrentFrame();
const gx = ease(frame, 30, 70, 0, 360);
const gy = ease(frame, 35, 65, 0, 360);
const line = rgba(palette.foreground, 0.035);
return (
);
};
const IntroScene: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const title = spring({ frame, fps, config: { damping: 200 } });
const rule = ease(frame, 0, 180, 20, 48);
const tag = spring({ frame: Math.max(0, frame - 30), fps, config: { damping: 200 } });
return (
Relay
Make room for focused work
);
};
const Captions: React.FC<{ frame: number }> = ({ frame }) => {
const active = frame < 60 ? 0 : frame < 120 ? 1 : 2;
return (
<>
{captions.map((c, i) => {
const o = fade(frame, c.start, c.start + 12, c.end - 10, c.end);
const y = ease(frame, 28, 0, c.start, c.start + 12);
return (
);
})}
{captions.map((c, i) => (
))}
>
);
};
const Interface: React.FC<{ frame: number; fps: number }> = ({ frame, fps }) => {
const enter = spring({ frame, fps, config: { damping: 200 } });
const focusH = ease(frame, 0, 190, 62, 92);
const focusO = ease(frame, 0, 1, 60, 66);
const focusTextO = ease(frame, 0, 1, 84, 100);
const panelO = ease(frame, 0, 1, 112, 124);
return (
This week
{[palette.accent, palette.secondary, palette.muted].map((col, i) => (
))}
{days.map((d, i) => (
{d}
))}
{blocks.map((b, k) => {
const p = spring({ frame: Math.max(0, frame - (6 + k * 6)), fps, config: { damping: 200 } });
return (
);
})}
{meters.map((m, i) => {
const p = ease(frame, 0, 1, 118 + i * 10, 168 + i * 10);
const pct = Math.round(m.value * p);
return (
);
})}
);
};
const ProductScene: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
return (
);
};
const FinalScene: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const s1 = spring({ frame: Math.max(0, frame - 4), fps, config: { damping: 200 } });
const s2 = spring({ frame: Math.max(0, frame - 14), fps, config: { damping: 200 } });
const opacity = ease(frame, 0, 1, 0, 12);
return (
Start your next week
with Relay
Relay
);
};
export const BenchmarkVideo: React.FC = () => {
return (
);
};