Vue拖拽組件開(kāi)發(fā)實(shí)例詳解
為什么選擇Vue?
主要原因:對(duì)于前端開(kāi)發(fā)來(lái)說(shuō),兼容性是我們必須要考慮的問(wèn)題之一。我們的項(xiàng)目不需要兼容低版本瀏覽器。項(xiàng)目本身也是一個(gè)數(shù)據(jù)驅(qū)動(dòng)型的。加之,Vue本身具有以下主要特性:
•使用虛擬DOM;
•輕量級(jí)框架;
•高效的數(shù)據(jù)綁定;
•靈活的組件系統(tǒng);
•完整的開(kāi)發(fā)生態(tài)鏈。
這就是我們?yōu)槭裁催x擇Vue框架的一些原因。
為什么要封裝成一個(gè)Vue組件?
主要目的是可提高代碼的復(fù)用性和可維護(hù)性。
•復(fù)用性:組件化后,一些樣式和邏輯均通過(guò)配置參數(shù)的方式去差異化體現(xiàn),所以參數(shù)的可配置性提高了組件的復(fù)用率和靈活性。
•可維護(hù)性:組件化后,組件內(nèi)部的邏輯只對(duì)組件負(fù)責(zé),外部的邏輯只通過(guò)配置參數(shù)適配,所以提高了代碼的邏輯清晰度,可以快速定位代碼出現(xiàn)問(wèn)題的地方。
組件化搭建頁(yè)面圖示:

上圖可看出,在Vue中,所謂組件化搭建頁(yè)面,簡(jiǎn)單來(lái)說(shuō),頁(yè)面實(shí)際上是由一個(gè)個(gè)功能獨(dú)立的組件搭建而成。這些組件之間可以組合、嵌套,最終形成了我們的頁(yè)面。
組件構(gòu)成
下面是一個(gè)完成的組件構(gòu)成:
// 組件內(nèi)模板 // 組件內(nèi)邏輯代碼 <script type="text/javascript"></script> // 組件內(nèi)封裝的樣式 <style lang="scss" scoped></style>
開(kāi)發(fā)Vue移動(dòng)拖拽組件為例
拖拽原理
手指在移動(dòng)的過(guò)程中,實(shí)時(shí)改變?cè)氐奈恢眉磘op和left值,使元素隨著手指的移動(dòng)而移動(dòng)。
拖拽實(shí)現(xiàn)

•開(kāi)始拖動(dòng)時(shí):獲取到接觸點(diǎn)相對(duì)于整個(gè)視圖區(qū)的坐標(biāo)clientX,clientY;獲取元素距離視圖上側(cè)和左側(cè)的距離 initTop,initLeft;計(jì)算接觸點(diǎn)距離元素上側(cè)和左側(cè)的距離 elTop = clientY - initTop, elLeft = clientX - initLeft;
•拖動(dòng)過(guò)程中:通過(guò) currTop = clientY - elTop, currLeft = clientX - elLeft實(shí)時(shí)獲取元素距離視圖上側(cè)和左側(cè)的距離值,并賦值給元素,使元素跟著手指的移動(dòng)而動(dòng)起來(lái);
•拖動(dòng)結(jié)束,定位元素。
Vue中的實(shí)現(xiàn)
使用Vue,最大的不同之處是我們幾乎不去操作DOM,要充分利用Vue的數(shù)據(jù)驅(qū)動(dòng)來(lái)實(shí)現(xiàn)拖拽功能。本例中,我們只需在垂直方向上拖動(dòng)元素,所以只需考慮垂直方向的移動(dòng)即可。

