8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Qiita株式会社Advent Calendar 2022

Day 11

React でカルーセルを自作してみた

Last updated at Posted at 2022-12-12

以下の記事を参考に、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",
})

これで、あとは itemContainerStyletransitiontransform: 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)

そして、onTransitionEndisTransition を変化させてコピーした要素から元の要素へ移動を行えば完成です。

        <div
          css={itemContainerStyle(currentItem, isTransition)}
          onTransitionEnd={handleTransitionEnd}
        >

以下の例の toggle color ボタンで、コピーした要素と元の要素の色を分けたり、同じにしたりできるので、試してみてください!

See the Pen @emotion/react in CodePen by Wataru Yamada (@wataru86) on CodePen.

詳しいロジックの説明は元の記事に記載してあるのでそちらを参考にしてください。

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?