以下の記事を参考に、React でカルーセルを自作してみました。
完成したものがこちらになります。
See the Pen @emotion/react in CodePen by Wataru Yamada (@wataru86) on CodePen.
実装までの流れ
完成したプログラムを CodePen で表示したかったので、最初に CodePen で TypeScript + React + @emotion/react
が使える環境を作りました。
@emotion/react
を CodePen で使用できるようにするまで試行錯誤したときの記事はこちらになります。
参考にした記事に書いてある通り、親要素に white-space: nowrap
を、子要素に display: inline-block
を付けました。
const App = () => {
return (
<div css={screenStyle}>
<div css={itemContainerStyle}>
<div css={itemStyle}>1</div>
<div css={itemStyle}>2</div>
<div css={itemStyle}>3</div>
<div css={itemStyle}>4</div>
<div css={itemStyle}>5</div>
</div>
</div>
)
}
const screenStyle = css({
width: 200,
overflow: "hidden",
})
const itemContainerStyle =
css({
width: 100,
height: 100,
whiteSpace: "nowrap",
margin: "auto",
})
const itemStyle = css({
width: "100%",
height: "100%",
display: "inline-block",
})
これで、あとは itemContainerStyle
に transition
と transform: translate3d
を追加して、変数で移動する距離を計算すればカルーセルになります。
useState
で中央に表示する item の番号の状態を持って、 button
で増減させれば左右に動かせます。
const App = () => {
const [currentItem, setCurrentItem] = useState(0)
const handlePrev = () => setCurrentItem((n) => n - 1)
const handleNext = () => setCurrentItem((n) => n + 1)
return (
<>
<div css={screenStyle}>
<div css={itemContainerStyle}>
<div css={itemStyle}>1</div>
<div css={itemStyle}>2</div>
<div css={itemStyle}>3</div>
<div css={itemStyle}>4</div>
<div css={itemStyle}>5</div>
</div>
</div>
<div>
<button onClick={handlePrev}>prev</button>
<button onClick={handleNext}>next</button>
<button onClick={toggleColor}>toggle color</button>
</div>
</>
)
}
const itemContainerStyle = (n: number) =>
css({
width: 100,
height: 100,
whiteSpace: "nowrap",
margin: "auto",
transform: `translate3d(${-100 * n}%, 0, 0)`,
...(isTransition ? { transition: "transform 1s" } : {}),
})
無限カルーセルの方法もこちらの記事に書いてあったので、
方法を簡単に説明すると、以下を追加します。
- リストの前後に要素のコピーを追加する
- コピーした要素への遷移が終わったときに、アニメーションなしでコピー元の要素に移動する
元のリストはこれで、
1 2 3 4 5
コピー後はこうなります。
4' 5' 1 2 3 4 5 1' 2'
^^ ^^ ^^ ^^ <- コピーした要素
5
から 1'
にアニメーションした後に、アニメーションなしで 1
に移動すれば無限に回すことができます。
実装としては、新たな状態 isTransition
を一つ用意して、これが true
のときにアニメーションして、これが false
の時はアニメーションなしで移動させます。
const [isTransition, setIsTransition] = useState(false)
そして、onTransitionEnd
で isTransition
を変化させてコピーした要素から元の要素へ移動を行えば完成です。
<div
css={itemContainerStyle(currentItem, isTransition)}
onTransitionEnd={handleTransitionEnd}
>
以下の例の toggle color
ボタンで、コピーした要素と元の要素の色を分けたり、同じにしたりできるので、試してみてください!
See the Pen @emotion/react in CodePen by Wataru Yamada (@wataru86) on CodePen.
詳しいロジックの説明は元の記事に記載してあるのでそちらを参考にしてください。