質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.49%

ドラッグドロップでマウスを素早く動かした時対象が置き去りにされるのを改善したい

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 700

score 23

ドラッグドロップの練習としてコードを書いてみました。
ゆっくりとマウスを動かしているときは実現したい挙動をしてくれるのですが、素早くマウスを動かすとドラッグしたい対象を置き去りにしてしまします。
置き去りにしないようにするにはどうすればいいでしょうか?
わかる方がいればご教授いただければ幸いです。

<table class="box_cover" onMouseDown="return false;" onSelectStart="return false">
  <tr class="row1">
    <td class="r1_low1">
      <div id="box1" class="item">キリン1</div>
    </td>
    <td class="r1_low2">
      <div id="box2" class="item">ライオン1</div>
    </td>
    <td class="r1_low3">
      <div id="box3" class="item">ゾウ1</div>
    </td>
  </tr>
  <tr class="row2">
    <td class="r2_low1">
    </td>
    <td class="r2_low2">
      <div id="box7" class="item">ひよこ1</div>
    </td>
    <td class="r2_low3">
    </td>
  </tr>
  <tr class="row3">
    <td class="r3_low1">
    </td>
    <td class="r3_low2">
      <div id="box8" class="item">ひよこ2</div>
    </td>
    <td class="r3_low3">
    </td>
  </tr>
  <tr class="row4">
    <td class="r4_low1"> 
      <div id="box10" class="item">ゾウ2</div>
    </td>
    <td class="r4_low2">
      <div id="box11" class="item">ライオン2</div>
    </td>
    <td class="r4_low3">
      <div id="box12" class="item">キリン2</div>
    </td>
  </tr>
</table>
let table=document.querySelector('.box_cover');

       function mdown(){
        event.target.classList.add('target');
        table.addEventListener('mousemove',mmove,false);
       }

       function mmove(){
        let clickPoint=event.target;
        console.log(clickPoint);
        let itemHeight=clickPoint.clientHeight/2;
        let itemWidth=clickPoint.clientWidth/2;

        if( clickPoint.classList.contains('target') ){

            if( clickPoint.classList.contains('item') ){
                    let pageY=event.pageY;
                    let pageX=event.pageX;
                    clickPoint.style.top=`${pageY-itemHeight}px`;
                    clickPoint.style.left=`${pageX-itemWidth}px`;
                    clickPoint.classList.add('zIndex');
                    clickPoint.classList.add('hover');
                }

        }


                table.addEventListener('mouseup',mup,false);
       }

       function mup(){
           table.removeEventListener('mousemove',mmove,false);
           event.target.classList.remove('target');
           event.target.classList.remove('zIndex');
       }

       table.addEventListener('mousedown',mdown,false);
       let table=document.querySelector('.box_cover');

       function mdown(){
           let clickPoint=event.target;
            event.target.classList.add('target');

            table.addEventListener('mousemove',mmove,false);
            if( clickPoint.classList.contains('target') ){

                if( clickPoint.classList.contains('item') ){
                    clickPoint.classList.add('zIndex');
                    clickPoint.classList.add('hover');
                }

            }
            table.addEventListener('mouseup',mup,false);
       }

       function mmove(){
        let clickPoint=event.target;
        console.log(clickPoint);
        //座標の読み取り
        let itemHeight=clickPoint.clientHeight/2;
        let itemWidth=clickPoint.clientWidth/2;

        if( clickPoint.classList.contains('target') ){
            if( clickPoint.classList.contains('item') ){
                    let pageY=event.pageY;
                    let pageX=event.pageX;
                    clickPoint.style.top=`${pageY-itemHeight}px`;
                    clickPoint.style.left=`${pageX-itemWidth}px`;
                }
        }

       }

       function mup(){
           table.removeEventListener('mousemove',mmove,false);
           event.target.classList.remove('target');
           event.target.classList.remove('zIndex');
       }

       table.addEventListener('mousedown',mdown,false);
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • m.ts10806

    2020/05/10 20:29

    「素早く」ってどれくらいでしょうか。
    動かすことに集中しすぎてマウスONが離れてないとかないですか?

    キャンセル

  • knight1220

    2020/05/10 20:51

    マウスダウンが離れているということはありません。

    文章で説明するのは非常に説明するのは難しいのですが、
    マウスを鋭く動かすといえばいいのでしょうか、ある程度ゆっくり動かしている間は実現したい挙動をしますが、一気ににマウスの移動量を増やすと対象が置き去りにされマウスポインタのみが移動してしまう状態です。
    マウスダウンしたときにクラス(target)を付与しマウスアップしたときクラスを外すようにしているのですが置き去りにされた要素を検証画面で確認するとクラス名が残ったままの状態になっています。

    キャンセル

回答 1

+1

mousemove は、マウスポインタの移動中に連続的に発火するイベントですので
イベントリスナは、処理内容を座標の変化に応じた内容に特化して軽量化してください。

classList.add() と、table.addEventListener('mouseup',mup,false); は、 mousedown に移動しても動作するのではないでしょうか。
特に、後者の「イベントリスナのアタッチ」が連続的に行われる点は絶対に修正すべきです。
(際限のない多重登録が影響し、mouseup時に全リスナ実行(ラグ発生)するので、「置き去り」現象になっています)

マウス関連の mousedown, mousemove, mouseup を3つセットで用いるベントは
発火頻度も考慮して実装します。

  1. mousedown ... 初期化(2. 3. のイベントリスナをアタッチ)
  2. mousemove ... 座標取得と更新制御をできるだけ軽量な処理にする。
    (リフレッシュレートが高い閲覧環境では何回かに1度にすることも検討する)
  3. mouseup ... クリーンナップ(2. 3. のイベントリスナをデタッチ)

※ window 表示の PC用ブラウザなどは、押下したままウィンドウ枠の外でマウスボタンの開放なども発生しうるので focus / blur など、他のイベントを補助的に利用するなどして対策します。


追記)コメントを受けて
状態の変化に応じてイベントが発火していますので、実際の操作と紐付けて考えます。


ドラッグドロップの練習として

マウスイベントだけで実装する以外に、MDN:ドラッグ操作でもドラッグ&ドロップを学習できると思います
(ページ左のメニューにある「イベント」セクションで案内されている各イベントも要チェック)。

DragEvent は MouseEvent を継承しているので、ご質問のようなMouse操作と同様に座標取得ができます。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/05/12 05:00

    そのとおりです。
    「実際の操作が、表示などに影響するタイミングがあるので、最適なタイミングになるように調整しましょう」ということです。

    キャンセル

  • 2020/05/13 01:28

    コードを修正してみたのですが、
    置き去りされる現象は改善されませんでした。
    他の書き方を考えたほうがいいのでしょうか?
    もしお時間がありましたらご教授いただければ幸いです。

    キャンセル

  • 2020/05/13 13:01

    マウスイベントでの実装だと余分なイベント発火が起こるようですので、ドラッグ&ドロップ用にネイティブ実装されたイベントを使うほうが良いみたいですね。
    https://developer.mozilla.org/ja/docs/Web/API/Document/drag_event のサンプルコードでは「This div is draggable」を移動できますが、置き去り現象は起こらないようです。
    編集されたコードにおいて、イベントリスナ内で event.target のように event 情報を利用する場合はリスナの引数に event を指定しましょう。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る