探討把一個元素從它所在的div 拖動到另一個div內的實現方法


故事背景:

      接到一個新需求,要求用vue搞,主要是拖動實現布局,關鍵點有單個組件拖動,一行多列里面的組件拖動,  單個組件可以拖入一行多列里, 單個組件的拖動好實現,關鍵是把一個組件拖動到另一個類似於表格里面,而且有的情況下還需要限制拖動只能在水平方向,自己搜集資料, 實驗,終於搞出來了。

原理上主要分為兩類:

  1. HTML5自帶的拖放api,可用的庫有 : Vue.Draggable  
  2. 使用js 監聽鼠標的移動位置, 可用的庫有:  jquery ui
  3. 使用point-event: none(下面會詳細說明)

各自缺點

H5拖動的缺點:不能限制在水平 或垂直方向上拖動。

使用原生js缺點:大量的dom操作,代碼復雜(jquery ui 封裝的比較好了,可直接用)

但是問題來了,這次的需求是把基於jquery ui 的拖動 用 vue 重構,那么用vue.draggable吧, 但是需求里正好有一條是要限制在水平方向上拖動,尷尬,用不了。

尋找方案

最開始的嘗試:

<!DOCTYPE html>
<html>
<head>
    <title>vue結合原生js實現拖動</title>
<script src="https://cdn.bootcss.com/vue/2.4.2/vue.min.js"></script>
</head>
<body>
<div id="app">
<div class="ctn ctn1">
    <div class="sub sub1" v-for="(site, index) in list1">
         <div class="dragCtn fixed" :style="{ left: X+'px', top: Y+'px'}"
           @mousedown="mousedown(site, $event)"
           @mousemove.prevent='mousemove(site, $event)'
           @mouseup='mouseup(site, $event)'>
             拖動我
         </div>
   </div>
</div>

<div class="ctn ctn2">
    <div class="sub sub2" v-for="(site, index) in list2"
          @mouseenter='mouseenter(site, $event)'>
         <div class="dragCtn">
             {{ index }} : {{ site.name }}
         </div>
   </div>
</div>
   
</div>

<script>
new Vue({
  el: '#app',
  data: {
    list1: [{name:1, index:0}],
    list2: [{name:'a', index:0}, {name:'b', index:1}, {name:'c', index: 2}, {name:'d', index: 3}],
    vm:'',
    sb_bkx: 0,
    sb_bky: 0,
    is_moving: false,
    X: 0,
    Y: 0
  },
  methods: {
      mousedown: function (site, event) {
        var startx=event.x;
        var starty=event.y;
        this.sb_bkx=startx - event.target.offsetLeft;
        this.sb_bky=starty - event.target.offsetTop;
        this.is_moving = true;
      },
      mousemove: function (site, event) {
          var endx=event.x - this.sb_bkx;
          var endy=event.y - this.sb_bky;
          var _this = this
          if(this.is_moving){
            this.X = endx;
            this.Y = endy;
      }
      },
      mouseup: function (e) {
        this.is_moving = false;
      },
    mouseenter: function (){
      console.log('鼠標進入')
    }
  }
})
</script>

<style>
    .ctn{
        line-height: 50px;
        cursor: pointer;
        font-size: 20px;
        text-align: center;
        float: left;
    }
    .sub:hover{
        background: #e6dcdc;
        color: white;
        width: 100px;
    }
       .ctn1{
           border: 1px solid green;
           width: 100px;
       }
       .ctn2{
           border: 1px solid black;
           width: 100px;
           margin-left: 50px;
       }
       .fixed{
         width: 100px;
      position: fixed;
      background: red;
      left: 0;
      top: 0;
      cursor: move;
       }
</style>
</body>
</html>

 

就這樣實現了基本的拖動,但是在拖動的時候,就不能觸  mouseenter  事件了,而且鼠標必須拖動的很慢,移動快一點,拖動的div就跟不上了,這一點到現在還困惑,希望各位大俠指點。

然后在網上找了很多資料,類庫,但是不能完全符合我的需求,於是准備自己寫了;

       首先是給組件添加mousedown事件,然后mousemove的時候 監聽鼠標的位置,再賦值給組件,實現拖動,但是當組件拖動進入另一個元素的時候,無法監聽mouseenter, 后來想的辦法是給正在拖動的組件加上 point-event:none 屬性,就是消除原有的鼠標事件,就可以觸發其他組件的mouerenter事件了,point-event 屬性的具體用法 可以參考這里:

www.zhangxinxu.com/wordpress/2…

因為拖動是用原生js寫的,所以可以限制在水平方向拖動,再加上可以觸發mouseenter事件,就正好實現的我的需求。

偽代碼如下:

mousedown: function (event, site) {
        document.onmousemove=function (ev) {         
           // 移動的時候給元素增加 point-event:none 屬性           
           ...        
    }       
     document.onmouseup=function (ev) {
           // up的時候 要移除point-event屬性
           ...        
     }   
 }              

        但是后來上面要求,要兼容ie10,由於 point-event:none 是H5的屬性,於是我趕緊去看看兼容性, 可怕的事發生了,point-event 屬性只兼容到 ie11,完蛋!

         再想其他辦法吧,沒了思路,老版本的拖動是基於 jquery ui  的 ,於是去看了 jquery ui 的源碼,看看它的拖動是怎么實現的。

jquery ui 拖動實現原理

     不熟悉 jquery ui 的拖動方法的可以先看下  這里   

      看下面這段代碼:

 $(function() {
    $( "#draggable" ).draggable();
    $( "#droppable" ).droppable({
      drop: function( event, ui ) {
        $( this ).html( "Dropped!" );
      }
    });
  });

      之前的關鍵問題就是怎么判斷拖動的元素  $( "#draggable" )  在什么時候 進入了可以放置的區域      $( "#droppable" ) 的,看了源碼,它的實現方式 簡單來說就是拖動  $( "#draggable" )  的時候監聽 鼠標的位置, 同時獲取  $( "#droppable" )   的區域位置信息,只要鼠標進入該區域,就觸發。

      順着這個思路,做了實驗 可以滿足自己的需求 good !

      demo的實現效果如下:

代碼地址: https://github.com/YalongYan/vue-drag-layout

拖動布局的實現方式應該還有更好的,歡迎大家提出更好的實現方式。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM