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

閆亞龍發表於2018-11-29

故事背景:

 
 
 
接到一個新需求,要求用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的實現效果如下:

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

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

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

來源:https://juejin.im/post/5bf4cef7e51d4553f4271e73

相關文章