From 24b00fefcd760eac0399fed2f6efdd215fe97b3a Mon Sep 17 00:00:00 2001 From: sazzadulalambd Date: Mon, 22 Jun 2026 17:12:33 +0600 Subject: [PATCH] feat: add AnimatedCounter component and update stats section with hero asset color adjustment --- src/app/page.tsx | 55 ++++++++++++++--------- src/components/ui/3d/HeroBackground3D.tsx | 2 +- src/components/ui/AnimatedCounter.tsx | 45 +++++++++++++++++++ 3 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 src/components/ui/AnimatedCounter.tsx diff --git a/src/app/page.tsx b/src/app/page.tsx index 37f58f3..d229726 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -4,6 +4,7 @@ import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import Image from 'next/image'; import HeroBackground3D from '@/components/ui/3d/HeroBackground3D'; +import AnimatedCounter from '@/components/ui/AnimatedCounter'; import CardIcon3D from '@/components/ui/3d/CardIcon3D'; import { Zap, ArrowRight, Bike, Wallet, Leaf, ShieldCheck, MapPin, Truck, ChevronRight, BarChart3, BatteryCharging, Clock, Activity } from 'lucide-react'; @@ -117,24 +118,34 @@ export default function LandingPage() {
- {/* Stats Section (Overlapping Hero) */} -
-
-
-
500+
-
Active Riders
-
-
-
200+
-
EV Fleet Size
-
-
-
18%
-
Avg. Investor ROI
-
-
-
50k+
-
Deliveries Done
+ {/* Stats Section */} +
+
+
+
+

+ +

+

Active Riders

+
+
+

+ +

+

EV Fleet Size

+
+
+

+ +

+

Avg. Investor ROI

+
+
+

+ +

+

Deliveries Done

+
@@ -166,7 +177,8 @@ export default function LandingPage() {
- {/* Service 2 */} + {/* Service 2 - FICO Investment - User will redesign this section */} + {/*
@@ -174,14 +186,15 @@ export default function LandingPage() {

FICO Investment

Invest in fractional EV ownership. Let us manage the fleet while you enjoy guaranteed monthly returns.

- {/* */} +
+ */} {/* Service 3 */}
diff --git a/src/components/ui/3d/HeroBackground3D.tsx b/src/components/ui/3d/HeroBackground3D.tsx index 95e127c..0bd7c80 100644 --- a/src/components/ui/3d/HeroBackground3D.tsx +++ b/src/components/ui/3d/HeroBackground3D.tsx @@ -119,7 +119,7 @@ function Battery({ position, scale = 1, rotation = [0, 0, 0] }: { position: [num {/* Main Battery Body */} - + {/* Top Cap */} diff --git a/src/components/ui/AnimatedCounter.tsx b/src/components/ui/AnimatedCounter.tsx new file mode 100644 index 0000000..51ae2fe --- /dev/null +++ b/src/components/ui/AnimatedCounter.tsx @@ -0,0 +1,45 @@ +'use client'; +import { useState, useEffect, useRef } from 'react'; + +export default function AnimatedCounter({ end, duration = 2000, suffix = "" }: { end: number, duration?: number, suffix?: string }) { + const [count, setCount] = useState(0); + const [hasStarted, setHasStarted] = useState(false); + const elementRef = useRef(null); + + useEffect(() => { + const observer = new IntersectionObserver((entries) => { + if (entries[0].isIntersecting && !hasStarted) { + setHasStarted(true); + } + }); + + if (elementRef.current) { + observer.observe(elementRef.current); + } + + return () => observer.disconnect(); + }, [hasStarted]); + + useEffect(() => { + if (!hasStarted) return; + + let startTime: number | null = null; + const animate = (currentTime: number) => { + if (!startTime) startTime = currentTime; + const progress = Math.min((currentTime - startTime) / duration, 1); + // Ease out cubic function for smooth deceleration + const easeOut = 1 - Math.pow(1 - progress, 3); + const currentCount = Math.floor(easeOut * end); + + setCount(currentCount); + + if (progress < 1) { + requestAnimationFrame(animate); + } + }; + + requestAnimationFrame(animate); + }, [end, duration, hasStarted]); + + return {count}{suffix}; +}