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