この記事はReact #2 Advent Calendar 2020の16日目の記事です。
はじめに
この記事ではreact-draggbleでクリックイベントを追加する方法についてまとめています。
react-draggableの情報は少なく、自分は苦労したので、少しでも参考になれば幸いです。
react-draggableとは
github:https://github.com/STRML/react-draggable
デモ
Reactでドラッグ処理を実現する為のライブラリです。
まずはドラッグを実現する
サンプルコード:https://codesandbox.io/s/red-platform-3q7iv?file=/src/components/piece.tsx
react-draggableを素直に使ってドラッグを実現しました。
ポイントをいくつか解説していきます。
ドラッグしたい要素を<Draggable>
でラップする
<Draggable>
要素は既存の要素をラップし、新しいイベントハンドラとスタイルでそれを拡張します。DOMにラッパー要素は作成されません。
とドキュメントにあるので、まずは<Draggable>
で要素をラップします。
Propsにはx、yの座標とonDragイベントを渡しています。
ドラッグイベントでstateを更新して、要素の座標を変える
useStateで座標の状態を定義します。
onDragイベントでは<Draggable>
のdataから得た座標情報でstateを更新しています。
これによりドラッグで要素の座標が変わります
<Draggable>
の型とProps
<Draggable>
の型とPropsはドキュメントではこの様になっています。(https://github.com/STRML/react-draggable#draggable-props)
今回使った箇所はこの辺りです。
onDragイベントの型はDraggbleEventHandlerです。引数で取っているdataの方で座標の更新に使ったlastXとlastYが定義されています。
ドラッグ+クリックを実現する
こっから本題です。
先ほど作ったドラッグ要素にクリックで回転する挙動を加えていきます。
サンプルコード:https://codesandbox.io/s/react-draggble-onclick-jmwz8?file=/src/components/piece.tsx
DOM部分
ドラッグのみの時からの主な変更点は3つ。
・onStopイベント追加
・クリック要素にstyleを加え動的に回転を加える
・クリック要素をdivタグでラップ
1つずつ説明していきます。
onStopイベントの追加
ここではonClickイベントではなく、onStopイベントを用いています。
onClickイベントだとドラッグ終了時にも発火してしまいます。それを防ぐためにonClickに状態を持たせることも可能ですが、今回はonStopイベント実行時にて条件分岐を加えることにしました。
条件の判定はuseRefを使っています。
またonStopにてクリック時のアクションを実装する事で、スマホなどのタッチ操作でもクリックと同じアクションを実装できます。
onClickを使った場合、スマホ用にonTouchStartも用いる必要がありそうです。
参考:https://github.com/STRML/react-draggable/issues/49
クリック要素にstyleを加え動的に回転を加えられる
クリックした要素に実際に回転が見た目で加わる様に、style={}で動的にデータを流し込んでいます。
currentRotateにはuseStateにより更新された回転の状態が格納されます。
クリック要素を<div>
でラップ
こちらに関しては今回の実装の肝になります!!
回転の実装はクリックによりcurrentRotateが更新され、それをstyleの当て込んで実現します。
しかし、<Draggable>
では直下の要素のstyleのtransformを制御してドラッグ機能を実現しています。
<Draggable>
直下でのtransform操作はできないので、クリックイベントでstyleを反映させたい要素を<div>
でラップする事でtransformを反映させています。
イベント処理部分
主な変更点は以下の通りです
・onStopイベントの追加
・useRefの追加
onStopイベントの追加
onStopイベントではクリックした時の挙動を加えていきます。
まず前提条件としてonStopはクリック時も、ドラッグ終了時も両方発火してしまうので、 useRefにて条件分岐を行う事でクリック時にのみ処理をあてます。
条件分岐内ではcurrentRotate+90によって、クリックする度に90度の回転を加えています。
useRefの追加
useRefにてドラッグか否かの情報を持ち、それをもとにonStopでの条件分岐を行っています。
onStop時に参照したいだけなので、useStateではなくuseRefを使っています。
参考①:USEREFの使い方がわからん
参考②:React.useRef()を使って無駄なレンダリングを減らそう
まとめ
・ドラッグ終了時の発火を防ぐために、onStopにてクリック時の処理を書く
・<Draggable>
直下はtransformが制御されている為、動的にスタイリングしたい場合は要素をラップする
上記を踏まえればreact-draggableでもクリックの処理を実現できます。