Files
Evandro Fonseca Junior 08dd046887 feat: add animated components for enhanced UI/UX
- Introduced DrawLine component for drawing animations.
- Added LazyRevealImage component for lazy loading images with reveal animations.
- Implemented TextDiffuseReveal for animated text reveals with staggered effects.
- Created TopProgressBar for a dynamic loading indicator.
- Added PointCloud mock for testing animations.
- Developed useReducedMotion hook to handle user preferences for reduced motion.
- Centralized animation configurations in config.ts for easier management.
- Defined animation variants in variants.ts for consistent animation effects.
- Established AnimationProvider to supply global animation settings.
- Created tests for AnimationProvider and useReducedMotion hook.
- Added SVG assets for branding and UI elements.
- Documented frontend review plan and hero circuit board animation plan.
2026-03-15 21:06:43 -03:00

64 lines
1.4 KiB
TypeScript

"use client";
import { useEffect, useRef, useState } from "react";
import { useInView, useReducedMotion } from "framer-motion";
interface CounterValueProps {
value: number;
className?: string;
duration?: number;
prefix?: string;
suffix?: string;
once?: boolean;
amount?: number;
}
export function CounterValue({
value,
className = "",
duration = 1200,
prefix = "",
suffix = "",
once = true,
amount = 0.45,
}: Readonly<CounterValueProps>) {
const shouldReduceMotion = useReducedMotion();
const ref = useRef<HTMLSpanElement | null>(null);
const isInView = useInView(ref, { once, amount });
const [displayValue, setDisplayValue] = useState(0);
useEffect(() => {
if (!isInView) return;
if (shouldReduceMotion) {
setDisplayValue(value);
return;
}
const start = performance.now();
let rafId = 0;
const frame = (now: number) => {
const elapsed = now - start;
const progress = Math.min(elapsed / duration, 1);
const eased = 1 - Math.pow(1 - progress, 3);
setDisplayValue(Math.round(value * eased));
if (progress < 1) {
rafId = requestAnimationFrame(frame);
}
};
rafId = requestAnimationFrame(frame);
return () => cancelAnimationFrame(rafId);
}, [isInView, value, duration, shouldReduceMotion]);
return (
<span ref={ref} className={className}>
{prefix}
{displayValue}
{suffix}
</span>
);
}