import React from "react";
import {
AbsoluteFill,
useCurrentFrame,
useVideoConfig,
spring,
interpolate,
} from "remotion";
import { theme } from "../../components/shared/theme";
import { AppScreenshot, MockScreen } from "../../components/shared/AppScreenshot";
/**
* Scene 4: Routes generated
*
* Route auto-generates inside phone frame.
* Map with animated route line + game cards appear.
* Overlay: "Real route. Real games."
*/
type Stop = {
city: string;
x: number;
y: number;
};
const STOPS: Stop[] = [
{ city: "LA", x: 180, y: 300 },
{ city: "SF", x: 140, y: 130 },
{ city: "SD", x: 240, y: 400 },
{ city: "PHX", x: 480, y: 340 },
];
export const RouteScene: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// Overlay text
const overlayProgress = spring({
frame: frame - 0.3 * fps,
fps,
config: theme.animation.snappy,
});
const overlayOpacity = interpolate(
frame - 0.3 * fps,
[0, 0.2 * fps],
[0, 1],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);
return (
{/* Overlay: "Real route. Real games." */}
{/* Phone with route */}
);
};
/** Route map + game list inside phone */
const RouteContent: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// Route draw
const lineProgress = interpolate(
frame,
[0.15 * fps, 1.5 * fps],
[0, 1],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp" }
);
const pathSegments: string[] = [];
for (let i = 0; i < STOPS.length - 1; i++) {
const from = STOPS[i];
const to = STOPS[i + 1];
if (i === 0) pathSegments.push(`M ${from.x} ${from.y}`);
const cx = (from.x + to.x) / 2;
const cy = Math.min(from.y, to.y) - 30;
pathSegments.push(`Q ${cx} ${cy} ${to.x} ${to.y}`);
}
const fullPath = pathSegments.join(" ");
// Game cards
const games = [
{ team: "@ Dodgers", venue: "Dodger Stadium", date: "Jun 12", color: "#005A9C" },
{ team: "@ Giants", venue: "Oracle Park", date: "Jun 14", color: "#FD5A1E" },
{ team: "@ Padres", venue: "Petco Park", date: "Jun 16", color: "#2F241D" },
{ team: "@ D-backs", venue: "Chase Field", date: "Jun 18", color: "#A71930" },
];
return (
{/* Map section (top 45%) */}
{STOPS.map((stop, index) => {
const markerProgress = spring({
frame: frame - (0.2 + index * 0.35) * fps,
fps,
config: { damping: 12, stiffness: 180 },
});
const leftPct = (stop.x / 600) * 100;
const topPct = (stop.y / 480) * 100;
return (
);
})}
{/* Game cards (bottom 55%) */}
Your Games
{games.map((game, index) => {
const cardDelay = 1.2 + index * 0.18;
const cardProgress = spring({
frame: frame - cardDelay * fps,
fps,
config: theme.animation.snappy,
});
const translateX = interpolate(cardProgress, [0, 1], [250, 0]);
const opacity = interpolate(cardProgress, [0, 1], [0, 1]);
return (
{game.date.split(" ")[1]}
{game.date.split(" ")[0]}
);
})}
);
};