154 lines
4.0 KiB
JavaScript
154 lines
4.0 KiB
JavaScript
/**
|
|
* Home hero carousel — autoplay + dot navigation + mobile swipe
|
|
*/
|
|
(function () {
|
|
var AUTOPLAY_MS = 5000;
|
|
var SWIPE_THRESHOLD = 48;
|
|
var MOBILE_MQ = '(max-width: 991.98px)';
|
|
|
|
function initHomeHeroCarousel(root) {
|
|
if (root.getAttribute('data-carousel-ready') === 'true') return;
|
|
root.setAttribute('data-carousel-ready', 'true');
|
|
|
|
var slides = root.querySelectorAll('[data-home-hero-slide]');
|
|
var dots = root.querySelectorAll('[data-home-hero-dot]');
|
|
var viewport = root.querySelector('.home-hero-carousel__viewport');
|
|
if (!slides.length) return;
|
|
|
|
var index = 0;
|
|
var timer = null;
|
|
var touchStartX = 0;
|
|
var touchStartY = 0;
|
|
var touchTracking = false;
|
|
|
|
function setSlide(nextIndex) {
|
|
index = (nextIndex + slides.length) % slides.length;
|
|
|
|
slides.forEach(function (slide, i) {
|
|
var active = i === index;
|
|
slide.classList.toggle('is-active', active);
|
|
slide.setAttribute('aria-hidden', active ? 'false' : 'true');
|
|
});
|
|
|
|
dots.forEach(function (dot, i) {
|
|
var active = i === index;
|
|
dot.classList.toggle('is-active', active);
|
|
dot.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
});
|
|
}
|
|
|
|
function startAutoplay() {
|
|
stopAutoplay();
|
|
timer = window.setInterval(function () {
|
|
setSlide(index + 1);
|
|
}, AUTOPLAY_MS);
|
|
}
|
|
|
|
function stopAutoplay() {
|
|
if (timer) {
|
|
window.clearInterval(timer);
|
|
timer = null;
|
|
}
|
|
}
|
|
|
|
function isMobileSwipeEnabled() {
|
|
return window.matchMedia(MOBILE_MQ).matches;
|
|
}
|
|
|
|
function bindTouchSwipe() {
|
|
if (!viewport || viewport.getAttribute('data-swipe-ready') === 'true') return;
|
|
viewport.setAttribute('data-swipe-ready', 'true');
|
|
|
|
viewport.addEventListener(
|
|
'touchstart',
|
|
function (e) {
|
|
if (!isMobileSwipeEnabled() || !e.touches.length) return;
|
|
touchStartX = e.touches[0].clientX;
|
|
touchStartY = e.touches[0].clientY;
|
|
touchTracking = true;
|
|
stopAutoplay();
|
|
},
|
|
{ passive: true }
|
|
);
|
|
|
|
viewport.addEventListener(
|
|
'touchend',
|
|
function (e) {
|
|
if (!touchTracking) return;
|
|
touchTracking = false;
|
|
|
|
if (!isMobileSwipeEnabled()) {
|
|
startAutoplay();
|
|
return;
|
|
}
|
|
|
|
var touch = e.changedTouches[0];
|
|
if (!touch) {
|
|
startAutoplay();
|
|
return;
|
|
}
|
|
|
|
var dx = touch.clientX - touchStartX;
|
|
var dy = touch.clientY - touchStartY;
|
|
|
|
if (Math.abs(dx) < SWIPE_THRESHOLD || Math.abs(dx) <= Math.abs(dy)) {
|
|
startAutoplay();
|
|
return;
|
|
}
|
|
|
|
if (dx < 0) {
|
|
setSlide(index + 1);
|
|
} else {
|
|
setSlide(index - 1);
|
|
}
|
|
startAutoplay();
|
|
},
|
|
{ passive: true }
|
|
);
|
|
|
|
viewport.addEventListener(
|
|
'touchcancel',
|
|
function () {
|
|
touchTracking = false;
|
|
startAutoplay();
|
|
},
|
|
{ passive: true }
|
|
);
|
|
}
|
|
|
|
dots.forEach(function (dot) {
|
|
dot.addEventListener('click', function () {
|
|
var target = Number(dot.getAttribute('data-home-hero-dot'));
|
|
if (Number.isNaN(target)) return;
|
|
setSlide(target);
|
|
startAutoplay();
|
|
});
|
|
});
|
|
|
|
root.addEventListener('mouseenter', stopAutoplay);
|
|
root.addEventListener('mouseleave', startAutoplay);
|
|
root.addEventListener('focusin', stopAutoplay);
|
|
root.addEventListener('focusout', function (e) {
|
|
if (!root.contains(e.relatedTarget)) startAutoplay();
|
|
});
|
|
|
|
bindTouchSwipe();
|
|
setSlide(0);
|
|
startAutoplay();
|
|
}
|
|
|
|
function boot() {
|
|
document.querySelectorAll('[data-home-hero-carousel]').forEach(initHomeHeroCarousel);
|
|
}
|
|
|
|
window.AifoHomeHeroCarouselInit = initHomeHeroCarousel;
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', boot);
|
|
} else {
|
|
boot();
|
|
}
|
|
|
|
document.addEventListener('aifo:home-hero-carousel-reload', boot);
|
|
})();
|