// *スクロールバーを含まないvwの値設定 const setViewportWidth = function () { const $100vw = document.documentElement.clientWidth; const $50vw = $100vw / 2; const $1vw = $100vw / 100; document.documentElement.style.setProperty('--1vw', `${$1vw}px`); document.documentElement.style.setProperty('--50vw', `${$50vw}px`); document.documentElement.style.setProperty('--100vw', `${$100vw}px`); } window.addEventListener('DOMContentLoaded', setViewportWidth); window.addEventListener('resize', setViewportWidth); // *slickスライダー(ローカルjs) document.addEventListener('DOMContentLoaded', function () { const slider = document.querySelector('.howto-app__list'); const prevBtn = document.querySelector('.slick-prev'); const nextBtn = document.querySelector('.slick-next'); // クローンスライドの追加 let slides = Array.from(slider.children); const firstClone = slides[0].cloneNode(true); const lastClone = slides[slides.length - 1].cloneNode(true); firstClone.classList.add('clone'); lastClone.classList.add('clone'); slider.insertBefore(lastClone, slides[0]); slider.appendChild(firstClone); // クローン追加後にスライド配列を再取得 slides = Array.from(slider.children); let currentIndex = 1; // 最初は本物の1枚目 let isEnabled = true; let autoplayInterval; let isTransitioning = false; // スライド位置を更新 const updateSlider = (withTransition = true) => { const slideWidth = slides[0].offsetWidth; const gap = parseFloat(getComputedStyle(slider).columnGap) || 0; const offset = currentIndex * (slideWidth + gap); if (withTransition) { slider.style.transition = 'transform 1s ease'; } else { slider.style.transition = 'none'; } slider.style.transform = `translateX(-${offset}px)`; }; // スライド移動処理 const goToSlide = (index) => { if (isTransitioning) return; isTransitioning = true; currentIndex = index; updateSlider(true); }; // オートプレイ開始 const autoplay = () => { autoplayInterval = setInterval(() => { goToSlide(currentIndex + 1); }, 5000); }; // オートプレイ停止 const stopAutoplay = () => { clearInterval(autoplayInterval); }; // レスポンシブ動作 const checkResponsive = () => { if (window.innerWidth >= 744) { slider.style.display = 'grid'; slider.style.transform = 'none'; stopAutoplay(); prevBtn.style.display = 'none'; nextBtn.style.display = 'none'; isEnabled = false; } else { slider.style.display = 'flex'; updateSlider(false); prevBtn.style.display = 'block'; nextBtn.style.display = 'block'; isEnabled = true; autoplay(); } }; // 前へ・次へボタン操作 prevBtn.addEventListener('click', () => { if (isEnabled) goToSlide(currentIndex - 1); }); nextBtn.addEventListener('click', () => { if (isEnabled) goToSlide(currentIndex + 1); }); // スライドのtransition終了時にクローンから本物へジャンプ slider.addEventListener('transitionend', () => { const realSlidesCount = slides.length - 2; if (currentIndex === 0) { // 最後のクローンから本物の最後へ currentIndex = realSlidesCount; updateSlider(false); } else if (currentIndex === slides.length - 1) { // 最初のクローンから本物の最初へ currentIndex = 1; updateSlider(false); } isTransitioning = false; }); // ウィンドウリサイズに対応 window.addEventListener('resize', checkResponsive); // 初期化処理 slider.style.display = 'flex'; slider.style.transition = 'transform 1s ease'; Array.from(slider.children).forEach(slide => { slide.style.flex = '0 0 100%'; }); // 最初の本物スライドに位置を合わせる updateSlider(false); checkResponsive(); }); // *detailsアコーディオン document.addEventListener("DOMContentLoaded", () => { setUpAccordion(); }); const setUpAccordion = () => { const details = document.querySelectorAll(".js-details"); const RUNNING_VALUE = "running"; // アニメーション実行中のときに付与する予定のカスタムデータ属性の値 const IS_OPENED_CLASS = "is-opened"; // アイコン操作用のクラス名 details.forEach((element) => { const summary = element.querySelector(".js-summary"); const content = element.querySelector(".js-content"); summary.addEventListener("click", (event) => { // デフォルトの挙動を無効化 event.preventDefault(); // 連打防止用。アニメーション中だったらクリックイベントを受け付けないでリターンする if (element.dataset.animStatus === RUNNING_VALUE) { return; } // detailsのopen属性を判定 if (element.open) { // アコーディオンを閉じるときの処理 // アイコン操作用クラスを切り替える(クラスを取り除く) element.classList.toggle(IS_OPENED_CLASS); // アニメーションを実行 const closingAnim = content.animate(closingAnimKeyframes(content), animTiming); // アニメーション実行中用の値を付与 element.dataset.animStatus = RUNNING_VALUE; // アニメーションの完了後に closingAnim.onfinish = () => { // open属性を取り除く element.removeAttribute("open"); // アニメーション実行中用の値を取り除く element.dataset.animStatus = ""; }; } else { // アコーディオンを開くときの処理 // open属性を付与 element.setAttribute("open", "true"); // アイコン操作用クラスを切り替える(クラスを付与) element.classList.toggle(IS_OPENED_CLASS); // アニメーションを実行 const openingAnim = content.animate(openingAnimKeyframes(content), animTiming); // アニメーション実行中用の値を入れる element.dataset.animStatus = RUNNING_VALUE; // アニメーション完了後にアニメーション実行中用の値を取り除く openingAnim.onfinish = () => { element.dataset.animStatus = ""; }; } }); }); } // アニメーションの時間とイージング const animTiming = { duration: 400, easing: "ease-out" }; // アコーディオンを閉じるときのキーフレーム const closingAnimKeyframes = (content) => [ { height: content.offsetHeight + 'px', // height: "auto"だとうまく計算されないため要素の高さを指定する opacity: 1, }, { height: 0, opacity: 0, } ]; // アコーディオンを開くときのキーフレーム const openingAnimKeyframes = (content) => [ { height: 0, opacity: 0, }, { height: content.offsetHeight + 'px', opacity: 1, } ];