Professional Web Designer and WordPress Elementor Expert working on modern website design
Code

Scroll

<!– ============================================================
PASTE INTO: Elementor → Custom Code → Body End
Only handles scroll pinning — Elementor controls all card styles
============================================================ –>

<style>
/* Only what’s needed to make pinning work cleanly */
.parents-container {
overflow: hidden !important;
transition: none !important;
}

.parents-container .swiper-wrapper {
will-change: transform;
transition: none !important;
}
</style>

<link rel=”stylesheet” href=”https://unpkg.com/[email protected]/dist/lenis.css” />
<script src=”https://unpkg.com/[email protected]/dist/lenis.min.js”></script>
<script src=”https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js”></script>
<script src=”https://cdn.jsdelivr.net/npm/[email protected]/dist/ScrollTrigger.min.js”></script>

<script>
(function () {
“use strict”;

var SECTION = “.parents-container”;

window.addEventListener(“load”, function () {
setTimeout(init, 800);
});

/* ── Lenis (desktop only) ── */
var lenis;
function startLenis() {
if (window.innerWidth <= 1024 || lenis || typeof Lenis === “undefined”) return;
lenis = new Lenis({ duration: 1.4 });
(function raf(t) { lenis.raf(t); requestAnimationFrame(raf); })(0);

ScrollTrigger.scrollerProxy(document.documentElement, {
scrollTop: function (v) {
if (arguments.length) lenis.scrollTo(v, { immediate: true });
return window.scrollY;
},
getBoundingClientRect: function () {
return { top: 0, left: 0, width: window.innerWidth, height: window.innerHeight };
},
fixedMarkers: true
});
lenis.on(“scroll”, ScrollTrigger.update);
}

/* ── Main init ── */
function init() {
gsap.registerPlugin(ScrollTrigger);

var section = document.querySelector(SECTION);
if (!section) {
console.error(“[HScroll] ❌ .parents-container not found”);
return;
}

/* Find the swiper wrapper — Elementor Carousel renders it */
var wrapper = section.querySelector(“.swiper-wrapper”);
if (!wrapper) {
console.error(“[HScroll] ❌ .swiper-wrapper not found”);
return;
}

var slides = wrapper.querySelectorAll(“.swiper-slide”);
console.log(“[HScroll] Found”, slides.length, “slides”);
console.log(“[HScroll] wrapper.scrollWidth:”, wrapper.scrollWidth);

/* ── Disable Swiper touch/drag — GSAP scroll takes over ── */
var swiperEl = section.querySelector(“.swiper”);
if (swiperEl && swiperEl.swiper) {
swiperEl.swiper.allowTouchMove = false;
swiperEl.swiper.allowSlideNext = false;
swiperEl.swiper.allowSlidePrev = false;
console.log(“[HScroll] Swiper touch disabled ✓”);
}

startLenis();

if (window.matchMedia(“(max-width: 1024px)”).matches) {
ScrollTrigger.normalizeScroll(true);
}

buildScroll(section, wrapper);
}

/* ── Measure real overflow from DOM ── */
function measure(section, wrapper, extra) {
extra = extra || 0;

var saved = wrapper.style.transform;
wrapper.style.transform = “translate3d(0px,0px,0px)”;

var sRect = section.getBoundingClientRect();
var lastEl = wrapper.lastElementChild;
var lastRect = lastEl.getBoundingClientRect();
var padR = parseFloat(getComputedStyle(section).paddingRight) || 0;

var overflow = lastRect.right – (sRect.right – padR);
var result = Math.max(0, Math.ceil(overflow) + extra);

wrapper.style.transform = saved;

console.log(“[HScroll] Measured overflow:”, result, “px”);
return result;
}

/* ── Build ScrollTrigger ── */
function buildScroll(section, wrapper) {
var tl;
var mm = gsap.matchMedia();

function make(extra) {
ScrollTrigger.getAll().forEach(function (st) {
if (st.vars && st.vars.trigger === section) st.kill();
});

gsap.set(wrapper, { x: 0 });
var endLen = measure(section, wrapper, extra);

if (endLen <= 0) {
console.warn(“[HScroll] ⚠️ endLen is 0 — retrying in 600ms”);
setTimeout(function () { buildScroll(section, wrapper); }, 600);
return;
}

tl = gsap.timeline({
scrollTrigger: {
trigger: section,
start: “top top”,
end: “+=” + endLen,
scrub: true,
pin: true,
pinSpacing: true,
pinType: window.matchMedia(“(max-width: 767px)”).matches
? “transform”
: “fixed”,
anticipatePin: 1,
fastScrollEnd: true,
invalidateOnRefresh: true,
onRefreshInit: function () { gsap.set(wrapper, { x: 0 }); },
onRefresh: function () {
endLen = measure(section, wrapper, extra);
tl.scrollTrigger.end = tl.scrollTrigger.start + endLen;
}
}
}).to(wrapper, {
x: -endLen,
ease: “none”,
immediateRender: false
});

console.log(“[HScroll] 🟢 Running | scrolls”, endLen + “px”);
}

mm.add(“(max-width: 767px)”, function () { make(0); });
mm.add(“(min-width: 768px) and (max-width: 1024px)”, function () { make(20); });
mm.add(“(min-width: 1025px)”, function () { make(80); });
}

/* ── Resize ── */
var rTO;
window.addEventListener(“resize”, function () {
clearTimeout(rTO);
rTO = setTimeout(function () { startLenis(); ScrollTrigger.refresh(); }, 150);
});

})();
</script>

Ready to Grow Your Business?

Whether you need local SEO to get found by nearby customers or a professional website that converts, we’re here to help. Check out our transparent pricing or learn more about our process.

Related Articles

Share the Post:

Related Articles