最近在用vue3重構後臺的一個功能。一個彈窗元件,彈出一個表單。然後點選提交。
早上運維突然跑過來問我,為啥彈窗擋住了下邊的表格的資料,我新增的時候,都沒法對照表格來看了。你必須給我解決一下。
我參考了一下幾大Vue的ui元件庫。發現element iview antv。好像都沒這個功能。為啥運維需要這個功能??
但是沒辦法,只能整一個就是了。
做之前本來想直接做到dialog這個元件中。但是又擔心後面其他的元件會用到。於是決定把拖拽功能做到指令中。
整個功能點如圖。滑鼠在拖拽區域拖動,整個對話方塊在瀏覽器可視範圍內移動。
Drag指令主要實現思路
-
在指令掛載的時候,監聽當前html節點的滑鼠點選事件
-
然後在點選當前html節點的時候,判斷是否點選在drag-target這個class所在的子節點上。如果是,那麼觸發document滑鼠移動事件。然後計算出滑鼠移動距離,對應修改彈出框的left值和top值。並記錄下當前按下的位置x和y
let x = e.clientX; let y = e.clientY;
-
如何限制拖動的節點只能在螢幕內移動,不能移動出螢幕呢?
-
限制left不能小於0,在定位為position: fixed 的時候,left如果小於0,那麼html節點的左側肯定已經在顯示區域外了。那麼我們不能讓left小於0
let bodyW = document.body.clientWidth; let bodyH = document.body.clientHeight; let left = elLeft - (x - move.clientX); if (left < 0) { left = 0; }
-
限制left不能大於可視區域的寬度減去當前html節點的寬度,如果left大於這個寬度,那麼當前html節點肯定右側已經處於顯示區域的右側外邊了
if (left > bodyW - el.offsetWidth) { left = bodyW - el.offsetWidth; }
-
上下拖拽位置限制和左右拖拽限制思路是一樣,只要保證top的值大於0且小於螢幕可視範圍的高度減去當前html節點的高度,那麼拖動就無法拖出螢幕了。
let top = elTop - (y - move.clientY); if (top < 0) { top = 0; } if (top > bodyH - el.offsetHeight) { top = bodyH - el.offsetHeight }
drag指令完整程式碼
import { App } from 'vue'; export default { install(Vue: App<Element>) { Vue.directive('drag', { mounted(el: HTMLElement, bind) { el.onmousedown = (e) => { let elLeft = el.offsetLeft; let elTop = el.offsetTop; let dom = <HTMLElement>e.target; if (dom.classList.contains('drag-target')) { let x = e.clientX; let y = e.clientY; document.onmousemove = (move: MouseEvent) => { let bodyW = document.body.clientWidth; let bodyH = document.body.clientHeight; let left = elLeft - (x - move.clientX); if (left < 0) { left = 0; } if (left > bodyW - el.offsetWidth) { left = bodyW - el.offsetWidth; } el.style.left = left + 'px' let top = elTop - (y - move.clientY); if (top < 0) { top = 0; } if (top > bodyH - el.offsetHeight) { top = bodyH - el.offsetHeight } el.style.top = top + 'px' document.onmouseup = (up: MouseEvent) => { document.onmousemove = null; document.onmouseup = null } if (window.getSelection()) { window.getSelection()?.removeAllRanges() } } } } }, unmounted(el, bind) { el.onmousedown = null; } }) } }
使用
import DragDirective from './DragDirective' createApp(App).use(DragDirective).mount('#app')
註冊指令到Vue App上,然後在需要移動的html節點上加上 v-drag ,並在觸發拖拽的子節點的class上,加上drag-target
<div class="f-dialog" v-if="show" v-drag ref="dialog" :style="{ left: data.left + 'px' }" > <div class="f-dialog-header drag-target"> <slot name="header"> <span>{{ title }}</span> </slot> <f-icon icon="icon-close" class="f-modal-close" @click="close(false)" ></f-icon> </div> <div class="f-dialog-content"> <slot></slot> </div> <div class="f-dialog-footer"> <slot name="footer"> <button @click="close(true)">確定</button> <button @click="close(false)">取消</button> </slot> </div> </div>
效果圖
-
更多幹貨,以及本文的示例程式碼, 歡迎關注我的公眾號: 青城同學 回覆 拖拽程式碼 獲取下載地址
當然也可以掃碼