Welcome animation conflict with barba js

I am having an issue with a site I am working on. I am looking to call a function on the first initial load of the web page (not between internal navigation of pages. Initially I had this scripted tied to a function called playWelcomeAnimation() where it set a GSAP timeline, runs an intro animation, then on completion calls another function and sets a class to the body tag. This worked fine. Well this worked out just fine, until I began to introduce Barba JS into the fold to handle internal page transitions. Once this went into place, several bugs were introduced which I for the life of me cannot resolve.

  1. I want to make it so the welcome animation ONLY plays on the first load of the website, not between or after internal page transitions or links.
  2. I want to make it so Barba JS does not initialize on the first load of the website, as it seems to introduce multiple duplicate function calls and just isn’t what I want at all.
  3. For whatever reason, the intro GSAP animations within the playWelcomeAnimation() timeline no longer work. They do not completely fade in, the opacity stays only about a quarter of what it should be. The setIntro() function which calls another function to add divs with specified classes for placement. All these things which lead to several SVG animations giving that classic “flash era” web intro.

My website URL is Located at this URL

My full javascript source code is as follows

document.documentElement.classList.remove('no-js');
sessionStorage.clear();
let firstLoad = true;  // Tracks the first page load across sessions
let firstTime = true;  // For Barba.js initial load
console.log('Registering DOMContentLoaded listener');
// Wait until DOM is fully loaded for anything that relies on DOM elements
document.addEventListener("DOMContentLoaded", function () {
    console.log('DOM fully loaded and parsed');
    const body = document.body;
    const html = document.documentElement;
    const getUrl = window.location;
    const getHomeUrl = `${getUrl.protocol}//${getUrl.host}`;
    const scrollTxt = document.querySelector('.scroll-txt');
    const marquee = document.querySelectorAll('.marquee');
    const cursor = document.querySelector('.cursor');
    const loadingElement = document.querySelector('.loading');
    const $inner = document.querySelector('.intro-inner');
    const $path = document.querySelector('.intro-bg-svg-path');
    const player = document.querySelector('.spreaker-player');
    const urls = document.querySelectorAll('a');
    const links = document.querySelectorAll('a[href="#"]');
    const btns = document.querySelectorAll('input[type="submit"]');
    const projects = document.querySelector('.shirt-btn');
    const subscribeBTN = document.querySelector('.subscribe-btn');
    const closeBTN = document.querySelector('.close-modal-btn');
    const $base = 16;
    const start = 'M 10,0 L 10,10 C 10,10 10,5 5,5 C 0,5 0,10 0,10 L 0,0 Z';
    const end = 'M 10,0 L 10,0 C 10,0 10,0 5,0 C 0,0 0,0 0,0 L 0,0 Z';
    let playingWelcome = false;
    let isMobile = false;
    let isInside = false;
    let newBodyClasses = '';
    let mouseX = 0;
    let mouseY = 0;
    let posX = 0;
    let posY = 0;
    let $ts = 0.5;
    let $dly = -0.25;
    console.log('Setting up variables and constants');

    // Utility Functions
    function setVH() {
        const $vh = window.innerHeight;
        html.style.setProperty('--vh', `${$vh}px`);
    }

    // Initialize Site Function (moved within DOMContentLoaded)
    async function initializeSite() {
        if (firstLoad) {
            console.log('First load detected. Initiating welcome animation...');
            await initWelcomeAnimation();  // Wait for welcome animation
            firstLoad = false;
        }
        initBarba();  // Initialize Barba after the welcome flow completes
    }

    // Initialization Functions
    function initSiteOnce() {
        if (!body.classList.contains('site-initialized')) {
            console.log('initSiteOnce: Initializing site...');
            initSite();  // Call actual initialization
            body.classList.add('site-initialized');  // Add flag
            console.log('initSiteOnce: Site has been initialized, class added.');
        } else {
            console.log('initSiteOnce: Site already initialized.');
        }
    }

    // Function to play the welcome animation
    function initWelcomeAnimation() {
        return new Promise(async (resolve) => {
            let hasWelcomePlayed = sessionStorage.getItem('welcomePlayed');
            console.log('Session storage welcomePlayed:', hasWelcomePlayed);

            if (!hasWelcomePlayed || hasWelcomePlayed === 'false') {
                console.log('No welcomePlayed flag found. Playing welcome animation...');
                await playWelcomeAnimation();  // Wait for the animation to complete
                sessionStorage.setItem('welcomePlayed', 'true'); // Set the flag
            } else {
                console.log('Welcome animation already played.');
                initSiteOnce();  // Run initSiteOnce if the animation is skipped
            }

            resolve();  // Resolve the promise after animation or initSiteOnce
        });
    }

    // Function to play Welcome Animation
    function playWelcomeAnimation() {
        return new Promise((resolve) => {
            const logo = document.querySelector('.intro-logo');
            const words = document.querySelectorAll('.intro-word');

            console.log('Logo element:', logo);
            console.log('Word elements:', words);
            
            if (!logo || words.length === 0) {
                console.error('Animation elements missing: ', {
                    logoExists: !!logo,
                    wordsCount: words.length
                });
                resolve(); // Resolve immediately if elements are missing
                return;
            }

            if (playingWelcome) {
                resolve();
                return;
            }
            playingWelcome = true;

            console.log('Starting welcome animation...');  // Debugging line to ensure animation starts

            const tl = gsap.timeline();
            tl.to('body', { overflow: 'hidden' })
            .to(logo, {
                opacity: 1,
                duration: 0.5,
                onStart: () => console.log('Fading in logo'),
                onComplete: () => {
                    console.log('Logo fully visible');
                    gsap.set(logo, { clearProps: "opacity" });
                }
            })
            .from(words, {
                duration: 0.3,
                stagger: 0.3,
                delay: 0.2,
                opacity: 0,
                y: 50,
                ease: 'Power3.easeOut',
                onStart: () => console.log('Starting word fade in'),
                onComplete: () => console.log('Word fade in complete')
            })
            .to(logo, {
                duration: 0.3,
                delay: 2.5,
                opacity: 0,
                y: -50,
                ease: 'Power3.easeOut'
            })
            .to($path, $ts, {
                attr: { d: 'M 10,0 L 10,10 C 10,10 10,5 5,5 C 0,5 0,10 0,10 L 0,0 Z' },
                ease: Power2.easeIn,
                delay: $dly
            })
            .to($path, $ts, {
                attr: { d: 'M 10,0 L 10,0 C 10,0 10,0 5,0 C 0,0 0,0 0,0 L 0,0 Z' },
                ease: Power2.easeOut
            })
            .to('body', { overflow: 'auto' }, '-=2')
            .to('.intro', {
                display: 'none',
                onComplete: () => {
                    console.log('Welcome animation completed.');
                    setIntro();
                    setTimeout(initSiteOnce, 5000);
                    playingWelcome = false;  // Reset the flag
                    resolve();
                }
            });
        });
    }

    // Barba JS Setup
    function initBarba() {
        try {
            barba.init({
                prevent: ({ current, next }) => {
                    console.log('Barba.js prevent: Allowing transition.');
                    return false;  // Always allow transitions
                },
                transitions: [
                    {
                        name: 'opacity-transition',
                        leave(data) {
                            const done = this.async(); // Signal async transition start
                            loadingElement.classList.add('is-visible');
                            return gsap.to(data.current.container, 0.3, {
                                opacity: 0,
                                onComplete: done  // Call done when animation completes
                            });
                        },
                        enter(data) {
                            const done = this.async(); // Signal async transition start
                            body.classList.remove('site-ready');
                            return gsap.from(data.next.container, {
                                opacity: 0,
                                onComplete: done  // Call done when animation completes
                            });
                        },
                        afterEnter() {
                            window.scrollTo(0, 0);
                            setTimeout(() => {
                                initPageReady();
                                initSiteOnce();
                                loadingElement.classList.remove('is-visible');
                                body.classList.add('site-ready');
                            }, 500);
                        }
                    }
                ]
            });

            barba.hooks.beforeEnter((data) => {
                console.log('Barba.js hook: beforeEnter');
                const response = data.next.html.replace(/(<\/?)body( .+?)?>/gi, '$1notBody$2>');
                const tempDiv = document.createElement('div');
                tempDiv.innerHTML = response;
                newBodyClasses = tempDiv.querySelector('notBody').getAttribute('class');
            });

            barba.hooks.afterEnter(() => {
                console.log('Barba.js hook: afterEnter - Initializing site once.');
                initSiteOnce();  // Ensure initSite runs only once after transition
            });

            console.log('Barba.js initialized successfully');

        } catch (error) {
            console.error('Error initializing Barba.js:', error);
        }
    }

    // Async function to control the flow
    (async function () {
        if (firstLoad) {
            console.log('First load detected. Initiating welcome animation...');
            await initWelcomeAnimation();  // Wait for the welcome setup to complete
            firstLoad = false;
        }
        initBarba();  // Initialize Barba after the welcome flow completes
    })();

    // Function to initialize the site
    function initSite() {
        console.log('Initializing site...');
        window.scrollTo(0, 0);
        body.classList.add('site-ready');

        const intro = document.querySelector('.intro');
        if (intro) {
            intro.style.display = 'none';
        }

        handleScroll();  // Call handleScroll to initialize any scroll-based behavior
    }

    // Function to set the page scroll top
    function initPageReady() {
        console.log('initPageReady: Initializing page.');
        handleScroll();
        body.classList.add('is-loaded');
    }

    // Animation and Intro Functions
    function setIntro() {
        if (!$inner) {
            console.error('Element - .intro-inner not found.');
            return;
        }

        const elements = [
            { className: 'intro-who--symbol', delay: 800 },
            { className: 'intro-is-symbol', delay: 1100 },
            { className: 'intro-underline--symbol', delay: 1650 },
            { className: 'intro-waves--symbol', delay: 1800 },
            { className: 'intro-girl--symbol', delay: 1900 },
            { className: 'intro-arrows--symbol', delay: 1000 },
            { className: 'intro-marks--symbol', delay: 1100 },
            { className: 'intro-spiral--symbol', delay: 1200 },
            { className: 'intro-crosses--symbol', delay: 1300 },
            { className: 'intro-cricket--symbol', delay: 1400 }
        ];

        elements.forEach(({ className, delay }) => {
            setTimeout(() => addMask('div', className), delay);
            console.log('mask called for');
        });
    }

    function addMask(element, className) {
        console.log('mask added');
        const mask = document.createElement(element);
        mask.classList.add(className);
        $inner.appendChild(mask);
    }

    function handleMarqueeAnimations() {
        marquee.forEach(function (e) {
            // Clone the marquee item and set clip path style
            const letter = e.querySelector('.marquee__item');
            const clone = letter.cloneNode(true);
            letter.after(clone);
            e.style.clipPath = "inset(0%)";

            // Create individual timeline for each marquee item
            const $mar = e.querySelectorAll('.marquee__inner');
            const tl = gsap.to($mar, { duration: 10, xPercent: -100, ease: "none", repeat: -1 }).timeScale(1);

            // Flag to prevent rapid toggling
            let isHovered = false;

            // Event listeners
            e.addEventListener("mouseenter", () => {
                if (!isHovered) {
                    gsap.to(tl, { timeScale: 0, duration: 1.5, ease: "sine" });
                    e.classList.add('is-hovered');
                    isHovered = true; // Mark as hovered
                }
            });

            e.addEventListener("mouseleave", () => {
                if (isHovered) {
                    gsap.to(tl, { timeScale: 1, duration: 1.5, ease: "sine" });
                    e.classList.remove('is-hovered');
                    isHovered = false; // Reset hover state
                }
            });
        });
    }

    // Function for scroll behavior
    function handleScroll() {
        // scrollTXT functionality
        const scrolled = document.scrollingElement.scrollTop;
        const rotationValue = (scrolled / 10) % Math.PI;
        scrollTxt.style.transform = `rotate(${rotationValue}rad)`;

        // scrollHold functionality
        const elements = document.querySelectorAll('.scroll-hold, .cky-revisit-bottom-left');
        const scrollPosition = window.scrollY + window.innerHeight;
        const documentHeight = html.scrollHeight;
        const viewportHeight = window.innerHeight;

        elements.forEach(element => {
            const elementHeight = element.offsetHeight;

            const isStuck = documentHeight - scrollPosition <= elementHeight;
            element.style.position = isStuck ? 'absolute' : 'fixed';
            element.style.top = isStuck 
                ? `${documentHeight - elementHeight - element.offsetHeight}px` 
                : `${viewportHeight - ($base + elementHeight)}px`;

            element.classList.toggle('stuck', isStuck);
        });
    }

    // Reveal function for elements in viewport
    function reveal() {
        const windowHeight = window.innerHeight;
        const revealPoint = 100;

        document.querySelectorAll('.viewport').forEach(element => {
            const revealTop = element.getBoundingClientRect().top;
            const inView = revealTop < windowHeight - revealPoint;
            element.classList.toggle('in-view', inView);
        });
    }

    // Event Listeners for Buttons and Links
    function setupInteractiveElements() {
        subscribeBTN.addEventListener("click", () => body.classList.add("modal-shown"));
        closeBTN.addEventListener("click", () => body.classList.remove("modal-shown"));
        
        urls.forEach(url => {
            url.addEventListener('mouseenter', () => cursor.classList.add('active'));
            url.addEventListener('mouseleave', () => cursor.classList.remove('active'));
        });

        links.forEach(link => {
            link.addEventListener('click', event => event.preventDefault());
        });
    }

    // Cursor Animation
    function animateCursor() {
        TweenMax.to({}, 0.001, {
            repeat: -1,
            onRepeat: function() {
                posX += (mouseX - posX) / 9;
                posY += (mouseY - posY) / 9;

                TweenMax.set(cursor, {
                    css: {
                        left: mouseX,
                        top: mouseY
                    }
                });
            }
        });
    }

    // Shirt Hover
    function showElement() {
        const button = document.querySelector('.shirt-btn');
        const preview = document.querySelector('.preview');
    
        button.addEventListener('mouseenter', () => {
            gsap.to(preview, { opacity: 1, duration: 0.1 });
        });
    
        button.addEventListener('mouseleave', () => {
            gsap.to(preview, { opacity: 0, duration: 0.1 });
        });
    
        button.addEventListener('mousemove', (event) => {
            const rect = preview.getBoundingClientRect();
            const offsetX = event.pageX - rect.width / 2;
            const offsetY = event.pageY - (rect.height / 2 + rect.height * 0.2);
            gsap.to(preview, {
                left: offsetX,
                top: offsetY,
                duration: 0.8,
                ease: "power4.out"
            });
        });
    }

    // Initialize the site after DOM is fully loaded
    setVH();
    initializeSite(); // Start the initial site logic
    handleMarqueeAnimations();
    setupInteractiveElements();
    animateCursor();
    showElement();

    // Attach Event Listeners
    window.addEventListener('scroll', reveal);
    window.addEventListener('scroll', handleScroll);
    window.addEventListener('resize', handleScroll);
    window.addEventListener('resize', setVH);
    window.addEventListener('mousemove', (event) => {
        mouseX = event.clientX;
        mouseY = event.clientY;
    });
});

I have input a lot of console logs for tracking purposes as well as text notes detailing what certain functions or calls do. Any and all help appreciated.

Hi @tibrent, I haven’t used barba.js myself but from a look at your code, isn’t the prevent() method where you’d have to check for firstLoad? I.e.:

barba.init({
  prevent () {
    return firstLoad
  },
  // ...
})

And then initialize it before the welcome animation check:

async function initializeSite () {
  initBarba()
  
  if (firstLoad) {
    await initWelcomeAnimation()
    firstLoad = false
  }
}

BTW that initialization appears twice in your code, once as a named function and then as an anonymous IIFE… this will probably break your initialization altogether.

Edit: Fixed prevent condition.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.