Vue拖拽組件開發(fā)實例詳解
為什么選擇Vue?
主要原因:對于前端開發(fā)來說,兼容性是我們必須要考慮的問題之一。我們的項目不需要兼容低版本瀏覽器。項目本身也是一個數據驅動型的。加之,Vue本身具有以下主要特性:
•使用虛擬DOM;
•輕量級框架;
•高效的數據綁定;
•靈活的組件系統(tǒng);
•完整的開發(fā)生態(tài)鏈。
這就是我們?yōu)槭裁催x擇Vue框架的一些原因。
為什么要封裝成一個Vue組件?
主要目的是可提高代碼的復用性和可維護性。
•復用性:組件化后,一些樣式和邏輯均通過配置參數的方式去差異化體現,所以參數的可配置性提高了組件的復用率和靈活性。
•可維護性:組件化后,組件內部的邏輯只對組件負責,外部的邏輯只通過配置參數適配,所以提高了代碼的邏輯清晰度,可以快速定位代碼出現問題的地方。
組件化搭建頁面圖示:
上圖可看出,在Vue中,所謂組件化搭建頁面,簡單來說,頁面實際上是由一個個功能獨立的組件搭建而成。這些組件之間可以組合、嵌套,最終形成了我們的頁面。
組件構成
下面是一個完成的組件構成:
// 組件內模板 // 組件內邏輯代碼 <script type="text/javascript"></script> // 組件內封裝的樣式 <style lang="scss" scoped></style>
開發(fā)Vue移動拖拽組件為例
拖拽原理
手指在移動的過程中,實時改變元素的位置即top和left值,使元素隨著手指的移動而移動。
拖拽實現
•開始拖動時:獲取到接觸點相對于整個視圖區(qū)的坐標clientX,clientY;獲取元素距離視圖上側和左側的距離 initTop,initLeft;計算接觸點距離元素上側和左側的距離 elTop = clientY - initTop, elLeft = clientX - initLeft;
•拖動過程中:通過 currTop = clientY - elTop, currLeft = clientX - elLeft實時獲取元素距離視圖上側和左側的距離值,并賦值給元素,使元素跟著手指的移動而動起來;
•拖動結束,定位元素。
Vue中的實現
使用Vue,最大的不同之處是我們幾乎不去操作DOM,要充分利用Vue的數據驅動來實現拖拽功能。本例中,我們只需在垂直方向上拖動元素,所以只需考慮垂直方向的移動即可。
上圖中,通過data中的dragList渲染拖拽區(qū)域列表,代碼如下:
template: <div class="drag-title">拖拽可調整順序</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 } ... ] }, }
假設我們將元素從位置1拖至位置3,本質上是數組的順序發(fā)生了改變。這就有必要提一下Vue的最大特性:數據驅動。
所謂的數據驅動就是當數據發(fā)生變化時,通過修改數據狀態(tài),使用戶界面發(fā)生相應的改變,開發(fā)者不需要手動的去修改DOM。
Vue的數據驅動是通過MVVM這種框架來實現的,MVVM框架主要包含3個部分:Model、View、Viewmodel。
– Model:數據部分;
– View:視圖部分;
– Viewmodel:連接視圖與數據的中間件。
順著這個思路走下去,我們知道:
– oldIndex:元素在數組中的初始索引index;
– elHeight:單個元素塊的高;
– currTop = clientY - elTop:元素在拖動過程中距離可視區(qū)上側距離;
– currTop - initTop > 0:得知元素是向上拖拽;
– currTop - initTop < 0:得知元素是向下拖拽。
我們以向下拖拽來說:
– 首先,我們要在拖拽結束事件touchend中判斷元素從拖動開始到拖動結束時拖動的距離。若小于某個設定的值,則什么也不做;
– 然后,在touchmove事件中判斷,若(currTop - initTop) % elHeight>= elHeight/2成立,即當元素拖至另一個元素塊等于或超過1/2的位置時,即可將元素插入到最新的位置為newIndex = (currTop - initTop) / elHeight + oldIndex。
– 最后,若手指離開元素,那么我們在touchend事件中,通過this.dragList.splice(oldIndex, 1),this.dragList.splice(newIndex, 0, item)重新調整數組順序。頁面會根據最新的dragList渲染列表。
寫到這里,我們儼然已經用Vue實現了移動端的拖拽功能。但是拖拽體驗并不好,接下來,我們對它進行優(yōu)化。
優(yōu)化點:我們希望,在元素即將可能落到的位置,提前留出一個可以放得下元素的區(qū)域,讓用戶更好的感知拖拽的靈活性。
方案:(方案已被驗證是可行的)將li的結構做一下修改,代碼如下:
<ul> <li class="drag-item"> <div class="leave-block"></div> // 向上拖拽時留空 <div class="">{{item.txt}}</div> <div class="leave-block"></div> // 向下拖拽時留空</li> </ul>
•拖拽開始:將元素的定位方式由static設置為absolute,z-index設置為一個較大的值,防止元素二次拖拽無效;
•拖拽過程中:將元素即將落入新位置的那個li下div的item.isShow設置為true,其他li下div的item.isShow均設置為false;
•拖拽結束:將所有l(wèi)i下div的item.isShow 均設置為false,將元素定位方式由absolute設置為static。
貼一段偽代碼:
touchStart(e){ // 獲取元素距離視口頂部的初始距離 initTop = e.currentTarget.offsetTop; // 開始拖動時,獲取鼠標距離視口頂部的距離 initClientY = e.touches[0].clientY; // 計算出接觸點距離元素頂部的距離 elTop = e.touches[0].clientY - initTop; }, touchMove(index, item, e){ // 將拖拽結束時,給元素設置的static定位方式移除,防止元素二次拖拽無效 e.target.classList.remove('static'); // 給拖拽的元素設置絕對定位方式 e.target.classList.add('ab'); // 獲取元素在拖拽過程中距離視口頂部距離 currTop = e.touches[0].clientY - elTop; // 元素在拖拽過程中距離視口頂部距離賦給元素 e.target.style.top = currTop ; // 獲取元素初始位置 oldIndex = index; // 獲取拖拽元素 currItem = item; // 若元素已經拖至區(qū)域外 if(e.touches[0].clientY > (this.dragList.length) * elHeight){ // 將元素距離上側的距離設置為拖動區(qū)視圖的高 currTop = (this.dragList.length) * elHeight; return; } // 向下拖拽 if(currTop > initTop ){ // 若拖拽到大于等于元素的一半時,即可將元素插入到最新的位置 if((currTop - initTop) % elHeight>= elHeight / 2){ // 計算出元素拖到的最新位置 newIndex = Math.round((currTop - initTop) / elHeight) + index; // 確保新元素的索引不能大于等于列表的長度 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){ // 若拖動距離大于某個設定的值,則按照上述,執(zhí)行相關代碼 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實現移動端拖拽組件的過程。我們知道,有些項目是需要在PC端用Vue實現此功能。這里簡單提一下PC與移動端的區(qū)別如下:
•PC端可以使用的事件組有兩種:第一種:H5新特性draggable,dragstart,drag,dragend;第二種:mousedown,mousemove,mouseup;
•PC端獲取鼠標坐標是通過e.clientX,clientY,區(qū)別于移動端的e.touches[0].clientX,e.touches[0].clientY。
小結
本文從Vue拖拽組件開發(fā)為例,剖析Vue組件的結構、開發(fā)思路、Vue的數據驅動等,對Vue組件化的原理,進行了更深入的理解。 并將Vue實現拖拽的方案提供給大家學習研究。
P.S. 牢記一點,切勿在Vue中過多得操作DOM,要能深入理解Vue數據驅動的核心思想。
相關文章
vue 實現setInterval 創(chuàng)建和銷毀實例
這篇文章主要介紹了vue 實現setInterval 創(chuàng)建和銷毀實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-07-07Vue中EpicEditor和vue-quill-editor的使用詳解
EpicEditor和Vue-quill-editor都是基于Quill.js的富文本編輯器,并且都提供了許多強大的功能,下面我們就來介紹一下二者的具體使用,感興趣的小伙伴可以了解一下2023-11-11Vuejs第一篇之入門教程詳解(單向綁定、雙向綁定、列表渲染、響應函數)
組件(Component)是 Vue.js 最強大的功能之一。接下來給大家介紹vuejs單向綁定、雙向綁定、列表渲染、響應函數的相關知識,非常不錯,感興趣的朋友一起看看吧2016-09-09vue3中通過遍歷傳入組件名稱動態(tài)創(chuàng)建多個component 組件
這篇文章主要介紹了vue3中通過遍歷傳入組件名稱動態(tài)創(chuàng)建多個component 組件,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03