Current directory: /home/klas4s23/domains/585455.klas4s23.mid-ica.nl/public_html/Gastenboek/uploads
// --- TEXT LOADER ANIMATION ---
const loader = document.getElementById('loader');
const loaderName = document.querySelector('.loader-name');
const loaderTitle = document.querySelector('.loader-title');
const percentage = document.querySelector('.loader-percentage');
// Config
const text = "COLIN POORT";
const targetMap = {
0: 'C', // C -> C
5: 'W', // Space -> W
6: 'P' // P -> P
};
const collapseIndices = [1, 2, 3, 4, 7, 8, 9, 10];
// Generate DOM Structure
loaderName.innerHTML = '';
const wrappers = [];
const tracks = [];
text.split('').forEach((char, index) => {
const wrapper = document.createElement('span');
wrapper.className = 'char-wrapper';
Object.assign(wrapper.style, {
display: 'inline-block',
overflow: 'hidden',
verticalAlign: 'bottom',
position: 'relative',
lineHeight: '1.0',
height: '1em',
width: 'auto'
});
if (char === ' ') {
wrapper.style.minWidth = '0.3em';
}
const track = document.createElement('div');
track.className = 'char-track';
Object.assign(track.style, {
display: 'flex',
flexDirection: 'column',
lineHeight: '1em'
});
// 1. Top
const topSpan = document.createElement('span');
topSpan.textContent = char;
topSpan.style.display = 'block';
topSpan.style.height = '1em';
if (char === ' ') topSpan.innerHTML = ' ';
// 2. Bottom
const bottomSpan = document.createElement('span');
bottomSpan.style.display = 'block';
bottomSpan.style.height = '1em';
const targetChar = targetMap[index];
if (targetChar) {
bottomSpan.textContent = targetChar;
} else {
bottomSpan.innerHTML = ' ';
}
track.appendChild(topSpan);
track.appendChild(bottomSpan);
wrapper.appendChild(track);
loaderName.appendChild(wrapper);
wrappers.push(wrapper);
tracks.push(track);
});
// LOADER SEQUENCE
const tl = gsap.timeline();
// Check if session exists
const sessionActive = sessionStorage.getItem('sessionActive');
if (!sessionActive) {
sessionStorage.setItem('sessionActive', 'true');
// --- FULL ANIMATION ---
tl.set(tracks, { yPercent: 50 });
// 0. Counter
let progress = { value: 0 };
tl.to(progress, {
value: 100,
duration: 1.5,
ease: "power2.inOut",
onUpdate: () => {
percentage.textContent = Math.round(progress.value) + '%';
}
});
// 1. Reveal (Up)
tl.to(tracks, {
yPercent: 0,
duration: 1.0,
stagger: 0.05,
ease: "power4.out",
}, "-=1.3");
tl.to(loaderTitle, {
y: 0,
duration: 1.0,
ease: "power4.out"
}, "-=1.0");
// 2. Scroll (To Target)
tl.to(tracks, {
yPercent: -50,
duration: 0.8,
stagger: 0.02,
ease: "power3.inOut"
});
tl.to(loaderTitle, { y: -100, opacity: 0, duration: 0.8, ease: "power2.in" }, "<");
tl.to(percentage, { opacity: 0, duration: 0.5 }, "<");
} else {
// --- SKIP TO COLLAPSE ---
// FAST FORWARD STATE
gsap.set(tracks, { yPercent: -50 });
gsap.set(loaderTitle, { y: -100, opacity: 0 });
gsap.set(percentage, { opacity: 0 });
}
// 3. Collapse (Wait 0.2s)
const collapsingWrappers = collapseIndices.map(i => wrappers[i]);
tl.to(collapsingWrappers, {
width: 0, minWidth: 0, margin: 0, padding: 0,
duration: 0.8, ease: "power4.inOut"
}, ">+0.1");
// 4. Curtain Reveal (Slide Up) & Hero Text Reveal
const heroText = document.querySelector('.hero-text');
// Initial state for hero text
gsap.set(heroText, { opacity: 0, scale: 0.95, y: 30 });
tl.to(loader, {
yPercent: -100, // Curtain lift effect
duration: 1.2,
ease: "power4.inOut",
delay: 0.1,
onStart: () => {
// Animate hero text as curtain lifts
gsap.to(heroText, {
opacity: 1,
scale: 1,
y: 0,
duration: 1.5,
ease: "power3.out",
delay: 0.2
});
},
onComplete: () => {
loader.classList.add('hidden');
}
});
// HERO SCROLL WORD
const wordTrack = document.querySelector('.word-track');
const pageWrapper = document.querySelector('.page-wrapper');
const speedFactor = 0.5;
let maxTranslateY = 0;
function updateLayout() {
if (wordTrack && pageWrapper) {
const wordHeight = wordTrack.firstElementChild.offsetHeight;
const wordCount = wordTrack.children.length;
maxTranslateY = (wordCount - 1) * wordHeight;
const scrollNeeded = maxTranslateY / speedFactor;
pageWrapper.style.marginTop = `calc(100vh + ${scrollNeeded}px)`;
}
}
updateLayout();
window.addEventListener('load', updateLayout);
window.addEventListener('resize', updateLayout);
window.addEventListener('scroll', () => {
const scrollY = window.scrollY;
let currentTranslate = scrollY * speedFactor;
if (currentTranslate > maxTranslateY) currentTranslate = maxTranslateY;
wordTrack.style.transform = `translateY(-${currentTranslate}px)`;
// Scroll Reveal Logic
const reveals = document.querySelectorAll('.reveal-on-scroll');
const windowHeight = window.innerHeight;
reveals.forEach(el => {
const elementTop = el.getBoundingClientRect().top;
if (elementTop < windowHeight - 100) {
el.classList.add('active');
}
});
});
// --- CONTACT SECTION ANIMATION ---
gsap.registerPlugin(ScrollTrigger);
// Adjust .contact-section default CSS to be ready for animation
gsap.set(".contact-section", {
width: "100%",
height: "100vh",
margin: "0",
marginBottom: "0",
padding: "0",
maxWidth: "none", // Ensure no max-width constraints
borderRadius: "0px",
borderWidth: "0px" // Start with 0 to avoid glitched double borders at edges
});
gsap.to(".contact-section", {
width: "calc(100% - 100px)", // More dramatic shrink
height: "calc(100vh - 100px)", // More dramatic shrink
margin: "50px 50px 0 50px", // Balances the shrink
borderRadius: "50px",
borderWidth: "1px",
ease: "power2.out",
scrollTrigger: {
trigger: ".contact-section",
start: "top 80%", // Start shrinking when top of section is near bottom of viewport
end: "center 50%", // End shrinking when center of section is in middle of viewport
scrub: 1,
pin: false, // ALLOW SCROLLING (Don't lock)
onUpdate: (self) => {
// Hack to ensure border is visible only when shrinking starts
if (self.progress > 0.01) {
gsap.set(".contact-section", { borderWidth: "1px" });
} else {
gsap.set(".contact-section", { borderWidth: "0px" });
}
}
}
});
// --- CONTACT AMBIENT GLOW ---
const movingGlow = document.querySelector('.moving-glow');
// Create a random movement loop
function moveGlow() {
// Randomize X and Y independently for organic feel
gsap.to(movingGlow, {
x: "random(-200, 200)", // Move comfortably within the section
y: "random(-100, 100)",
duration: "random(3, 6)", // Variable speed
ease: "sine.inOut",
onComplete: moveGlow
});
// Separate scale animation for 'breathing' effect
gsap.to(movingGlow, {
scale: "random(0.8, 1.2)",
duration: "random(2, 4)",
ease: "sine.inOut",
overwrite: "auto" // Allow this to run concurrently with position
});
}
// Start the loop
moveGlow();
// --- HAMBURGER MENU LOGIC ---
const hamburger = document.querySelector('.hamburger-menu');
const mobileNavOverlay = document.querySelector('.mobile-nav-overlay');
const mobileLinks = document.querySelectorAll('.mobile-link');
if (hamburger && mobileNavOverlay) {
hamburger.addEventListener('click', () => {
hamburger.classList.toggle('active');
mobileNavOverlay.classList.toggle('active');
// Animate links if opening
if (mobileNavOverlay.classList.contains('active')) {
gsap.to(mobileLinks, {
opacity: 1,
y: 0,
duration: 0.5,
stagger: 0.1,
ease: "power2.out",
delay: 0.2
});
} else {
// Reset links for next time
gsap.to(mobileLinks, {
opacity: 0,
y: 20,
duration: 0.3
});
}
});
// Close menu when a link is clicked
mobileLinks.forEach(link => {
link.addEventListener('click', () => {
hamburger.classList.remove('active');
mobileNavOverlay.classList.remove('active');
gsap.to(mobileLinks, {
opacity: 0,
y: 20,
duration: 0.3
});
});
});
}