Instruction
Lenis smooth scroll & counter animation
In this template we used Lenis Smooth Scroll library, if you want to make site scroll faster or even smoother tweak values of 'wheel multiplier' and 'lerp' in site settings custom code tab!
If you want to remove smooth scroll, remove this script code from site settings.
<link rel="stylesheet" href="https://unpkg.com/lenis@1.3.20/dist/lenis.css" />
<script src="https://unpkg.com/lenis@1.3.20/dist/lenis.min.js"></script>
<!-- [Attributes by Finsweet] CMS Filter -->
<script async src="https://cdn.jsdelivr.net/npm/@finsweet/attributes-cmsfilter@1/cmsfilter.js"></script>
<!-- Flowbase Booster [Tab Rotation] -->
<script src="https://cdn.jsdelivr.net/npm/@flowbase-co/boosters-tab-rotation-webflow@1.2.0/dist/tab-rotation.min.js" type="text/javascript"></script>
<script>
window.Webflow = window.Webflow || [];
window.Webflow.push(() => {
gsap.registerPlugin(ScrollTrigger);
// =========================
// Lenis
// =========================
const lenis = new Lenis({
duration: 0.9,
easing: (t) => 1 - Math.pow(1 - t, 3),
smoothWheel: true,
touchMultiplier: 1,
wheelMultiplier: 1
});
function raf(time) {
lenis.raf(time);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);
lenis.on("scroll", ScrollTrigger.update);
ScrollTrigger.scrollerProxy(document.documentElement, {
scrollTop(value) {
return arguments.length
? lenis.scrollTo(value, { immediate: true })
: lenis.scroll;
},
getBoundingClientRect() {
return {
top: 0,
left: 0,
width: window.innerWidth,
height: window.innerHeight
};
}
});
ScrollTrigger.defaults({
scroller: document.documentElement
});
ScrollTrigger.addEventListener("refresh", () => {
lenis.resize();
});
// =========================
// SCROLL COUNTER
// Attribute:
// counter-animation
// =========================
document.querySelectorAll("[counter-animation]").forEach(counter => {
const text = counter.textContent.trim();
const match = text.match(/-?\d[\d,]*(\.\d+)?/);
if (!match) return;
const rawNumber = match[0];
const number = parseFloat(rawNumber.replace(/,/g, ""));
const prefix = text.slice(0, match.index);
const suffix = text.slice(match.index + rawNumber.length);
const decimals = rawNumber.includes(".")
? rawNumber.split(".")[1].length
: 0;
const obj = { value: 0 };
gsap.to(obj, {
value: number,
duration: 2,
ease: "power3.out",
snap: {
value: decimals
? Math.pow(10, -decimals)
: 1
},
scrollTrigger: {
trigger: counter,
start: "top 85%",
once: true
},
onUpdate() {
const value = decimals
? obj.value.toFixed(decimals)
: Math.round(obj.value).toLocaleString();
counter.textContent =
`${prefix}${value}${suffix}`;
}
});
});
// =========================
// PAGE LOAD COUNTER
// Attribute:
// counter-page-load
//
// Example:
// counter-page-load="100+"
// counter-page-load="98%"
// counter-page-load="1.9K+"
// =========================
setTimeout(() => {
document.querySelectorAll("[counter-page-load]").forEach(counter => {
const targetText =
counter.getAttribute("counter-page-load");
if (!targetText) return;
const match =
targetText.match(/-?\d[\d,]*(\.\d+)?/);
if (!match) return;
const rawNumber = match[0];
const number =
parseFloat(rawNumber.replace(/,/g, ""));
const prefix =
targetText.slice(0, match.index);
const suffix =
targetText.slice(match.index + rawNumber.length);
const decimals =
rawNumber.includes(".")
? rawNumber.split(".")[1].length
: 0;
const obj = {
value: 0
};
gsap.to(obj, {
value: number,
duration: 1,
ease: "power3.out",
snap: {
value: decimals
? Math.pow(10, -decimals)
: 1
},
onUpdate() {
const value = decimals
? obj.value.toFixed(decimals)
: Math.round(obj.value).toLocaleString();
counter.textContent =
`${prefix}${value}${suffix}`;
}
});
});
}, 2000);
// =========================
// ScrollTrigger Refresh
// =========================
setTimeout(() => {
lenis.resize();
ScrollTrigger.refresh();
}, 600);
});
</script>