はじめに
こんにちは。KENTEMでフロントエンドを担当しているM・Sです。
ユーザーのドラッグやタッチによって要素を移動させたいときありますよね。
基本的にはDragEvent
やMouseEvent
, TouchEvent
などを使うことが多いかと思いますが、今回はPointerEvent
を強く推していきたいと思います。
結論から言うと、PointerEvent
を用いることでマルチデバイス対応が非常にシンプルになります。
PointerEventとは?
PointerEvent は、マウス・タッチ・ペンなどの入力デバイスを統一的に扱うために作られたイベントです。
ポインターは、入力機器(マウス、ペン、またはタッチパネルの上の接触点など)のハードウェアにとらわれない表現です。 ポインターは、画面などの接触面上の特定の座標(または座標の集合)をターゲットにすることができます。 MDN: PointerEvent
サンプル
早速ですがサンプルです。
今回はReactで記述しています。
See the Pen pointer-dnd by sho (@shogo61) on CodePen.
このサンプルでは、座標の取得をe.movement
, スタイルの指定をtransform : translate()
で行なっています。
これ以外にもe.client
で座標を取得したり、absolute
,top
,left
でスタイルの指定をすることも可能です。
イベントを快適にするプロパティ
currentTarget.setPointerCapture(pointerId)
これをPointerDown
またはPointerMove
のイベントにつけることで、要素からカーソルが離れてもイベントが続くようになります。
引数はイベントの引数に含まれているので、引数を e とした場合、 (e.pointerId) とすれば問題ありません。
pointerCaptureを設定しない場合↓
ゆっくり動かしている間は正常に動いていますが、少しカーソルの動きが早くなると要素が置いていかれるのがわかるかと思います。
currentTarget.releasePointerCapture(pointerId)
上述のsetPointerCapture
で設定したポインターキャプチャを解放(停止)します。
これをつけないと想定外の挙動を起こす可能性があるようですが、私の環境では再現できませんでした。
touch-action : none
タッチ本来の動作との競合を防ぐCSSプロパティです。
例えば、スマホでドラッグした場合にそれが画面のスクロールなのか、要素を動かすドラッグなのか判定できません。
そのため、画面のスクロールと要素の移動が同時に発生し、大変なことになります。
これを防ぐために、動かす対象の要素にtouch-action : none
を設定します。
この設定により、指定された要素上では画面のスクロールが無効になります。
(余談)addEventListener
を使ってpassive:false
をつける方法もありますが、こちらの方がシンプルなので気に入っています。
PointerEventの強み
先述の通りですが、PointerEvent
の強みは何といっても「イベントの一元化」です。
PointerEvent
を使うことで、スマホ対応のWebサービスにわざわざTouchEvent
を使う必要がありません。
それでいて、使用感はTouchEvent
やDragEvent
と大差ないのです。
FirefoxではDragの座標を取得できない?
Firefoxでは、onDrag
のclientX,Y
が取得できません。Bugzilla
16年前から言われているので思想として修正しないのかもしれません...
解決策として、PointerEvent
が有効です。
PointerEvent
を用いることで、Firefoxでもchromium系であっても普通にclientX,Y
を取得することができます。
まとめ
PointerEvent
を使えば、複数の入力デバイス対応がとてもシンプルになります!
皆さんも要素を動かすときはPointerEvent
を使ってみてはいかがでしょうか?
おわりに
KENTEMでは、様々な拠点でエンジニアを大募集しています! 建設×ITにご興味頂いた方は、是非下記のリンクからご応募ください。 recruit.kentem.jp career.kentem.jp