ReactとBulmaでドラッグで移動可能なダイアログを実装する

2023年3月27日月曜日

react

t f B! P L

ReactとBulmaの組み合わせで、マウスドラッグ可能なモーダルダイアログを実装する方法について紹介します。

スポンサーリンク

前提

対象のReactプロジェクトに対して、Bulmaのセットアップが出来ているものとします。

まだセットアップできていない場合は、以下のコマンドでインストールすれば使えます。

npm install bulma

もしくは、CDN経由で使う場合は、 public/index.html に以下のコードを追加します。

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">

ドラッグ可能なダイアログの実装

本題のドラッグ可能なモーダルダイアログの実装をします。詳細な処理内容はコードの中のコメントを見て頂ければと思います。

import React, { useEffect, useRef, useState } from 'react'

function DraggableDialog() {

  //ドラッグ中かを判定するフラグ
  const [isDrag, setIsDrag] = useState(false)

  //ドラッグ情報を保持するステート
  const [state, setState] = useState({
    sx: 0,    //ドラッグ開始時の「pageX」
    sy: 0,    //ドラッグ開始時の「pageY」
    mx: 0,    //ドラッグ時のX方向の移動量を保持
    my: 0,    //ドラッグ時のY方向の移動量を保持
    tx: 0,    //対象要素の相対位置(X)
    ty: 0,    //対象要素の相対位置(Y)
  })
 
  //Mouse Down
  const handleMouseDown = (e) => {
    //マウスドラッグ開始位置の初期化
    setState(old => {
      return {
        ...old,
        sx: e.pageX, 
        sy: e.pageY,
        mx: 0,
        my: 0,
      }
    })
    //ドラッグ中のフラグをON
    setIsDrag(true)
  }

  useEffect(() => {
    //ドラッグ中の場合はウィンドウのマウスイベントを監視
    if (isDrag) {
      window.addEventListener("mousemove", handleMouseMove)
      window.addEventListener("mouseup", handleMouseUp)      
    }
  
    return () => {
      window.removeEventListener("mousemove", handleMouseMove)
      window.removeEventListener("mouseup", handleMouseUp)
    }
  }, [isDrag])

  //ウィンドウのマウス移動イベント
  const handleMouseMove = (e) => {
    setState(old => {
      return {
        ...old,
        mx: e.pageX - old.sx,
        my: e.pageY - old.sy,
      }
    })
    e.preventDefault()
  }

  /**
  * ヘッダのマウスダウン
  * @param {MouseEvent} e 
  */
  const handleMouseUp = (e) => {
    //if (!isDrag) return
    setState(old => {
      return {
        ...old,
        tx: old.tx + old.mx,
        ty: old.ty + old.my,
        mx: 0,
        my: 0,
      }
    })
    setIsDrag(false)
  }

  return (
    <div className="modal is-active">
      <div className="modal-background"></div>

      <div 
        className="modal-card"
        style={{
          transform: `translate(${state.tx + state.mx}px, ${state.ty + state.my}px)`
        }}
      >
        <header 
          className="modal-card-head"
          onMouseDown={handleMouseDown}
        >
          <p className="modal-card-title">Modal title</p>
          <button className="delete" aria-label="close"></button>
        </header>
        <section className="modal-card-body">
          コンテンツ
        </section>
        <footer className="modal-card-foot">
          <button className="button is-success">OK</button>
          <button className="button">Cancel</button>
        </footer>
      </div>
    </div>
  )
}

export default DraggableDialog

このコードでは、useStateフックを使用して、ドラッグの開始位置、移動距離、ドラッグ中かどうかなどの情報を保持するstate変数を宣言します。また、今回はダイアログのヘッダー部分をドラッグすると移動するようにしています。

スポンサーリンク
スポンサーリンク

このブログを検索

Profile

自分の写真
Webアプリエンジニア。 日々新しい技術を追い求めてブログでアウトプットしています。
プロフィール画像は、猫村ゆゆこ様に書いてもらいました。

仕事募集もしていたり、していなかったり。

QooQ