Barba JS and Lenis Scroll conflict

317 views Asked by At

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>
0

There are 0 answers