feat: implement Lenis smooth scroll and animated stat counters

- Add Lenis smooth scroll library (Landing page only)
- Create SmoothScroll wrapper component with GSAP integration
- Implement animated stat counters with scroll-triggered count-up
- Add GSAP animations for stats section (40%, 5yrs, 15m)
- Update package.json with lenis dependency
This commit is contained in:
Satyam
2026-02-14 22:54:57 +05:30
parent 6d3b1c231a
commit e88e1e4da5
5 changed files with 230 additions and 52 deletions
+53 -3
View File
@@ -25,6 +25,7 @@ const Landing = () => {
const heroRef = useRef(null);
const textRef = useRef(null);
const sectionsRef = useRef([]);
const statRefs = useRef([]);
const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false);
useEffect(() => {
@@ -55,6 +56,34 @@ const Landing = () => {
);
});
// Animated Stat Counters
statRefs.current.forEach((el) => {
if (!el) return;
const target = parseFloat(el.dataset.target);
const suffix = el.dataset.suffix || '';
gsap.fromTo(el,
{ innerText: 0 },
{
innerText: target,
duration: 2,
ease: "power1.out",
scrollTrigger: {
trigger: el,
start: "top 85%",
toggleActions: "play none none none",
},
snap: { innerText: suffix === '%' ? 1 : 0.1 },
onUpdate: function () {
const value = this.targets()[0].innerText;
const formattedValue = suffix === '%' ? Math.ceil(value) : Math.ceil(value);
el.innerText = formattedValue + suffix;
}
}
);
});
}, []);
const addToRefs = (el) => {
@@ -325,17 +354,38 @@ const Landing = () => {
<div className="grid md:grid-cols-3 gap-8">
<div className="bg-white/5 p-8 rounded-3xl border border-white/10 hover:bg-white/10 transition-colors">
<div className="text-4xl font-black text-red-500 mb-2">40%</div>
<div
ref={(el) => statRefs.current[0] = el}
data-target="40"
data-suffix="%"
className="text-4xl font-black text-red-500 mb-2"
>
0%
</div>
<h3 className="font-bold text-xl mb-2">Insurance Denials</h3>
<p className="text-zinc-400 text-sm">Of claims are denied due to "lack of maintenance" if proof of regular inspection isn't provided.</p>
</div>
<div className="bg-white/5 p-8 rounded-3xl border border-white/10 hover:bg-white/10 transition-colors">
<div className="text-4xl font-black text-yellow-500 mb-2">5yrs</div>
<div
ref={(el) => statRefs.current[1] = el}
data-target="5"
data-suffix="yrs"
className="text-4xl font-black text-yellow-500 mb-2"
>
0yrs
</div>
<h3 className="font-bold text-xl mb-2">Life Reduced</h3>
<p className="text-zinc-400 text-sm">A minor leak left for 6 months can reduce your roof's lifespan by up to 5 years.</p>
</div>
<div className="bg-white/5 p-8 rounded-3xl border border-white/10 hover:bg-white/10 transition-colors">
<div className="text-4xl font-black text-green-500 mb-2">15m</div>
<div
ref={(el) => statRefs.current[2] = el}
data-target="15"
data-suffix="m"
className="text-4xl font-black text-green-500 mb-2"
>
0m
</div>
<h3 className="font-bold text-xl mb-2">Our Estimate Time</h3>
<p className="text-zinc-400 text-sm">While others take days, we give you a quote before our drone even lands.</p>
</div>