Initial commit
This commit is contained in:
125
script.js
Normal file
125
script.js
Normal file
@@ -0,0 +1,125 @@
|
||||
const body = document.body;
|
||||
const menuToggle = document.querySelector('.menu-toggle');
|
||||
const navLinks = document.querySelectorAll('.site-nav a');
|
||||
const progress = document.querySelector('.scroll-progress span');
|
||||
|
||||
if (menuToggle) {
|
||||
menuToggle.addEventListener('click', () => {
|
||||
const isOpen = body.classList.toggle('menu-open');
|
||||
menuToggle.setAttribute('aria-expanded', String(isOpen));
|
||||
});
|
||||
}
|
||||
|
||||
navLinks.forEach((link) => {
|
||||
link.addEventListener('click', () => {
|
||||
body.classList.remove('menu-open');
|
||||
menuToggle?.setAttribute('aria-expanded', 'false');
|
||||
});
|
||||
});
|
||||
|
||||
const updateProgress = () => {
|
||||
if (!progress) return;
|
||||
const max = document.documentElement.scrollHeight - window.innerHeight;
|
||||
const value = max > 0 ? window.scrollY / max : 0;
|
||||
progress.style.transform = `scaleX(${value})`;
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', updateProgress, { passive: true });
|
||||
updateProgress();
|
||||
|
||||
const revealObserver = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('is-visible');
|
||||
revealObserver.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.12, rootMargin: '0px 0px -5% 0px' });
|
||||
|
||||
document.querySelectorAll('.reveal').forEach((element) => revealObserver.observe(element));
|
||||
|
||||
const currentPage = body.dataset.page;
|
||||
document.querySelectorAll(`[data-nav="${currentPage}"]`).forEach((link) => {
|
||||
link.setAttribute('aria-current', 'page');
|
||||
});
|
||||
|
||||
const dot = document.querySelector('.cursor-dot');
|
||||
const ring = document.querySelector('.cursor-ring');
|
||||
|
||||
if (dot && ring && window.matchMedia('(pointer: fine)').matches) {
|
||||
let mouseX = 0;
|
||||
let mouseY = 0;
|
||||
let ringX = 0;
|
||||
let ringY = 0;
|
||||
|
||||
window.addEventListener('mousemove', (event) => {
|
||||
mouseX = event.clientX;
|
||||
mouseY = event.clientY;
|
||||
dot.classList.add('is-active');
|
||||
ring.classList.add('is-active');
|
||||
dot.style.left = `${mouseX}px`;
|
||||
dot.style.top = `${mouseY}px`;
|
||||
});
|
||||
|
||||
const animateRing = () => {
|
||||
ringX += (mouseX - ringX) * 0.16;
|
||||
ringY += (mouseY - ringY) * 0.16;
|
||||
ring.style.left = `${ringX}px`;
|
||||
ring.style.top = `${ringY}px`;
|
||||
requestAnimationFrame(animateRing);
|
||||
};
|
||||
|
||||
animateRing();
|
||||
|
||||
document.querySelectorAll('a, button, input, textarea, select').forEach((target) => {
|
||||
target.addEventListener('mouseenter', () => ring.classList.add('is-hovering'));
|
||||
target.addEventListener('mouseleave', () => ring.classList.remove('is-hovering'));
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-count]').forEach((element) => {
|
||||
const target = Number(element.dataset.count);
|
||||
const suffix = element.dataset.suffix || '';
|
||||
const counterObserver = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (!entry.isIntersecting) return;
|
||||
const start = performance.now();
|
||||
const duration = 1300;
|
||||
const tick = (time) => {
|
||||
const progressValue = Math.min((time - start) / duration, 1);
|
||||
const eased = 1 - Math.pow(1 - progressValue, 4);
|
||||
element.textContent = `${Math.round(target * eased)}${suffix}`;
|
||||
if (progressValue < 1) requestAnimationFrame(tick);
|
||||
};
|
||||
requestAnimationFrame(tick);
|
||||
counterObserver.disconnect();
|
||||
});
|
||||
}, { threshold: 0.5 });
|
||||
counterObserver.observe(element);
|
||||
});
|
||||
|
||||
const contactForm = document.querySelector('[data-contact-form]');
|
||||
if (contactForm) {
|
||||
contactForm.addEventListener('submit', (event) => {
|
||||
event.preventDefault();
|
||||
const status = contactForm.querySelector('.form-status');
|
||||
const name = new FormData(contactForm).get('name') || 'there';
|
||||
status.textContent = `Thank you, ${name}. Your collaboration brief is ready for review.`;
|
||||
contactForm.reset();
|
||||
});
|
||||
}
|
||||
|
||||
const applicationForm = document.querySelector('[data-application-form]');
|
||||
if (applicationForm) {
|
||||
applicationForm.addEventListener('submit', (event) => {
|
||||
event.preventDefault();
|
||||
const status = applicationForm.querySelector('.form-status');
|
||||
const name = new FormData(applicationForm).get('name') || 'builder';
|
||||
status.textContent = `Application received, ${name}. Connect this form to a submission service before launch.`;
|
||||
applicationForm.reset();
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-year]').forEach((node) => {
|
||||
node.textContent = new Date().getFullYear();
|
||||
});
|
||||
Reference in New Issue
Block a user