上圖中,通過(guò)data中的dragList渲染拖拽區(qū)域列表,代碼如下:
template:
<div class="drag-title">拖拽可調(diào)整順序</div>
<ul class="drag-list">
<li class="drag-item">{{item.txt}}</li>
</ul>
script:
export default {
data() {
return {
dragList:null
}
},
created() {
this.dragList = [
{
isDrag: false,
txt: '列表1',
isShow: false
}
...
]
},
}
假設(shè)我們將元素從位置1拖至位置3,本質(zhì)上是數(shù)組的順序發(fā)生了改變。這就有必要提一下Vue的最大特性:數(shù)據(jù)驅(qū)動(dòng)。
所謂的數(shù)據(jù)驅(qū)動(dòng)就是當(dāng)數(shù)據(jù)發(fā)生變化時(shí),通過(guò)修改數(shù)據(jù)狀態(tài),使用戶(hù)界面發(fā)生相應(yīng)的改變,開(kāi)發(fā)者不需要手動(dòng)的去修改DOM。
Vue的數(shù)據(jù)驅(qū)動(dòng)是通過(guò)MVVM這種框架來(lái)實(shí)現(xiàn)的,MVVM框架主要包含3個(gè)部分:Model、View、Viewmodel。
– Model:數(shù)據(jù)部分;
– View:視圖部分;
– Viewmodel:連接視圖與數(shù)據(jù)的中間件。
順著這個(gè)思路走下去,我們知道:
– oldIndex:元素在數(shù)組中的初始索引index;
– elHeight:?jiǎn)蝹€(gè)元素塊的高;
– currTop = clientY - elTop:元素在拖動(dòng)過(guò)程中距離可視區(qū)上側(cè)距離;
– currTop - initTop > 0:得知元素是向上拖拽;
– currTop - initTop < 0:得知元素是向下拖拽。
我們以向下拖拽來(lái)說(shuō):
– 首先,我們要在拖拽結(jié)束事件touchend中判斷元素從拖動(dòng)開(kāi)始到拖動(dòng)結(jié)束時(shí)拖動(dòng)的距離。若小于某個(gè)設(shè)定的值,則什么也不做;
– 然后,在touchmove事件中判斷,若(currTop - initTop) % elHeight>= elHeight/2成立,即當(dāng)元素拖至另一個(gè)元素塊等于或超過(guò)1/2的位置時(shí),即可將元素插入到最新的位置為newIndex = (currTop - initTop) / elHeight + oldIndex。
– 最后,若手指離開(kāi)元素,那么我們?cè)趖ouchend事件中,通過(guò)this.dragList.splice(oldIndex, 1),this.dragList.splice(newIndex, 0, item)重新調(diào)整數(shù)組順序。頁(yè)面會(huì)根據(jù)最新的dragList渲染列表。
寫(xiě)到這里,我們儼然已經(jīng)用Vue實(shí)現(xiàn)了移動(dòng)端的拖拽功能。但是拖拽體驗(yàn)并不好,接下來(lái),我們對(duì)它進(jìn)行優(yōu)化。
優(yōu)化點(diǎn):我們希望,在元素即將可能落到的位置,提前留出一個(gè)可以放得下元素的區(qū)域,讓用戶(hù)更好的感知拖拽的靈活性。
方案:(方案已被驗(yàn)證是可行的)將li的結(jié)構(gòu)做一下修改,代碼如下:
<ul>
<li class="drag-item">
<div class="leave-block"></div>
// 向上拖拽時(shí)留空
<div class="">{{item.txt}}</div>
<div class="leave-block"></div>
// 向下拖拽時(shí)留空</li>
</ul>
•拖拽開(kāi)始:將元素的定位方式由static設(shè)置為absolute,z-index設(shè)置為一個(gè)較大的值,防止元素二次拖拽無(wú)效;
•拖拽過(guò)程中:將元素即將落入新位置的那個(gè)li下div的item.isShow設(shè)置為true,其他li下div的item.isShow均設(shè)置為false;
•拖拽結(jié)束:將所有l(wèi)i下div的item.isShow 均設(shè)置為false,將元素定位方式由absolute設(shè)置為static。
貼一段偽代碼:
touchStart(e){
// 獲取元素距離視口頂部的初始距離
initTop = e.currentTarget.offsetTop;
// 開(kāi)始拖動(dòng)時(shí),獲取鼠標(biāo)距離視口頂部的距離
initClientY = e.touches[0].clientY;
// 計(jì)算出接觸點(diǎn)距離元素頂部的距離
elTop = e.touches[0].clientY - initTop;
},
touchMove(index, item, e){
// 將拖拽結(jié)束時(shí),給元素設(shè)置的static定位方式移除,防止元素二次拖拽無(wú)效
e.target.classList.remove('static');
// 給拖拽的元素設(shè)置絕對(duì)定位方式
e.target.classList.add('ab');
// 獲取元素在拖拽過(guò)程中距離視口頂部距離
currTop = e.touches[0].clientY - elTop;
// 元素在拖拽過(guò)程中距離視口頂部距離賦給元素
e.target.style.top = currTop ;
// 獲取元素初始位置
oldIndex = index;
// 獲取拖拽元素
currItem = item;
// 若元素已經(jīng)拖至區(qū)域外
if(e.touches[0].clientY > (this.dragList.length) * elHeight){
// 將元素距離上側(cè)的距離設(shè)置為拖動(dòng)區(qū)視圖的高
currTop = (this.dragList.length) * elHeight;
return;
}
// 向下拖拽
if(currTop > initTop ){
// 若拖拽到大于等于元素的一半時(shí),即可將元素插入到最新的位置
if((currTop - initTop) % elHeight>= elHeight / 2){
// 計(jì)算出元素拖到的最新位置
newIndex = Math.round((currTop - initTop) / elHeight) + index;
// 確保新元素的索引不能大于等于列表的長(zhǎng)度
if(newIndex < this.dragList.length){
// 將所有列表留空處隱藏
for(var i = 0;i< this.dragList.length;i++){
this.dragList[i].isShow = false;
}
// 將元素即將拖到的新位置的留空展示
this.dragList[newIndex].isShow = true;
}
else {
return;
}
}
}
// 向上拖拽,原理同上
if(currTop < initTop){ ... } }, touchEnd(e){ // 若拖動(dòng)距離大于某個(gè)設(shè)定的值,則按照上述,執(zhí)行相關(guān)代碼 if(Math.abs(e.changedTouches[0].clientY - initClientY ) > customVal){
this.dragList.splice(oldIndex, 1);
this.dragList.splice(newIndex, 0, currItem);
for(var i = 0;i< this.dragList.length;i++){
this.dragList[i].isShow = false;
this.dragList[i].isShowUp = false;
}
}
e.target.classList.remove('ab');
e.target.classList.add('static');
}
優(yōu)化后,如下圖所示:

以上便是用Vue實(shí)現(xiàn)移動(dòng)端拖拽組件的過(guò)程。我們知道,有些項(xiàng)目是需要在PC端用Vue實(shí)現(xiàn)此功能。這里簡(jiǎn)單提一下PC與移動(dòng)端的區(qū)別如下:
•PC端可以使用的事件組有兩種:第一種:H5新特性draggable,dragstart,drag,dragend;第二種:mousedown,mousemove,mouseup;
•PC端獲取鼠標(biāo)坐標(biāo)是通過(guò)e.clientX,clientY,區(qū)別于移動(dòng)端的e.touches[0].clientX,e.touches[0].clientY。
小結(jié)
本文從Vue拖拽組件開(kāi)發(fā)為例,剖析Vue組件的結(jié)構(gòu)、開(kāi)發(fā)思路、Vue的數(shù)據(jù)驅(qū)動(dòng)等,對(duì)Vue組件化的原理,進(jìn)行了更深入的理解。 并將Vue實(shí)現(xiàn)拖拽的方案提供給大家學(xué)習(xí)研究。
P.S. 牢記一點(diǎn),切勿在Vue中過(guò)多得操作DOM,要能深入理解Vue數(shù)據(jù)驅(qū)動(dòng)的核心思想。
- Vue 可拖拽組件Vue Smooth DnD的使用詳解
- vue拖拽組件 vuedraggable API options實(shí)現(xiàn)盒子之間相互拖拽排序
- Vue拖拽組件列表實(shí)現(xiàn)動(dòng)態(tài)頁(yè)面配置功能
- vue拖拽組件使用方法詳解
- vue 實(shí)現(xiàn)拖拽動(dòng)態(tài)生成組件的需求
- vue使用Split封裝通用拖拽滑動(dòng)分隔面板組件
- vue開(kāi)發(fā)拖拽進(jìn)度條滑動(dòng)組件
- vue draggable resizable 實(shí)現(xiàn)可拖拽縮放的組件功能
- 利用Vue-draggable組件實(shí)現(xiàn)Vue項(xiàng)目中表格內(nèi)容的拖拽排序
- Vue組件Draggable實(shí)現(xiàn)拖拽功能
- Vue實(shí)現(xiàn)可拖拽組件的方法
相關(guān)文章
在vue中使用防抖和節(jié)流,防止重復(fù)點(diǎn)擊或重復(fù)上拉加載實(shí)例
今天小編就為大家分享一篇在vue中使用防抖和節(jié)流,防止重復(fù)點(diǎn)擊或重復(fù)上拉加載實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11
Vue3?TypeScript?實(shí)現(xiàn)useRequest詳情
本文介紹了Vue3?TypeScript實(shí)現(xiàn)useRequest詳情,useRequest可能是目前社區(qū)中最強(qiáng)大,最接地氣的請(qǐng)求類(lèi)?Hooks了。可以覆蓋99%的網(wǎng)絡(luò)請(qǐng)求場(chǎng)景,無(wú)論是讀還是寫(xiě),無(wú)論是普通請(qǐng)求還是分頁(yè)請(qǐng)求,無(wú)論是緩存還是防抖節(jié)流,通通都能支持,關(guān)于其介紹需要的小伙伴可以參考一下2022-05-05
Vue項(xiàng)目每次發(fā)版后要清理瀏覽器緩存問(wèn)題解決辦法
最近項(xiàng)目更新頻繁,每次一更新客戶(hù)都說(shuō)還跟之前的一樣,一查原因是因?yàn)榭蛻?hù)沒(méi)有清空瀏覽器的緩存,所以為了方便客戶(hù)看到最新版本,開(kāi)始調(diào)研再發(fā)布新版本后自動(dòng)清理緩存,這篇文章主要給大家介紹了關(guān)于Vue項(xiàng)目每次發(fā)版后要清理瀏覽器緩存問(wèn)題的解決辦法,需要的朋友可以參考下2024-02-02
在vue中使用vue-echarts-v3的實(shí)例代碼
這篇文章主要介紹了在vue中使用vue-echarts-v3的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09
Element-plus側(cè)邊欄踩坑的問(wèn)題解決
本文主要介紹了Element-plus側(cè)邊欄踩坑的問(wèn)題解決,主要解決了el-menu直接嵌套el-menu-item菜單,折疊時(shí)不會(huì)出現(xiàn)文字顯示和小箭頭無(wú)法隱藏的問(wèn)題,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08
Vue?element實(shí)現(xiàn)權(quán)限管理業(yè)務(wù)流程詳解
目前本人再使用vue-element-admin項(xiàng)目時(shí)都是通過(guò)直接刪除一些用不上的路由來(lái)進(jìn)行側(cè)邊欄的清除,但是其實(shí)有一個(gè)更加好的辦法來(lái)對(duì)項(xiàng)目的側(cè)邊欄顯示的內(nèi)用進(jìn)行管理,就是權(quán)限管理,其實(shí)也不知道這個(gè)方法好不好,原理上來(lái)說(shuō)時(shí)跟直接刪除該路由的方式時(shí)一樣的2022-08-08
VUE中如何優(yōu)雅實(shí)現(xiàn)爺孫組件的數(shù)據(jù)通信
所謂祖孫組件,也就是3層嵌套的組件,下面這篇文章主要給大家介紹了關(guān)于VUE中如何優(yōu)雅實(shí)現(xiàn)爺孫組件的數(shù)據(jù)通信的相關(guān)資料,需要的朋友可以參考下2022-04-04

