<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>
<script>
gsap.registerPlugin(ScrollTrigger);
const lenis = new Lenis({
duration: 1.5,
easing: (t) => 1 - Math.pow(1 - t, 3),
smooth: true,
smoothTouch: false, // β
important
});
// sync
lenis.on('scroll', ScrollTrigger.update);
// raf loop
function raf(time) {
lenis.raf(time);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);
// β
correct scroller
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,
};
},
});
// β
important
ScrollTrigger.defaults({
scroller: document.documentElement,
});
// refresh fix
ScrollTrigger.addEventListener('refresh', () => lenis.resize());
ScrollTrigger.refresh();
</script>
<script>
gsap.registerPlugin(ScrollTrigger);
gsap.utils.toArray(".counter").forEach(counter => {
const finalText = counter.textContent.trim();
const endValue = parseFloat(finalText.replace(/[^0-9.-]/g, ""));
const suffix = finalText.replace(/[0-9.-]/g, "");
const decimals = finalText.includes(".") ? 1 : 0;
// Reset to zero
counter.textContent = "0" + suffix;
const obj = { value: 0 };
gsap.to(obj, {
value: endValue,
duration: 2,
ease: "power2.out",
scrollTrigger: {
trigger: counter,
start: "top 80%",
once: true
},
onUpdate() {
counter.textContent =
obj.value.toFixed(decimals) + suffix;
}
});
});
</script><script>
document.addEventListener("DOMContentLoaded", () => {
// ====================================
// ELEMENTS
// ====================================
const cards = document.querySelectorAll(
".single-testimonials-clint-image-wrap"
);
const contents = document.querySelectorAll(
".single-testimonials-wrap"
);
const borders = document.querySelectorAll(
".single-testimonials-clint-image-border"
);
const DURATION = 5;
let current = 0;
let masterTL;
// ====================================
// INITIAL STATE
// ====================================
gsap.set(cards, {
opacity: 0.35,
scale: 0.75
});
gsap.set(cards[0], {
opacity: 1,
scale: 1
});
gsap.set(contents, {
opacity: 0,
y: 40,
position: "absolute",
top: 0,
left: 0,
width: "100%",
display: "none"
});
gsap.set(contents[0], {
opacity: 1,
y: 0,
display: "block"
});
gsap.set(borders, {
opacity: 0,
clipPath: "inset(0 100% 0 0)"
});
// ====================================
// WRAPPER HEIGHT FIX
// ====================================
const wrapper = document.querySelector(
".testimonials-contant-wrapper"
);
wrapper.style.position = "relative";
wrapper.style.minHeight =
contents[0].offsetHeight + "px";
// ====================================
// GO TO TAB
// ====================================
function goTo(index) {
if (index === current && masterTL) return;
if (masterTL) {
masterTL.kill();
}
const previous = current;
// ====================================
// TIMELINE
// ====================================
masterTL = gsap.timeline({
onComplete: () => {
const next =
(index + 1) % cards.length;
goTo(next);
}
});
// ====================================
// IMAGE RESET
// ====================================
masterTL.to(
cards,
{
opacity: 0.35,
scale: 0.75,
duration: 0.5,
ease: "power2.out"
},
0
);
// Active image
masterTL.to(
cards[index],
{
opacity: 1,
scale: 1,
duration: 0.6,
ease: "back.out(1.7)"
},
0
);
// ====================================
// BORDER RESET
// ====================================
gsap.set(borders, {
opacity: 0,
clipPath: "inset(0 100% 0 0)"
});
gsap.set(borders[index], {
opacity: 1
});
// Progress animation
masterTL.to(
borders[index],
{
clipPath: "inset(0 0% 0 0)",
duration: DURATION,
ease: "linear"
},
0
);
// ====================================
// OLD CONTENT OUT
// ====================================
if (contents[previous]) {
masterTL.to(
contents[previous],
{
opacity: 0,
y: -40,
duration: 0.4,
ease: "power2.out",
onComplete: () => {
gsap.set(
contents[previous],
{
display: "none"
}
);
}
},
0
);
}
// ====================================
// NEW CONTENT IN
// ====================================
gsap.set(contents[index], {
display: "block"
});
wrapper.style.minHeight =
contents[index].offsetHeight + "px";
masterTL.fromTo(
contents[index],
{
opacity: 0,
y: 40
},
{
opacity: 1,
y: 0,
duration: 0.8,
ease: "power3.out"
},
0.2
);
// ====================================
// TEXT STAGGER
// ====================================
masterTL.fromTo(
contents[index].children,
{
opacity: 0,
y: 20
},
{
opacity: 1,
y: 0,
stagger: 0.08,
duration: 0.5,
ease: "power2.out"
},
0.3
);
current = index;
}
// ====================================
// CLICK EVENT
// ====================================
cards.forEach((card, index) => {
card.addEventListener("click", () => {
goTo(index);
});
});
// ====================================
// START
// ====================================
current = -1;
goTo(0);
});
</script><script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script>
window.addEventListener("load", () => {
const preloader = document.querySelector(".prelader-wrapper");
const counter = document.querySelector(".prelader-counter-number");
const progressBar = document.querySelector(".preloader-progress-bar");
const preloaderBg = document.querySelector(".prelader-background");
preloader.style.display = "flex";
let count = { value: 0 };
// π₯ Start state fix (important)
counter.innerText = "0";
progressBar.style.width = "0%";
const tl = gsap.timeline();
tl.to(preloaderBg, {
clipPath: "circle(150% at 100% 0%)",
duration: 1.4,
ease: "power4.inOut"
})
.from(".prelader-wrap", {
opacity: 0,
y: 40,
duration: 0.8,
ease: "power3.out"
})
.to(count, {
value: 100,
duration: 3,
ease: "power2.out",
onUpdate: () => {
let val = Math.floor(count.value);
counter.innerText = val; // β starts from 0
progressBar.style.width = val + "%";
}
})
.to(".prelader-wrap", {
opacity: 0,
y: -40,
duration: 0.6
})
.to(preloaderBg, {
clipPath: "circle(0% at 100% 0%)",
duration: 1.2
})
.to(preloader, {
opacity: 0,
duration: 0.4,
onComplete: () => {
preloader.style.display = "none";
}
});
});
</script>We are a results-driven digital agency dedicated to helping brands grow
