Files
alwisp/www/js/main.js

115 lines
3.9 KiB
JavaScript
Raw Normal View History

/* ============================================================
ALWISP main.js
============================================================ */
'use strict';
// ── NAV: scroll shadow + mobile toggle ──────────────────────
(function () {
const header = document.getElementById('site-header');
const toggle = document.getElementById('nav-toggle');
const menu = document.getElementById('nav-menu');
const navLinks = menu ? menu.querySelectorAll('.nav__link') : [];
// Scroll shadow
if (header) {
const onScroll = () => header.classList.toggle('scrolled', window.scrollY > 10);
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
}
// Mobile toggle
if (toggle && menu) {
toggle.addEventListener('click', () => {
const isOpen = menu.classList.toggle('is-open');
toggle.setAttribute('aria-expanded', isOpen);
});
// Close on nav link click
navLinks.forEach(link => {
link.addEventListener('click', () => {
menu.classList.remove('is-open');
toggle.setAttribute('aria-expanded', 'false');
});
});
// Close on outside click
document.addEventListener('click', e => {
if (!header.contains(e.target)) {
menu.classList.remove('is-open');
toggle.setAttribute('aria-expanded', 'false');
}
});
}
// Active nav link
const currentPath = window.location.pathname.replace(/\/$/, '') || '/';
navLinks.forEach(link => {
const href = link.getAttribute('href').replace(/\/$/, '') || '/';
if (href === currentPath) link.classList.add('nav__link--active');
});
}());
// ── COUNTER ANIMATION (stats bar) ───────────────────────────
(function () {
const counters = document.querySelectorAll('.stat__value[data-count]');
if (!counters.length) return;
const easeOut = t => 1 - Math.pow(1 - t, 3);
function animateCounter(el) {
const target = parseInt(el.dataset.count, 10);
const duration = 1400;
const start = performance.now();
function step(now) {
const elapsed = now - start;
const progress = Math.min(elapsed / duration, 1);
el.textContent = Math.floor(easeOut(progress) * target);
if (progress < 1) requestAnimationFrame(step);
else el.textContent = target;
}
requestAnimationFrame(step);
}
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animateCounter(entry.target);
observer.unobserve(entry.target);
}
});
}, { threshold: 0.4 });
counters.forEach(c => observer.observe(c));
}());
// ── SCROLL REVEAL (cards, sections) ─────────────────────────
(function () {
const revealEls = document.querySelectorAll(
'.card, .stat, .why__text, .section__header, .cta-band__content'
);
if (!revealEls.length || !('IntersectionObserver' in window)) return;
revealEls.forEach((el, i) => {
el.style.opacity = '0';
el.style.transform = 'translateY(22px)';
el.style.transition = `opacity 0.55s ease ${i * 0.07}s, transform 0.55s ease ${i * 0.07}s`;
});
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
observer.unobserve(entry.target);
}
});
}, { threshold: 0.12 });
revealEls.forEach(el => observer.observe(el));
}());