103 lines
3.0 KiB
TypeScript
103 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
|
|
interface CounterProps {
|
|
end: number;
|
|
duration?: number;
|
|
}
|
|
|
|
const Counter: React.FC<CounterProps> = ({ end, duration = 2000 }) => {
|
|
const [count, setCount] = useState(0);
|
|
const countRef = useRef<HTMLSpanElement>(null);
|
|
const [hasStarted, setHasStarted] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const observer = new IntersectionObserver(
|
|
([entry]) => {
|
|
if (entry.isIntersecting && !hasStarted) {
|
|
setHasStarted(true);
|
|
}
|
|
},
|
|
{ threshold: 0.1 }
|
|
);
|
|
|
|
if (countRef.current) {
|
|
observer.observe(countRef.current);
|
|
}
|
|
|
|
return () => observer.disconnect();
|
|
}, [hasStarted]);
|
|
|
|
useEffect(() => {
|
|
if (!hasStarted) return;
|
|
|
|
let startTimestamp: number | null = null;
|
|
const step = (timestamp: number) => {
|
|
if (!startTimestamp) startTimestamp = timestamp;
|
|
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
|
|
setCount(Math.floor(progress * end));
|
|
if (progress < 1) {
|
|
window.requestAnimationFrame(step);
|
|
}
|
|
};
|
|
window.requestAnimationFrame(step);
|
|
}, [hasStarted, end, duration]);
|
|
|
|
return <span ref={countRef}>{count}</span>;
|
|
};
|
|
|
|
const counterItems = [
|
|
{
|
|
icon: "/assets/imgs/icon/icon-1.png",
|
|
count: 950,
|
|
suffix: "k+",
|
|
text: "Projects Succefull"
|
|
},
|
|
{
|
|
icon: "/assets/imgs/icon/icon-2.png",
|
|
count: 256,
|
|
suffix: "k+",
|
|
text: "Happy Customers"
|
|
},
|
|
{
|
|
icon: "/assets/imgs/icon/icon-3.png",
|
|
count: 852,
|
|
suffix: "k+",
|
|
text: "Consultants Planing"
|
|
},
|
|
{
|
|
icon: "/assets/imgs/icon/icon-4.png",
|
|
count: 965,
|
|
suffix: "+",
|
|
text: "Awards Winners"
|
|
}
|
|
];
|
|
|
|
const CounterAreaTwo: React.FC = () => {
|
|
return (
|
|
<section className="counter-area-two">
|
|
<div className="container">
|
|
<ul className="counter-area-two__list">
|
|
{counterItems.map((item, index) => (
|
|
<li key={index} className="counter-area-two__item count-box">
|
|
<div className="counter-area-two__icon">
|
|
<img src={item.icon} alt="icon" />
|
|
</div>
|
|
<div className="counter-area-two__content">
|
|
<h3 className="counter-area-two__count">
|
|
<Counter end={item.count} />
|
|
{item.suffix}
|
|
</h3>
|
|
<p className="counter-area-two__text">{item.text}</p>
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default CounterAreaTwo;
|