PageSourceSearch

https://kominka-neri.com/wp-content/themes/kominkaneri/js/top_slider.js?v=26061101

js kominka-neri.com collected 2026-10-06 15:17:13 UTC 5,117 bytes, 152 lines download raw bytes

1(function () {
2    const INTERVAL = 5000;   // 自動再生間隔(ms)
3    const FADE_DURATION = 1200; // フェード時間(ms)— CSSのtransitionと合わせる
4
5    const slider = document.getElementById('mainSlider');
6    const slides = slider.querySelectorAll('.slide');
7    const dotsContainer = document.getElementById('sliderDots');
8    const btnPrev = document.getElementById('btnPrev');
9    const btnNext = document.getElementById('btnNext');
10    const counterCurrent = document.getElementById('counterCurrent');
11    const counterTotal = document.getElementById('counterTotal');
12    const progressBar = document.getElementById('progressBar');
13
14    const total = slides.length;
15    let current = 0;
16    let timer = null;
17    let progressTimer = null;
18    let isTransitioning = false;
19
20    // ===== ブラー背景を各スライドに自動生成 =====
21    slides.forEach(function (slide) {
22        var img = slide.querySelector('img');
23        if (!img) return;
24        var bg = document.createElement('div');
25        bg.className = 'slide-bg';
26        function setBg() {
27            bg.style.backgroundImage = 'url(' + img.src + ')';
28        }
29        if (img.complete && img.naturalWidth > 0) {
30            setBg();
31        } else {
32            img.addEventListener('load', setBg);
33        }
34        slide.insertBefore(bg, slide.firstChild);
35    });
36
37    // ===== ドット生成 =====
38    counterTotal.textContent = String(total).padStart(2, '0');
39
40    slides.forEach(function (_, i) {
41        const dot = document.createElement('button');
42        dot.className = 'dot' + (i === 0 ? ' active' : '');
43        dot.setAttribute('role', 'tab');
44        dot.setAttribute('aria-label', 'スライド ' + (i + 1));
45        dot.setAttribute('aria-selected', i === 0 ? 'true' : 'false');
46        dot.addEventListener('click', function () {
47            if (i !== current) goTo(i);
48        });
49        dotsContainer.appendChild(dot);
50    });
51
52    const dots = dotsContainer.querySelectorAll('.dot');
53
54    // ===== スライド切替 =====
55    function goTo(index) {
56        if (isTransitioning) return;
57        isTransitioning = true;
58
59        slides[current].classList.remove('active');
60        dots[current].classList.remove('active');
61        dots[current].setAttribute('aria-selected', 'false');
62
63        current = (index + total) % total;
64
65        slides[current].classList.add('active');
66        dots[current].classList.add('active');
67        dots[current].setAttribute('aria-selected', 'true');
68        counterCurrent.textContent = String(current + 1).padStart(2, '0');
69
70        setTimeout(function () {
71            isTransitioning = false;
72        }, FADE_DURATION);
73    }
74
75    function next() { goTo(current + 1); }
76    function prev() { goTo(current - 1); }
77
78    // ===== プログレスバー =====
79    function startProgress() {
80        clearInterval(progressTimer);
81        progressBar.style.transition = 'none';
82        progressBar.style.width = '0%';
83
84        // 次フレームで transition を有効にしてアニメーション開始
85        requestAnimationFrame(function () {
86            requestAnimationFrame(function () {
87                progressBar.style.transition = 'width ' + INTERVAL + 'ms linear';
88                progressBar.style.width = '100%';
89            });
90        });
91    }
92
93    // ===== 自動再生 =====
94    function startAuto() {
95        stopAuto();
96        startProgress();
97        timer = setInterval(function () {
98            next();
99            startProgress();
100        }, INTERVAL);
101    }
102
103    function stopAuto() {
104        clearInterval(timer);
105        clearInterval(progressTimer);
106        progressBar.style.transition = 'none';
107        progressBar.style.width = '0%';
108    }
109
110    // ===== ボタンイベント =====
111    btnPrev.addEventListener('click', function () {
112        prev();
113        startAuto();
114    });
115
116    btnNext.addEventListener('click', function () {
117        next();
118        startAuto();
119    });
120
121    // ===== キーボード操作 =====
122    document.addEventListener('keydown', function (e) {
123        if (e.key === 'ArrowLeft') { prev(); startAuto(); }
124        if (e.key === 'ArrowRight') { next(); startAuto(); }
125    });
126
127    // ===== タッチスワイプ =====
128    var touchStartX = 0;
129    slider.addEventListener('touchstart', function (e) {
130        touchStartX = e.changedTouches[0].clientX;
131    }, { passive: true });
132
133    slider.addEventListener('touchend', function (e) {
134        var diff = touchStartX - e.changedTouches[0].clientX;
135        if (Math.abs(diff) > 40) {
136            if (diff > 0) { next(); } else { prev(); }
137            startAuto();
138        }
139    }, { passive: true });
140
141    // ===== ホバーで一時停止 =====
142    slider.addEventListener('mouseenter', function () { stopAuto(); });
143    slider.addEventListener('mouseleave', function () { startAuto(); });
144
145    // ===== 画面非表示時に一時停止(省電力) =====
146    document.addEventListener('visibilitychange', function () {
147        if (document.hidden) { stopAuto(); } else { startAuto(); }
148    });
149
150    // ===== 起動 =====
151    startAuto();
152}());

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.