Last active
June 29, 2022 12:53
-
-
Save kobitoDevelopment/7fbaa1d0d508b8e53ae12997bb38e2e2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <div class="wrapper"> | |
| <div class="js-scroll-slider-wrap"> | |
| <div class="js-scroll-slider" data-auto-speed-ratio="0.5"> | |
| <div class="js-scroll-slider__item"><img src="https://picsum.photos/id/275/400/400" alt=""></div> | |
| <div class="js-scroll-slider__item"><img src="https://picsum.photos/id/200/400/400" alt=""></div> | |
| <div class="js-scroll-slider__item"><img src="https://picsum.photos/id/1062/400/400" alt=""></div> | |
| <div class="js-scroll-slider__item"><img src="https://picsum.photos/id/1003/400/400" alt=""></div> | |
| <div class="js-scroll-slider__item"><img src="https://picsum.photos/id/1001/400/400" alt=""></div> | |
| </div> | |
| </div> | |
| <div class="js-scroll-slider-wrap"> | |
| <div class="js-scroll-slider is-reverse" data-auto-speed-ratio="0.5"> | |
| <div class="js-scroll-slider__item"><img src="https://picsum.photos/id/270/400/400" alt=""></div> | |
| <div class="js-scroll-slider__item"><img src="https://picsum.photos/id/10/400/400" alt=""></div> | |
| <div class="js-scroll-slider__item"><img src="https://picsum.photos/id/103/400/400" alt=""></div> | |
| </div> | |
| </div> | |
| </div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //要素数が変わっても一定の速度を保つ等速スクロールスライダー(ドラッグ、スワイプ動作対応) | |
| const scrollSlider = document.querySelectorAll(".js-scroll-slider"); | |
| if (scrollSlider.length > 0) { | |
| scrollSlider.forEach(function (slider, index) { | |
| //スクロールスピード設定 | |
| const speed = slider.dataset.autoSpeedRatio ? Number(slider.dataset.autoSpeedRatio) : 0.5; | |
| //observer監視用 親要素生成 | |
| const sliderWrap = document.querySelector(".js-scroll-slider-wrap"); | |
| //スライド要素を取得 | |
| const children = slider.children; // スライダー要素1つずつを子要素全てを含んで配列に格納 | |
| const childLength = children.length; | |
| //スライド要素一式を文字列で取得(複製用) | |
| let baseChildren = ""; | |
| for (let i = 0; i < children.length; i++) { | |
| baseChildren += children[i].outerHTML; | |
| } | |
| //スライド要素を1つ取得 | |
| const firstChild = slider.firstElementChild; | |
| //スライダー定義関数 | |
| let sliderWidth, winWidth; | |
| let countWidth = 0; | |
| let addCount = 1; | |
| const initializeSlider = function (countWidth, addCount) { | |
| //スライダー全体の幅を取得 | |
| const styles = getComputedStyle(firstChild); | |
| const width = parseFloat(styles.width); | |
| const marginRight = parseFloat(styles.marginRight); | |
| sliderWidth = (width + marginRight) * childLength; | |
| //画面外まで表示を確保するため要素を複製 | |
| winWidth = window.innerWidth; | |
| const checkWidth = winWidth * 3; | |
| countWidth = sliderWidth * addCount; | |
| while (countWidth < checkWidth) { | |
| slider.insertAdjacentHTML("beforeend", baseChildren); | |
| ++addCount; | |
| countWidth = sliderWidth * addCount; | |
| } | |
| //スライダー位置(複製したスライダーを画面外の左右に配置) | |
| slider.style.marginLeft = "-" + sliderWidth + "px"; | |
| }; | |
| initializeSlider(countWidth, addCount); | |
| /* | |
| アニメーション設定 | |
| */ | |
| let requestID; | |
| let x = 0; | |
| //反転処理判別 | |
| const isReverse = slider.classList.contains("is-reverse"); | |
| //スクロール関数 | |
| const scrollAnime = function () { | |
| //スライダーの終了ポイントを過ぎていたら位置を戻す | |
| if ((!isReverse && x <= -sliderWidth) || (isReverse && x >= sliderWidth)) { | |
| x = 0; | |
| } | |
| slider.style.transform = "translateX(" + x + "px)"; | |
| if (isReverse) { | |
| x += 1 * speed; | |
| } else { | |
| x -= 1 * speed; | |
| } | |
| requestID = requestAnimationFrame(scrollAnime); | |
| }; | |
| /* | |
| 要素が画面内に入ったら処理開始 | |
| */ | |
| //画面内に存在しているか判別 | |
| let isIntersecting = false; | |
| const observer = new IntersectionObserver(function (entries) { | |
| entries.forEach(function (entry) { | |
| if (entry.isIntersecting) { | |
| cancelAnimationFrame(requestID); | |
| //自動スクロール開始 | |
| requestID = requestAnimationFrame(scrollAnime); | |
| //画面内判定フラグ(endFunc用) | |
| isIntersecting = true; | |
| } else { | |
| //自動スクロール解除 | |
| cancelAnimationFrame(requestID); | |
| //画面内判定フラグ(endFunc用) | |
| isIntersecting = false; | |
| } | |
| }); | |
| }); | |
| //observer監視開始 | |
| observer.observe(sliderWrap); | |
| /* | |
| スワイプ、ドラッグ対応 | |
| */ | |
| //スライダーのstyleを取得 | |
| const sliderStyles = getComputedStyle(slider); | |
| //クリック、タッチされているか判別 | |
| let isDown = false; | |
| //スワイプ、ドラッグで右に動かしているか判別 | |
| let isRightMove = false; | |
| // クリック開始位置を保存 | |
| let startX; | |
| //スライダー位置を保存 | |
| let sliderX; | |
| //マウスダウン、タッチスタート(移動開始) | |
| const startFunc = function (e) { | |
| e.preventDefault(); | |
| //画面上のx位置を取得 | |
| if (e.type === "touchstart") { | |
| startX = e.changedTouches[0].pageX; | |
| } else { | |
| startX = e.pageX; | |
| } | |
| //スライダーにclass追加 | |
| slider.classList.add("is-drag"); | |
| isDown = true; | |
| //マウスポインタを変更 | |
| slider.style.cursor = "grabbing"; | |
| //自動スクロール停止 | |
| cancelAnimationFrame(requestID); | |
| //スライダー位置取得 (translateXの値のみ取得) | |
| let matrix = new DOMMatrix(sliderStyles.transform); | |
| sliderX = parseFloat(matrix.m41); | |
| }; | |
| //マウスムーブ、タッチムーブ(移動中) | |
| const moveFunc = function (e) { | |
| //マウスダウン、タッチスタート時のみ処理 | |
| if (!isDown) { | |
| return; | |
| } | |
| e.preventDefault(); | |
| //移動距離を取得 | |
| let moveX; | |
| if (e.type === "touchmove") { | |
| moveX = startX - e.changedTouches[0].pageX; | |
| } else { | |
| moveX = startX - e.pageX; | |
| } | |
| //左右どちらに移動しているか判別 | |
| if (moveX < 0) { | |
| isRightMove = true; | |
| } else { | |
| isRightMove = false; | |
| } | |
| //スライダーを移動させる | |
| requestID = requestAnimationFrame(function () { | |
| x = sliderX - moveX; | |
| slider.style.transform = "translateX(" + x + "px)"; | |
| }); | |
| }; | |
| //マウスアップ、タッチエンド(移動終了) | |
| const endFunc = function (e) { | |
| e.preventDefault(); | |
| //設定をリセット | |
| slider.classList.remove("is-drag"); | |
| isDown = false; | |
| //マウスポインタを変更 | |
| slider.style.cursor = "pointer"; | |
| //スライダーの終了ポイントを過ぎていたら位置を戻す | |
| if (!isRightMove && x * -1 >= sliderWidth) { | |
| x = sliderWidth - x * -1; | |
| slider.style.transform = "translateX(" + x + "px)"; | |
| } else if (isRightMove && x >= 0) { | |
| x = -(sliderWidth - x); | |
| slider.style.transform = "translateX(" + x + "px)"; | |
| } | |
| //アニメーションをリセットし画面内の場合は自動スクロール開始 | |
| cancelAnimationFrame(requestID); | |
| if (isIntersecting) { | |
| requestID = requestAnimationFrame(scrollAnime); | |
| } | |
| }; | |
| //スワイプ対応 | |
| slider.addEventListener("touchstart", startFunc, { passive: true }); | |
| slider.addEventListener("touchmove", moveFunc, { passive: true }); | |
| slider.addEventListener("touchend", endFunc); | |
| //マウスドラッグ対応 | |
| slider.addEventListener("mouseenter", function () { | |
| //マウスポインタを変更 | |
| slider.style.cursor = "pointer"; | |
| }); | |
| slider.addEventListener("mousedown", startFunc); | |
| slider.addEventListener("mousemove", moveFunc); | |
| slider.addEventListener("mouseup", endFunc); | |
| slider.addEventListener("mouseleave", endFunc); | |
| /* | |
| レスポンシブ対応 | |
| */ | |
| window.addEventListener("resize", function () { | |
| // スライダー要素幅・window幅を再計算 | |
| initializeSlider(countWidth, addCount); | |
| //スライダー再定義 | |
| cancelAnimationFrame(requestID); | |
| requestID = requestAnimationFrame(scrollAnime); | |
| }); | |
| }); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| .wrapper { | |
| overflow: hidden; | |
| } | |
| .js-scroll-slider { | |
| display: flex; | |
| margin-bottom: 20px; | |
| } | |
| .js-scroll-slider__item { | |
| flex-shrink: 0; | |
| width: 400px; //希望のスライダー横幅 | |
| aspect-ratio: 1/1; //希望のスライダー縦横比 | |
| margin-right: 20px; | |
| img { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| @include mqS { | |
| width: 200px; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment