2021年4月7日水曜日

JavaScript で Drag & Drop

JavaScript で Drag & Drop をしようとして少しハマりました。 まずライブラリなどを使わなくても Drag & Drop は API で実装できる のですが、 サポート状況が微妙なので今のところあまり使えません。 また Web 標準で用意されている機能は Drag を開始しても元のオブジェクトを表示したままで、Drop した瞬間に元とのオブジェクトの表示を消すという動作をします。 少し直感的でない動作で、API もわかりにくい部類に入る気がする。 そんな訳で API を使わずマウスイベントで作るのが普通で、作り方は以下がわかりやすいですね。

巷のライブラリが提供する最低限のこと

それではほとんどの Drag & Drop ライブラリは何をやってくれるのでしょう。 Drag した瞬間に要素をマウスカーソルに追随して、Drop した瞬間にイベントで処理をします。 このへんの機能を提供するのが巷の Drag & Drop ライブラリという訳です。 あとは実際触ってみると、どのライブラリもイベント周りを自作するより簡単にしてくれていますね。 例えばクリックなのかドラッグなのかは、必ずチェックが必要です。 普通に実装すると動いたか動いてないかで場合分けが必要でちょっと面倒。そういう細々としたところを改善してくれる。

多くのライブラリの仕様はちょっと不思議

Drag & Drop ライブラリは、例えば以下のようなライブラリがあります。 多くのライブラリはソートを基本動作にしていることが多く、 案外融通の効かないライブラリが多い印象です。 私は別にソートとかいらないんだよなあ。 そうでないものも、Drag 要素と Drop 要素を宣言的に扱っていることが多く、A→B への Drag 機能を提供するもの、しかも A の中の要素群に対してまとめてイベントを発行するものが多いです。 これ Drag 要素と Drop 要素がたくさんあると、扱いが大変な気がする。 たぶん1対Nでイベントを貼って、Drop したらイベントを貼り直すという作業が必要になる。 やはりあまり融通が効かない感はあります。 どうしてそういう実装になってるのかよくわからないのですが、何か難しいところがあるのかも知れません。 そんな中、desandro/draggabilly はかなり融通が利くライブラリに見えました。

overflow: hidden; に注意

desandro/draggabilly は実際、非常に融通の利くライブラリで、 要素それぞれに Drag & Drop のイベントを設定できます。仕様としても非常にわかりやすい。 ただ実際に実装してみると色々な課題があることがわかりました。 ちなみにこれは自前実装するときにも起きる問題です。 真っ先にハマったのは、overflow: hidden; なオブジェクトから Drag & Drop をするときの表示でした。 Drag & Drop を開始すると、そのオブジェクトの外に出た時点で表示が消えてしまいます。 他にも z-index などの影響は受けやすいです。 移動中にあえて表示を消したい時もあるかもしれませんが、普通はそのような動作は望まないだろうと思う。

上記の問題を避けるには、ほとんどのケースでいったん別のオブジェクト (document.body など) に Drag 要素を移動して、 移動ぶんの座標をずらしながら絶対座標でマウスカーソルに表示を移動するのがより良い動作とわかります。 そのへんだけ修正すると結構いい感じに動作しました。 無理やり絶対座標に変換するので、イベントを張り直す必要があって多少面倒だけど、それくらい。 直すのが少し面倒そうだし、他のライブラリを使ってもどうせ張り直す必要があるので、効率が少し悪いけど別にいいやと。 desandro/draggabilly はサンプルも多いし、たぶん現時点で一番使いやすいライブラリと思う。

今後再び Drag & Drop を実装したり、やっぱ自前で実装しようとなったときに overflow: hidden; と z-index あたりの知見を残しておきたかったので、メモを書いておきます。

0 件のコメント: