first commit
This commit is contained in:
145
src/components/home/Stats.tsx
Normal file
145
src/components/home/Stats.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { statsData, serviceListData, commonDescriptions } from '../../data/siteData'
|
||||
|
||||
function useCountUp(end: number, duration: number = 2000, startCounting: boolean = false) {
|
||||
const [count, setCount] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (!startCounting) return
|
||||
|
||||
let startTime: number
|
||||
let animationFrame: number
|
||||
|
||||
const animate = (currentTime: number) => {
|
||||
if (!startTime) startTime = currentTime
|
||||
const progress = Math.min((currentTime - startTime) / duration, 1)
|
||||
setCount(Math.floor(progress * end))
|
||||
|
||||
if (progress < 1) {
|
||||
animationFrame = requestAnimationFrame(animate)
|
||||
}
|
||||
}
|
||||
|
||||
animationFrame = requestAnimationFrame(animate)
|
||||
return () => cancelAnimationFrame(animationFrame)
|
||||
}, [end, duration, startCounting])
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
function StatItem({ value, label, startCounting }: { value: number; label: string; startCounting: boolean }) {
|
||||
const count = useCountUp(value, 2000, startCounting)
|
||||
return (
|
||||
<div className="stats-1-left">
|
||||
<h4 className="text-[36px] font-semibold mb-1.5 font-sans" style={{ color: '#ffffff' }}>{count}</h4>
|
||||
<h6 className="text-lg" style={{ color: '#ffffff' }}>{label}</h6>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Stats() {
|
||||
const sectionRef = useRef<HTMLElement>(null)
|
||||
const [startCounting, setStartCounting] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries[0].isIntersecting) {
|
||||
setStartCounting(true)
|
||||
observer.disconnect()
|
||||
}
|
||||
},
|
||||
{ threshold: 0.3 }
|
||||
)
|
||||
|
||||
if (sectionRef.current) {
|
||||
observer.observe(sectionRef.current)
|
||||
}
|
||||
|
||||
return () => observer.disconnect()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<section
|
||||
ref={sectionRef}
|
||||
className="py-16 relative"
|
||||
id="stats"
|
||||
style={{
|
||||
backgroundImage: "url('/src/assets/images/2.jpg')",
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
>
|
||||
{/* Overlay */}
|
||||
<div
|
||||
className="absolute inset-0 z-0"
|
||||
style={{ background: 'rgba(10, 30, 80, 0.92)' }}
|
||||
/>
|
||||
|
||||
<div className="container mx-auto px-4 py-3 relative z-10">
|
||||
{/* Section Header */}
|
||||
<div className="text-center max-w-3xl mx-auto mb-8">
|
||||
<h3 className="text-3xl md:text-4xl font-bold mb-4 font-sans" style={{ color: '#ffffff' }}>
|
||||
{commonDescriptions.statsTitle}
|
||||
</h3>
|
||||
<p className="my-3" style={{ color: '#d0d0d0' }}>
|
||||
{commonDescriptions.sectionDesc}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap lg:flex-nowrap gap-12 pt-8 mt-3">
|
||||
{/* Left Content */}
|
||||
<div className="w-full lg:w-5/12">
|
||||
<h4 className="text-[38px] leading-[46px] font-semibold mb-4 font-sans" style={{ color: '#ffffff' }}>
|
||||
{commonDescriptions.statsSubtitle}
|
||||
</h4>
|
||||
<p className="mt-2.5 text-base leading-6" style={{ color: '#d0d0d0' }}>
|
||||
{commonDescriptions.statsDesc}
|
||||
</p>
|
||||
<p className="mt-2.5 text-base leading-6" style={{ color: '#d0d0d0' }}>
|
||||
{commonDescriptions.statsDesc2}
|
||||
</p>
|
||||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-3 gap-2.5 mt-12">
|
||||
{statsData.map((stat) => (
|
||||
<StatItem
|
||||
key={stat.id}
|
||||
value={stat.value}
|
||||
label={stat.label}
|
||||
startCounting={startCounting}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Content - Service List */}
|
||||
<div className="w-full lg:w-7/12 my-8 lg:my-0">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-[30px]">
|
||||
{serviceListData.map((service) => (
|
||||
<div
|
||||
key={service.id}
|
||||
className="group stats-service-card flex gap-4 p-10 rounded"
|
||||
>
|
||||
<div className="flex-shrink-0">
|
||||
<span className={`fa fa-${service.icon} text-[32px] text-secondary transition-colors duration-300`} />
|
||||
</div>
|
||||
<div>
|
||||
<h6 className="font-bold mb-2.5 service-title">
|
||||
<a href="#url" className="text-white group-hover:text-title transition-colors duration-300">
|
||||
{service.title}
|
||||
</a>
|
||||
</h6>
|
||||
<p className="text-[15px] leading-[25px] mt-2.5 text-[#d0d0d0] group-hover:text-title transition-colors duration-300">
|
||||
{service.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user