import React from "react"; import { AbsoluteFill, Img, interpolate, spring, useCurrentFrame, useVideoConfig, Series, staticFile, } from "remotion"; // ============================================ // SPRING CONFIGS (from best practices) // ============================================ const SPRING_SMOOTH = { damping: 200 }; const SPRING_SNAPPY = { damping: 20, stiffness: 200 }; const SPRING_BOUNCY = { damping: 12 }; // ============================================ // CAMERA - Simple wrapper, ONE motion at a time // ============================================ const Camera: React.FC<{ children: React.ReactNode; zoom?: number; x?: number; y?: number; }> = ({ children, zoom = 1, x = 0, y = 0 }) => (
{children}
); // ============================================ // KINETIC TEXT - Word by word reveal // ============================================ const KineticText: React.FC<{ text: string; delay?: number; stagger?: number; style?: React.CSSProperties; }> = ({ text, delay = 0, stagger = 4, style = {} }) => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const words = text.split(" "); return (
{words.map((word, i) => { const wordDelay = delay + i * stagger; const progress = spring({ frame: frame - wordDelay, fps, config: SPRING_SNAPPY, }); return ( {word} ); })}
); }; // ============================================ // BROWSER MOCKUP // ============================================ const BrowserMockup: React.FC<{ src: string; width?: number; }> = ({ src, width = 1000 }) => { const height = width * 0.5625; return (
app.reonomy.com
); }; // ============================================ // STEP LABEL // ============================================ const StepLabel: React.FC<{ step: number; text: string; color?: string; }> = ({ step, text, color = "#6366f1" }) => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const progress = spring({ frame: frame - 10, fps, config: SPRING_SNAPPY }); return (
Step {step}: {text}
); }; // ============================================ // CONTACT CARD // ============================================ const ContactCard: React.FC<{ type: "phone" | "email"; value: string; source?: string; delay: number; }> = ({ type, value, source, delay }) => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const progress = spring({ frame: frame - delay, fps, config: SPRING_SNAPPY }); return (
{type === "phone" ? "📞" : "📧"}
{value}
{source &&
{source}
}
); }; // ============================================ // ANIMATED STAT // ============================================ const AnimatedStat: React.FC<{ value: number | string; label: string; color: string; delay: number; }> = ({ value, label, color, delay }) => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const progress = spring({ frame: frame - delay, fps, config: SPRING_SMOOTH }); const numericValue = typeof value === "number" ? Math.round(interpolate(progress, [0, 1], [0, value])) : value; return (
{numericValue}
{label}
); }; // ============================================ // SCENES // ============================================ const SceneIntro: React.FC = () => { const frame = useCurrentFrame(); // Slow, elegant zoom settle: 1.12 -> 1.0 over longer duration const zoom = interpolate(frame, [0, 120], [1.12, 1.0], { extrapolateRight: "clamp", }); return (
); }; const SceneLogin: React.FC = () => { const frame = useCurrentFrame(); // Slow push in toward login area: 0.92 -> 1.08 over 140 frames const zoom = interpolate(frame, [0, 140], [0.92, 1.08], { extrapolateRight: "clamp", }); return ( ); }; const SceneSearch: React.FC = () => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); // Slower settle: start wide, ease in over 100 frames const zoom = interpolate(frame, [0, 100], [0.88, 1.0], { extrapolateRight: "clamp", }); const filterProgress1 = spring({ frame: frame - 50, fps, config: SPRING_SNAPPY }); const filterProgress2 = spring({ frame: frame - 70, fps, config: SPRING_SNAPPY }); return (
{/* Filter badges - staggered */}
✓ Has Phone
✓ Has Email
); }; const SceneProperty: React.FC = () => { const frame = useCurrentFrame(); // Slow, cinematic pan down over 120 frames const y = interpolate(frame, [0, 120], [-25, 15], { extrapolateRight: "clamp", }); return ( ); }; const SceneOwner: React.FC = () => { const frame = useCurrentFrame(); // Slow zoom in to owner section over 120 frames const zoom = interpolate(frame, [0, 120], [1.0, 1.12], { extrapolateRight: "clamp", }); return ( ); }; const SceneModal: React.FC = () => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); // Slower pop in then settle over longer duration const zoom = interpolate(frame, [0, 50, 100], [0.85, 1.04, 1.0], { extrapolateRight: "clamp", }); const labelProgress = spring({ frame: frame - 70, fps, config: SPRING_BOUNCY }); return ( {/* Success label */}
🎉 Contacts Extracted!
); }; const SceneResults: React.FC = () => { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); // Slow, elegant zoom out over 150 frames const zoom = interpolate(frame, [0, 150], [1.06, 1.0], { extrapolateRight: "clamp", }); const phones = [ { value: "919-469-9553", source: "Greystone Property Mgmt" }, { value: "727-341-0186", source: "Seaside Villas" }, { value: "903-566-9506", source: "Apartment Income Reit" }, { value: "407-671-2400", source: "E R Management" }, { value: "407-382-2683", source: "Bellagio Apartments" }, ]; const emails = [ { value: "berrizoro@gmail.com" }, { value: "aberriz@hotmail.com" }, { value: "jasonhitch1@gmail.com" }, { value: "albert@annarborusa.org" }, { value: "albertb@sterlinghousing.com" }, ]; const headerProgress = spring({ frame: frame - 10, fps, config: SPRING_SMOOTH }); const phoneHeaderProgress = spring({ frame: frame - 60, fps, config: SPRING_SMOOTH }); const emailHeaderProgress = spring({ frame: frame - 70, fps, config: SPRING_SMOOTH }); return (
{/* Title */}
CRE Leads Delivered Direct to Your CRM
… On Demand 🚀
{/* Contact columns */}
📞 Phone Numbers
{phones.map((p, i) => ( ))}
📧 Email Addresses
{emails.map((e, i) => ( ))}
{/* Stats footer */}
); }; // ============================================ // MAIN COMPOSITION // ============================================ export const ReonomyDemo: React.FC = () => { const frame = useCurrentFrame(); const { durationInFrames } = useVideoConfig(); // Global fade in/out (20 frames as recommended) const fadeIn = interpolate(frame, [0, 20], [0, 1], { extrapolateRight: "clamp" }); const fadeOut = interpolate(frame, [durationInFrames - 20, durationInFrames], [1, 0], { extrapolateLeft: "clamp", }); return ( ); };