CSSのscroll-snapをつかってカルーセルをつくってみました。
ループ無し
DEMO
ソースコード(抜粋)
JavaScript
const { useEffect, useRef, useState } = React; function App() { const [ list ] = useState([1, 2, 3]); const width = 200; return ( <div className="carousel"> <ul> {list.map((item, i) => { return ( <li key={ i } data-index={ item } >{ item }</li> ); })} </ul> </div> ); }
SCSS
.carousel { width: 200px; height: 200px; overflow: scroll; cursor: pointer; scroll-snap-type: x mandatory; ul { display: flex; align-items: center; justify-content: center; width: 300%; height: 100%; } li { display: flex; align-items: center; justify-content: center; width: calc(100% / 3); height: 100%; color: #ECEFF1; font-size: 80px; scroll-snap-align: center; &[data-index='1'] { background: #B71C1C; } &[data-index='2'] { background: #311B92; } &[data-index='3'] { background: #004D40; } } }
解説
ループ無し、つまり例で言うところの①と③を繋げなくて良いのであればものすごくシンプルです。
- overflow: scrollを掛けた要素に、scroll-snap-type: x mandatory (横方向にスクロールする場合)を掛ける
- スナップを効かせたい要素に、scroll-snap-align: center (中心合わせの場合)を掛ける
の2点でOKです。
例ではReactを使っていますが、Reactを使う必要がないぐらいシンプルです。
ループあり
DEMO
ソースコード(抜粋)
Javascript
const { useEffect, useRef, useState } = React; function App() { const [ list, setList ] = useState([3, 1, 2]); const timerRef = useRef(-1); const carouselRef = useRef(); const width = 200; useEffect(() => { // リストが入れ替わった際に真ん中の要素までスクロールする carouselRef.current.scrollLeft = width; }, [list]); function handleScroll() { clearTimeout(timerRef.current); timerRef.current = setTimeout(() => { // スクロールが終わってから0.1秒後に発火 if (width * (list.length - 1) === carouselRef.current.scrollLeft) { // 右端到達時 setList([ list[1], list[2], list[0] ]); // リストを入れ替える } else if (0 === carouselRef.current.scrollLeft) { // 左端到達時 setList([ list[2], list[0], list[1] ]); // リストを入れ替える } }, 100); } return ( <div ref={ carouselRef } onScroll={ handleScroll } className="carousel" > <ul> {list.map((item, i) => { return ( <li key={ i } data-index={ item } >{ item }</li> ); })} </ul> </div> ); }
SCSS
.carousel { width: 200px; height: 200px; overflow: scroll; cursor: pointer; scroll-snap-type: x mandatory; -ms-overflow-style: none; // スクロールバー非表示 scrollbar-width: none; // スクロールバー非表示 &::-webkit-scrollbar { display: none; // スクロールバー非表示 } ul { display: flex; align-items: center; justify-content: center; width: 300%; height: 100%; } li { display: flex; align-items: center; justify-content: center; width: calc(100% / 3); height: 100%; color: #ECEFF1; font-size: 80px; scroll-snap-align: center; &[data-index='1'] { background: #B71C1C; } &[data-index='2'] { background: #311B92; } &[data-index='3'] { background: #004D40; } } }
解説
スクロールイベントを監視して、右端に来た際、左端に来た際にリストを入れ替えることで、無限にスクロールできるように改良しています。
また、スクロールバーがあるとノイズになるのでCSSの力で非表示にしました。