【React】Mapのようにスワイプ移動できるページを実装する


概要

Google Mapのようにドラッグ操作でスワイプできるページを実装します。

https://vw5z8.csb.app/

ドラッグ操作には、Framer Motionを使用します。

スタイリングには、emotion/css(CSS in JS)を使用しています。

実装

下図は実装のイメージです。

screenが移動するのではなく、paperだけが移動すると考えてください。

  • screenは、ブラウザの表示領域(1vw・1vhの範囲)です。
  • 今回は縦横がscreenの2倍を領域をもつマップを作成したので、paperは2vw・2vhになっています。
  • containerは、paperの可動域です。screenに表示されるのが、#44B3C2パネルの左上から、#5D4C46パネルの右下までにしたいので、3vw・3vhとなっています。
import { motion } from 'framer-motion';
import React, { useRef, VFC } from 'react';
import { css } from '@emotion/css';

export const MapPage: VFC = () => {
    const containerRef = useRef<HTMLDivElement>(null)

    return (
        <motion.div ref={containerRef} className={styles.container}>
            <motion.div className={styles.paper} drag dragPropagation dragConstraints={containerRef}>
                <div className={styles.panel('#44B3C2')}>
                    <div className={styles.text}>Panel 1</div>
                </div>
                <div className={styles.panel('#F1A94E')}>
                    <div className={styles.text}>Panel 2</div>
                </div>
                <div className={styles.panel('#E45641')}>
                    <div className={styles.text}>Panel 3</div>
                </div>
                <div className={styles.panel('#5D4C46')}>
                    <div className={styles.text}>Panel 4</div>
                </div>
            </motion.div>
        </motion.div>
    )
}

const styles = {
    container: css`
        position: relative;
        width: 300vw;
        height: 300vh;
        /* (300 - 100) / 2 */
        transform: translate(-100vw, -100vh);
        display: flex;
        justify-content: center;
        align-items: center;
    `,
    paper: css`
        position: absolute;
        width: 200vw;
        height: 200vh;
        display: flex;
        flex-wrap: wrap;
        /* start page1 */
        top: 100vh;
        left: 100vw;
    `,
    panel: (bgColor: string) => css`
        width: 50%;
        height: 50%;
        background-color: ${bgColor};
        display: flex;
        justify-content: center;
        align-items: center;
    `,
    text: css`
        color: white;
        font-size: 3rem;
    `
}

paper(styles.paperを割り当ててるdiv)要素をスワイプさせたいので、ドラッグに関するプロパティを割り当てます。

  • drag:要素をドラッグできるようにする
  • dragPropagation:親要素をドラッグしたときに子要素も移動するようにする
  • dragConstraints:ドラッグできる領域を決める。今回はcontainer要素を可動域にしたいので、containerRefを割り当てています。

Framer MotionのDragについて詳しく知りたい方は、以下を参照してください。

成果物