import React from "react"; import { AbsoluteFill, interpolate, useCurrentFrame, useVideoConfig, Easing, Img, spring, } from "remotion"; import { MouseCursor } from "../components/MouseCursor"; import { Button } from "@/src/components/Button"; import { Loader2, Check, ShieldCheck } from "lucide-react"; // Import logo using the alias setup in remotion.config.ts import IconBlack from "@/src/assets/logo/Icon Black Transparent.svg"; const Background: React.FC<{ loadingOpacity: number }> = ({ loadingOpacity, }) => { return ( {/* Website-Matching Grid */}
{/* Subtle Gradient Overlay */}
{/* Dynamic "Processing" Rings (Background Activity during loading) */}
{/* STATIC Logo - Strictly no animation on the container */}
Mintel.me Component Library
); }; // Toast Notification Component const Toast: React.FC<{ _show: boolean; _text: string }> = ({ _show, _text, }) => { const _frame = useCurrentFrame(); const _fps = useVideoConfig(); return (
Authentication Successful Access granted to secure portal
); }; export const ButtonShowcase: React.FC = () => { const frame = useCurrentFrame(); const { width, height, fps } = useVideoConfig(); // ---- SEQUENCE TIMELINE (300 frames / 5s) ---- const ENTER_START = 20; const HOVER_START = 70; const CLICK_FRAME = 90; const LOADING_START = 95; const SUCCESS_START = 180; const TOAST_START = 190; const TOAST_END = 260; const EXIT_START = 220; // 1. Mouse Animation (Bézier Path) const getMousePos = (f: number) => { const startX = width * 1.2; const startY = height * 1.2; const targetX = width / 2; const targetY = height / 2; if (f < ENTER_START) return { x: startX, y: startY, vx: 0 }; // Approach if (f < HOVER_START) { const t = interpolate(f, [ENTER_START, HOVER_START], [0, 1], { extrapolateRight: "clamp", }); const ease = Easing.bezier(0.16, 1, 0.3, 1)(t); const x = interpolate(ease, [0, 1], [startX, targetX]); const y = interpolate(ease, [0, 1], [startY, targetY]); return { x, y, vx: -10 }; // Approximate Velocity } // Hover if (f < EXIT_START) { const noise = Math.sin(f * 0.1) * 2; return { x: targetX + noise, y: targetY + noise, vx: 0 }; } // Exit const t = interpolate(f, [EXIT_START, EXIT_START + 30], [0, 1], { extrapolateLeft: "clamp", }); const ease = Easing.exp(t); return { x: interpolate(ease, [0, 1], [targetX, width * 0.9]), y: interpolate(ease, [0, 1], [targetY, -100]), vx: 10, }; }; const { x: mouseX, y: mouseY, vx } = getMousePos(frame); // 3D Cursor Skew const cursorSkew = interpolate(vx, [-20, 20], [20, -20], { extrapolateRight: "clamp", extrapolateLeft: "clamp", }); const isClicking = frame >= CLICK_FRAME && frame < CLICK_FRAME + 8; const clickRotate = isClicking ? 30 : 0; // 2. Button State Logic const isLoading = frame >= LOADING_START && frame < SUCCESS_START; const isSuccess = frame >= SUCCESS_START; // Loading Spinner Rotation const spinnerRot = interpolate( frame, [LOADING_START, SUCCESS_START], [0, 720], ); // Button Scale Physics const pressSpring = spring({ frame: frame - CLICK_FRAME, fps, config: { stiffness: 400, damping: 20 }, }); const successSpring = spring({ frame: frame - SUCCESS_START, fps, config: { stiffness: 200, damping: 15 }, }); // Morph scale: Click(Compress) -> Loading(Normal) -> Success(Pop) let buttonScale = 1; if (frame >= CLICK_FRAME && frame < LOADING_START) { buttonScale = 1 - pressSpring * 0.05; } else if (isSuccess) { buttonScale = 1 + successSpring * 0.05; } // 3. Toast Animation const toastSpring = spring({ frame: frame - TOAST_START, fps, config: { stiffness: 100, damping: 15 }, }); const toastExit = spring({ frame: frame - TOAST_END, fps, config: { stiffness: 100, damping: 20 }, }); const toastY = interpolate(toastSpring, [0, 1], [100, -80]) + interpolate(toastExit, [0, 1], [0, 200]); const toastOpacity = interpolate(toastSpring, [0, 1], [0, 1]) - interpolate(toastExit, [0, 0.5], [0, 1]); return ( {/* Main Stage */}
{/* Button Container */}
{/* Toast Notification Layer - Bottom Center */}
{/* 3D Cursor */}
); };