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 */}
);
};