PageSourceSearch

https://tecidoskite.com.br/wp-content/themes/tecidoskite/assets/js/hero.js?ver=7.1

js tecidoskite.com.br collected 2026-10-05 03:41:34 UTC 6,024 bytes, 153 lines download raw bytes

1/**
2 * hero.js — Carrossel Hero
3 * Funcionalidades:
4 *  - Auto-play 6s
5 *  - Crossfade via classes .is-active / .is-prev
6 *  - Navegação por setas (prev/next)
7 *  - Dots sincronizados
8 *  - Swipe mobile (touchstart/touchend)
9 *  - Teclado (ArrowLeft / ArrowRight)
10 *  - Pause em hover e focus
11 *  - prefers-reduced-motion: desabilita auto-play
12 */
13
14document.addEventListener('DOMContentLoaded', () => {
15
16  const hero = document.querySelector('.hero');
17  if (!hero) return;
18
19  // ─── Elementos ────────────────────────────────────────────────
20  const slides = [...hero.querySelectorAll('.hero__slide')];
21  const dots = [...hero.querySelectorAll('.hero__dot')];
22  const btnPrev = hero.querySelector('.hero__btn--prev');
23  const btnNext = hero.querySelector('.hero__btn--next');
24
25  if (!slides.length) return;
26
27  // ─── Estado ───────────────────────────────────────────────────
28  const AUTOPLAY_DELAY = 6000;
29  const prefersReducedMotion = window.matchMedia(
30    '(prefers-reduced-motion: reduce)'
31  ).matches;
32
33  let currentIndex = 0;
34  let autoPlayTimer = null;
35  let isAnimating = false;
36  const ANIM_DURATION = 800; // ms — deve ser igual à transition do CSS
37
38  // ─── Ir para slide ────────────────────────────────────────────
39  function goToSlide(index) {
40    if (isAnimating || index === currentIndex) return;
41
42    isAnimating = true;
43
44    // Remove classes do slide atual
45    slides[currentIndex].classList.remove('is-active');
46    slides[currentIndex].classList.add('is-prev');
47    slides[currentIndex].setAttribute('aria-hidden', 'true');
48
49    // Atualiza dots
50    dots[currentIndex]?.classList.remove('is-active');
51    dots[currentIndex]?.setAttribute('aria-selected', 'false');
52
53    currentIndex = (index + slides.length) % slides.length;
54
55    // Ativa novo slide
56    slides[currentIndex].classList.add('is-active');
57    slides[currentIndex].classList.remove('is-prev');
58    slides[currentIndex].setAttribute('aria-hidden', 'false');
59
60    dots[currentIndex]?.classList.add('is-active');
61    dots[currentIndex]?.setAttribute('aria-selected', 'true');
62
63    // Remove .is-prev após a transição completar
64    setTimeout(() => {
65      slides.forEach((slide) => slide.classList.remove('is-prev'));
66      isAnimating = false;
67    }, ANIM_DURATION);
68  }
69
70  // ─── Navegação ────────────────────────────────────────────────
71  function next() { goToSlide(currentIndex + 1); }
72  function prev() { goToSlide(currentIndex - 1); }
73
74  // ─── Auto-play ────────────────────────────────────────────────
75  function startAutoPlay() {
76    if (prefersReducedMotion || autoPlayTimer !== null) return;
77    autoPlayTimer = setInterval(next, AUTOPLAY_DELAY);
78  }
79
80  function stopAutoPlay() {
81    clearInterval(autoPlayTimer);
82    autoPlayTimer = null;
83  }
84
85  function resetAutoPlay() {
86    stopAutoPlay();
87    startAutoPlay();
88  }
89
90  // ─── Setas ────────────────────────────────────────────────────
91  btnPrev?.addEventListener('click', () => { prev(); resetAutoPlay(); });
92  btnNext?.addEventListener('click', () => { next(); resetAutoPlay(); });
93
94  // ─── Dots ─────────────────────────────────────────────────────
95  dots.forEach((dot, i) => {
96    dot.addEventListener('click', () => { goToSlide(i); resetAutoPlay(); });
97  });
98
99  // ─── Pause em hover / focus ────────────────────────────────────
100  hero.addEventListener('mouseenter', stopAutoPlay);
101  hero.addEventListener('mouseleave', startAutoPlay);
102  hero.addEventListener('focusin', stopAutoPlay);
103  hero.addEventListener('focusout', (e) => {
104    if (!hero.contains(e.relatedTarget)) startAutoPlay();
105  });
106
107  // ─── Teclado (ArrowLeft / ArrowRight) ─────────────────────────
108  hero.addEventListener('keydown', (e) => {
109    if (e.key === 'ArrowRight') { next(); resetAutoPlay(); }
110    if (e.key === 'ArrowLeft') { prev(); resetAutoPlay(); }
111  });
112
113  // Tornar a seção focável para capturar eventos de teclado
114  if (!hero.hasAttribute('tabindex')) {
115    hero.setAttribute('tabindex', '0');
116  }
117
118  // ─── Swipe mobile ─────────────────────────────────────────────
119  let touchStartX = 0;
120  const SWIPE_THRESHOLD = 50;
121
122  hero.addEventListener('touchstart', (e) => {
123    touchStartX = e.touches[0].clientX;
124  }, { passive: true });
125
126  hero.addEventListener('touchend', (e) => {
127    const diff = touchStartX - e.changedTouches[0].clientX;
128    if (Math.abs(diff) > SWIPE_THRESHOLD) {
129      diff > 0 ? next() : prev();
130      resetAutoPlay();
131    }
132  }, { passive: true });
133
134  // ─── Inicialização ────────────────────────────────────────────
135  // Garantir estado correto no slide 0
136  slides.forEach((slide, i) => {
137    if (i === 0) {
138      slide.classList.add('is-active');
139      slide.setAttribute('aria-hidden', 'false');
140    } else {
141      slide.classList.remove('is-active');
142      slide.setAttribute('aria-hidden', 'true');
143    }
144  });
145
146  dots.forEach((dot, i) => {
147    dot.classList.toggle('is-active', i === 0);
148    dot.setAttribute('aria-selected', i === 0 ? 'true' : 'false');
149  });
150
151  startAutoPlay();
152
153});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.