スワイプでテキパキ切り替わるスライドUI を作ってみた

こういうのを作ってみました😀

## demo
![画面収録-2023-12-13-13.43.42.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3001238/cd6573b2-8444-bc07-b0b4-2499abe3f072.gif)


## How to 
ステップバイステップで、このUI の実装方法を紹介していきます！

今回このUI を実装は [Framer Motion](https://www.framer.com/motion/), [Next.js](https://nextjs.org), [Tailwind CSS](https://tailwindcss.com) を使いました。
アニメ部分のキーであるFramer Motion は、ポピュラーで高度なアニメーションを簡単に実装できるライブラリです。

### クリックで拡大・移動させる
Framer Motion のいいところは、はじまりと最後だけ定義してもちゃんと動くところです。
👇を見てみてください、これだけで拡大・移動させることができます。
```typescript
import { useState } from 'react'
import { motion } from 'framer-motion'


export default function SampleCard() {

    const [isSelected, setIsSelected] = useState(false)

    return (
        <>
            <motion.li
                layoutId="card"
                className="text-blue-600 font-bold w-72 p-7 rounded-md bg-blue-400 list-none mx-auto"
                onClick={() => setIsSelected(true)}
            >
                <motion.h1>Hello</motion.h1>
            </motion.li>
            {isSelected && (
                <motion.div
                    layoutId="card"
                    className="text-green-600 font-bold w-8/12 h-96 p-7 rounded-md bg-green-400 mx-auto"
                >
                    <motion.button className="text-white" onClick={() => setIsSelected(false)}>
                        close
                    </motion.button>
                    <motion.h1>Hello from Big</motion.h1>
                </motion.div>
            )}
        </>
    )
}

```

![画面収録-2023-12-13-2.23.50.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3001238/2a972d0b-7384-1a84-9d47-929dfcff6790.gif)

### ドラッグを認識させる
ドラッグを認識させるのも簡単です。motion タグに、drag 属性とonDrag 属性を設定するだけで認識できます。
```typescript
<motion.div
    layoutId="card"
    className="text-green-600 font-bold w-8/12 h-96 p-7 rounded-md bg-green-400 mx-auto"
    drag="x"
    onDrag={() => {
        console.log("Dragged !")
    }}
>

```
![画面収録-2023-12-13-13.56.21.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3001238/8cc437b5-1753-e713-f31a-435e63a83e99.gif)


### ドラッグでイベントを発生させる
ドラッグイベントを更新して、座標に応じてイベントを発生させます。
```typescript
<motion.div
    layoutId="card"
    className="text-green-600 font-bold w-8/12 h-96 p-7 rounded-md bg-green-400 mx-auto"
    drag="x"
    onDrag={(e, {offset}) => {
        // 初期座標からx軸で100px 以上移動したら
        if (offset.x > 100) {
            console.log("Wow !")
        }
    }}
>

```
![画面収録-2023-12-13-15.04.26_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3001238/2dc952ba-f720-c2c7-e7d3-b65b46cfbaa0.gif)


### スワイプで切り替わるようにする
あとは、イベントで切り替わるようにハンドルしたら完了です！
AnimatePresence タグでアンマウントされたときまで、自動でアニメーションを適用できます。
```typescript
import { useState } from 'react'
import { motion } from 'framer-motion'
import { AnimatePresence } from 'framer-motion'


export default function SampleCard() {

    const items = [{ id: "left" }, { id: "right" }]

    const [selected, setSelected] = useState("")

    return (
        <>
            <ul className = "flex mx-auto gap-5">
                {items.map(item => (
                    <motion.li
                        key={item.id}
                        layoutId={item.id}
                        className="text-blue-600 font-bold w-72 p-7 rounded-md bg-blue-400 list-none"
                        onClick={() => setSelected(item.id)}
                    >
                        <motion.h1>Hello</motion.h1>
                    </motion.li>                    
                ))}
            </ul>
            {selected !== "" && (
                <AnimatePresence>
                    <div className = "h-screen w-screen z-20 flex justify-center items-center">
                        <motion.div
                            key={selected}
                            layoutId={selected}
                            className="text-green-600 font-bold absolute w-8/12 h-96 p-7 rounded-md bg-green-400"
                            drag="x"
                            onDrag={(e, {offset}) => {
                                console.log("Dragged !")
                                // 初期座標からx軸で100px 以上移動したら
                                if (offset.x > 100) {
                                    setSelected("right")
                                } else if (offset.x < -100) {
                                    setSelected("left")
                                }
                            }}
                        >
                            <motion.button className="text-white" onClick={() => setSelected("")}>
                                close
                            </motion.button>
                            <motion.h1>Hello from {selected}</motion.h1>
                        </motion.div>
                    </div>
                </AnimatePresence>
            )}
        </>
    )
}


```

## おわりに
いかがだったでしょうか？
Framer Motion を使う際に参考にしていただければ幸いです！

このQiita の記事は、BitStar のアドイベントカレンダーのとして掲載しました。
ぜひ、他の記事も見ていただければ嬉しいです！

https://qiita.com/advent-calendar/2023/bitstar

BitStar では、React, Rails を使ってサービスを開発中です🧑‍💻
ご興味がある方は以下からご確認ください！

https://www.wantedly.com/projects/1508637