I'm using Lenis scroll for smooth scrolling and Barba.js for page transitions, they work together except if a page transition is triggered whilst lenis is still handling the scroll. For example user scrolls down, clicks link while scroll is gradually slowing down.
The bug means that the new page loads at the same scroll position as the page it left on, the window.scrollTo();
seems to work only when Lenis isn't still handling the scroll.
I have tried to stop Lenis using Scroll.stop();
or scrollInstance.stop();
in the beforeLeave
part of Barba.js but this causes the initial page to hard reload instead of the desired page transition.
I have been trying to resolve this for days now and I've gone blind to it, I must be missing something obvious so any help would be greatly appreciated.
Here is the code which has the bug:
<!--Lenis / Barba Scroll & Animations-->
<script>
const Scroll = {
constructor(options) {
// Create a new Lenis instance
this.lenis = new Lenis({
force: true, // This forces Lenis to scroll even when the cursor is over a WebGL element
...options
});
// Initialize the Scroll class
this.time = 0;
this.isActive = true;
this.init();
},
init() {
// Call the Lenis config method
this.lenis.config();
// Render the scroll
this.render();
// Handle the editor view
this.handleEditorView();
},
render() {
// Call the Lenis raf method
this.lenis.raf(() => {
// Update the time
this.time += 10;
// Render the next frame
window.requestAnimationFrame(this.render.bind(this));
});
},
// ... Other methods ...
config() {
// allow scrolling on overflow elements
const overscroll = [
...document.querySelectorAll('[data-scroll="overscroll"]')
];
if (overscroll.length > 0) {
overscroll.forEach((item) =>
item.setAttribute("onwheel", "event.stopPropagation()")
);
}
// stop and start scroll btns
const stop = [...document.querySelectorAll('[data-scroll="stop"]')];
if (stop.length > 0) {
stop.forEach((item) => {
item.onclick = () => {
this.stop();
this.isActive = false;
};
});
}
const start = [...document.querySelectorAll('[data-scroll="start"]')];
if (start.length > 0) {
start.forEach((item) => {
item.onclick = () => {
this.start();
this.isActive = true;
};
});
}
// toggle page scrolling
const toggle = [...document.querySelectorAll('[data-scroll="toggle"]')];
if (toggle.length > 0) {
toggle.forEach((item) => {
item.onclick = () => {
if (this.isActive) {
this.stop();
this.isActive = false;
} else {
this.start();
this.isActive = true;
}
};
});
}
// anchor links
const anchor = [...document.querySelectorAll("[data-scrolllink]")];
if (anchor.length > 0) {
anchor.forEach((item) => {
const id = parseFloat(item.dataset.scrolllink);
const target = document.querySelector(`[data-scrolltarget="${id}"]`);
if (target) {
//console.log(id, target);
item.onclick = () => this.scrollTo(target);
}
});
}
},
handleEditorView() {
const html = document.documentElement;
const config = { attributes: true, childList: false, subtree: false };
const callback = (mutationList, observer) => {
for (const mutation of mutationList) {
if (mutation.type === "attributes") {
const btn = document.querySelector(".w-editor-bem-EditSiteButton");
const bar = document.querySelector(".w-editor-bem-EditorMainMenu");
const addTrig = (target) =>
target.addEventListener("click", () => this.destroy());
if (btn) addTrig(btn);
if (bar) addTrig(bar);
}
}
};
const observer = new MutationObserver(callback);
observer.observe(html, config);
}
};
// Create a new Scroll instance and assign it to a variable
const scrollInstance = scroll();
// Barba JS
var pageID;
var introOverlay = document.querySelector('.intro-overlay');
barba.init({
transitions: [{
sync: true,
beforeLeave: function(data) {
let end = data.next.html.indexOf(' data-wf-site="');
let start = data.next.html.indexOf('data-wf-page="');
let string = data.next.html.slice(start, end);
let arr = string.split('"');
pageID = arr[1];
const done = this.async();
gsap.to(data.current.container, {
opacity: 0,
duration: 0.5,
onComplete: done,
});
},
leave: function(data) {
const done = this.async();
done();
},
beforeEnter: function(data) {
$('html').attr('data-wf-page', pageID);
window.Webflow && window.Webflow.destroy();
window.Webflow && window.Webflow.ready();
window.Webflow && window.Webflow.require('ix2').init();
window.Webflow && window.Webflow.require('lottie').lottie;
if (introOverlay) {
introOverlay.style.display = 'none';
}
window.scrollTo(0, 1);
window.scrollTo(0, 0);
$("[js-line-animation-delayed]").each(function() {
$(this).removeAttr("js-line-animation-delayed");
$(this).attr("js-line-animation", true);
});
},
afterEnter: function(data) {
setTimeout(() => {
$("[js-line-animation]").each(function(index) {
gsap.set($(this), {
autoAlpha: 1
});
let textEl = $(this);
let textContent = $(this).text();
let tl;
function splitText() {
new SplitType(textEl, {
types: "lines",
tagName: "span"
});
textEl.find(".line").each(function(index) {
let lineContent = $(this).html();
$(this).html("");
$(this).append(`<span class="line-inner" style="display: block;">${lineContent}</span>`);
});
tl = gsap.timeline({
scrollTrigger: {
trigger: textEl,
start: "top bottom",
end: "bottom bottom",
toggleActions: "none play none reset"
}
});
tl.fromTo(textEl.find(".line-inner"), {
yPercent: 100
}, {
yPercent: 0,
duration: 0.6,
stagger: {
amount: 0.4,
ease: "power1.out"
}
});
}
splitText();
let windowWidth = window.innerWidth;
window.addEventListener("resize", function() {
if (windowWidth !== window.innerWidth) {
windowWidth = window.innerWidth;
tl.kill();
textEl.text(textContent);
splitText();
}
});
});
barbaHeroHome();
}, 100);
}
}]
});
</script>