import React, { useMemo, useEffect, useState } from "react"; import { AbsoluteFill, interpolate, useCurrentFrame, useVideoConfig, delayRender, continueRender, spring, Img, } from "remotion"; import { MouseCursor } from "../components/MouseCursor"; import { ContactForm } from "@/src/components/ContactForm"; import { BackgroundGrid } from "@/src/components/Layout"; import { initialState } from "@/src/components/ContactForm/constants"; // Brand Assets import IconWhite from "@/src/assets/logo/Icon White Transparent.svg"; export const ContactFormShowcase: React.FC = () => { const frame = useCurrentFrame(); const { width, height, fps } = useVideoConfig(); const [handle] = useState(() => delayRender("Initializing Deep Interaction Script"), ); useEffect(() => { if (typeof window !== "undefined") (window as any).isRemotion = true; const timer = setTimeout(() => continueRender(handle), 500); return () => clearTimeout(timer); }, [handle]); // ---- TIMELINE CONSTANTS (1500 Frames / 25s) ---- const T = useMemo( () => ({ ENTER: 0, // Step 0: Type Selection SELECT_WEBSITE: 60, NEXT_0: 100, // Step 1: Company Profile COMPANY_TYPE_START: 150, COMPANY_TYPE_END: 250, NEXT_1: 300, // Step 2: Presence URL_TYPE_START: 350, URL_TYPE_END: 450, NEXT_2: 500, // Step 3: Scope & Services (Multi-clicks) SCOPE_CLICK_1: 550, SCOPE_CLICK_2: 600, SCOPE_CLICK_3: 650, NEXT_3: 750, // Step 4: Design Vibe VIBE_SELECT: 850, NEXT_4: 950, // Step 5: Contact Info NAME_TYPE_START: 1050, NAME_TYPE_END: 1120, EMAIL_TYPE_START: 1150, EMAIL_TYPE_END: 1250, MESSAGE_TYPE_START: 1280, MESSAGE_TYPE_END: 1400, SUBMIT: 1450, EXIT: 1500, }), [], ); // ---- FORM STATE LOGIC ---- const formState = useMemo(() => { const state = { ...initialState }; // Step 0: Fixed to website per request state.projectType = "website"; // Step 1: Company if (frame > T.COMPANY_TYPE_START) { const text = "Mintel Studios"; const progress = interpolate( frame, [T.COMPANY_TYPE_START, T.COMPANY_TYPE_END], [0, text.length], { extrapolateRight: "clamp" }, ); state.companyName = text.substring(0, Math.round(progress)); } // Step 2: URL if (frame > T.URL_TYPE_START) { const text = "mintel.me"; const progress = interpolate( frame, [T.URL_TYPE_START, T.URL_TYPE_END], [0, text.length], { extrapolateRight: "clamp" }, ); state.existingWebsite = text.substring(0, Math.round(progress)); } // Step 3: Selections if (frame > T.SCOPE_CLICK_1) state.selectedPages = ["Home", "About"]; if (frame > T.SCOPE_CLICK_2) state.selectedPages = ["Home", "About", "Services"]; if (frame > T.SCOPE_CLICK_3) state.features = ["blog_news"]; // Step 4: Design if (frame > T.VIBE_SELECT) state.designVibe = "tech"; // Step 5: Contact if (frame > T.NAME_TYPE_START) { const text = "Marc Mintel"; const progress = interpolate( frame, [T.NAME_TYPE_START, T.NAME_TYPE_END], [0, text.length], { extrapolateRight: "clamp" }, ); state.name = text.substring(0, Math.round(progress)); } if (frame > T.EMAIL_TYPE_START) { const text = "request@mintel.me"; const progress = interpolate( frame, [T.EMAIL_TYPE_START, T.EMAIL_TYPE_END], [0, text.length], { extrapolateRight: "clamp" }, ); state.email = text.substring(0, Math.round(progress)); } if (frame > T.MESSAGE_TYPE_START) { const text = "Hi folks! Let's build something cinematic and smooth."; const progress = interpolate( frame, [T.MESSAGE_TYPE_START, T.MESSAGE_TYPE_END], [0, text.length], { extrapolateRight: "clamp" }, ); state.message = text.substring(0, Math.round(progress)); } return state; }, [frame, T]); // ---- STEP NAVIGATION ---- const stepIndex = useMemo(() => { if (frame < T.NEXT_0) return 0; if (frame < T.NEXT_1) return 1; if (frame < T.NEXT_2) return 2; if (frame < T.NEXT_3) return 3; if (frame < T.NEXT_4) return 6; // Mapping depends on actual component order, let's assume standard sequence if (frame < T.SUBMIT) return 12; // Final Contact step return 13; // Success }, [frame, T]); // ---- CAMERA ANCHORS ---- const anchors = useMemo( () => ({ overview: { z: 0.85, x: 0, y: 0 }, type: { z: 1.15, x: 250, y: 150 }, company: { z: 1.3, x: 100, y: 50 }, presence: { z: 1.3, x: 100, y: -50 }, scope: { z: 1.1, x: -100, y: 0 }, design: { z: 1.15, x: 150, y: 100 }, contact: { z: 1.25, x: 0, y: 400 }, success: { z: 0.9, x: 0, y: 0 }, }), [], ); const activeAnchor = useMemo(() => { if (frame < T.SELECT_WEBSITE) return anchors.overview; if (frame < T.NEXT_0) return anchors.type; if (frame < T.NEXT_1) return anchors.company; if (frame < T.NEXT_2) return anchors.presence; if (frame < T.NEXT_3) return anchors.scope; if (frame < T.NEXT_4) return anchors.design; if (frame < T.SUBMIT) return anchors.contact; return anchors.success; }, [frame, anchors, T]); const _camera = useMemo(() => { // Continuous organic spring follow const _s = spring({ frame, fps, config: { stiffness: 45, damping: 20 }, }); return activeAnchor; }, [frame, activeAnchor, fps]); // Simple smooth camera interpolation for the actual movement const smoothCamera = useMemo(() => { return activeAnchor; }, [activeAnchor]); // ---- MOUSE PATH ---- const mouse = useMemo(() => { const targets = { off: { x: width * 1.2, y: height * 1.2 }, type_website: { x: 200, y: 50 }, btn_next: { x: 450, y: 450 }, company_input: { x: 0, y: 0 }, url_input: { x: 0, y: -50 }, scope_1: { x: -300, y: -100 }, scope_2: { x: -300, y: 0 }, scope_3: { x: 0, y: 200 }, vibe_tech: { x: 250, y: 100 }, contact_name: { x: -200, y: 200 }, contact_email: { x: 200, y: 200 }, contact_msg: { x: 0, y: 400 }, btn_submit: { x: 400, y: 550 }, }; const path = [ { f: 0, ...targets.off }, { f: T.SELECT_WEBSITE, ...targets.type_website }, { f: T.NEXT_0, ...targets.btn_next }, { f: T.COMPANY_TYPE_START, ...targets.company_input }, { f: T.NEXT_1, ...targets.btn_next }, { f: T.URL_TYPE_START, ...targets.url_input }, { f: T.NEXT_2, ...targets.btn_next }, { f: T.SCOPE_CLICK_1, ...targets.scope_1 }, { f: T.SCOPE_CLICK_2, ...targets.scope_2 }, { f: T.SCOPE_CLICK_3, ...targets.scope_3 }, { f: T.NEXT_3, ...targets.btn_next }, { f: T.VIBE_SELECT, ...targets.vibe_tech }, { f: T.NEXT_4, ...targets.btn_next }, { f: T.NAME_TYPE_START, ...targets.contact_name }, { f: T.EMAIL_TYPE_START, ...targets.contact_email }, { f: T.MESSAGE_TYPE_START, ...targets.contact_msg }, { f: T.SUBMIT, ...targets.btn_submit }, { f: T.EXIT, ...targets.off }, ]; let idx = 0; for (let i = 0; i < path.length; i++) { if (frame >= path[i].f) idx = i; } const p1 = path[idx]; const p2 = path[idx + 1] || p1; const s = spring({ frame: frame - p1.f, fps, config: { stiffness: 60, damping: 25 }, }); return { x: interpolate(s, [0, 1], [p1.x, p2.x]), y: interpolate(s, [0, 1], [p1.y, p2.y]), }; }, [frame, width, height, fps, T]); const isClicking = useMemo(() => { const clicks = [ T.SELECT_WEBSITE, T.NEXT_0, T.NEXT_1, T.NEXT_2, T.SCOPE_CLICK_1, T.SCOPE_CLICK_2, T.SCOPE_CLICK_3, T.NEXT_3, T.VIBE_SELECT, T.NEXT_4, T.SUBMIT, ]; return clicks.some((c) => frame >= c && frame < c + 8); }, [frame, T]); return (
{/* Logo HUD */}
); };