
はじめに
こんにちは。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