@TOC
前情提要#
在前端工作過程中,避免不了要接觸各種技術,拖拽就是其中一個,大部分關於拖拽的基礎知識和 Demo 都在 MDN 中寫的很詳細的,這裡便不再贅述,給大家分享一個MDN 飛機票
應用場景#
每個人會接觸不同的場景和需求,但是底層都是基於這些事件及 API 的,那我就直接分享幾個有價值的點,可以節省大家的時間。
⭐拖拽改變元素位置#
這個功能要依據幾個事件
1、dragstart
2、dragover //dragover 必須要在此事件中取消瀏覽器的默認行為,否則 drop 事件不會生效
3、drop
// 第一步,記錄鼠標指針在元素上的偏移量
const rightItemDragStart = (event: any) => {
// 記錄初始鼠標偏移量
mouseOffsetMap.value = { x: event.offsetX, y: event.offsetY }
// 傳遞拖拽元素id
event.dataTransfer.setData('text/plain', event.target.id)
event.dataTransfer.setData('rightDrag', true)
}
// 第二步 設置阻止默認行為
const handleDragOver = (event: DragEvent) => {
// 阻止默認行為以允許放置
event.preventDefault()
}
// 第三步 計算元素位置賦值
const handleDrop = (event: any) => {
// eventPos 這個位置就是元素放置後的準確位置
eventPos = {
x: event.offsetX - mouseOffsetMap.value.x,
y: event.offsetY - mouseOffsetMap.value.y,
}
...
// 阻止默認行為(會作為某些元素的鏈接打開)
event.preventDefault()
}
⭐拖拽改變目標區域的樣式#
依據倆個事件,要綁定給目標 Dom
1、dragenter
2、dragleave
/**
* 處理拖拽進入事件
*
* @param event 拖拽事件對象
*/
const handleDragEnter = (event: any) => {
rightWrapRef.value?.classList.add('out-line')
}
/**
* 當拖拽元素離開目標區域時觸發
*
* @param event 拖拽事件對象
*/
const handleDragLeave = (event: DragEvent) => {
rightWrapRef.value?.classList.remove('out-line')
}
⭐dragleave 拖拽事件穿透子元素的優雅解決方案#
如果目標區域的 dom 結構比較複雜,比如我在工作中的場景這樣:
我想從右側往左側的列表裡拖拽,並且在進入左側區域的時候,為區域增加一個虛線框,鼠標離開的時候取消虛線框。但此時問題出現了,在我拖拽進入區域後,每一個子元素的區域都會致使我重複觸發 enter,leave, 導致虛線框閃爍,在一番調研後發現確實有這個問題,它不是事件的冒泡,也不是默認行為,而是改方法的特性導致的,所以大家只好另闢蹊徑,怎麼做的都有,但我認為最優雅的是下面這種。主要利用倆個方法
1、handleLeftDragEnter
2、handleLeftDragLeave
const draggingCounter = ref(0)
const handleLeftDragEnter = (event: any) => {
draggingCounter.value++
// 左側元素放入左側區域不處理
leftWrapRef.value?.classList.add('out-line')
}
const handleLeftDragLeave = (event: any) => {
draggingCounter.value--
if(draggingCounter.value === 0) {
leftWrapRef.value?.classList.remove('out-line')
}
}
這裡利用一個計數變量,大家看我打印這個一次拖拽的邏輯就很好理解了
倆次進入元素是因為進入最外部區域算一次,途徑子元素算一次,離開子元素也是同理,我們通過判斷數值為 0 來推導離開父元素的區域也是很合理的做法。大家如果還有什麼好的建議和觀點歡迎討論學習。
最後#
📚 vue 專欄
☃️ 個人簡介:一個喜愛技術的人。
🌞 勵志格言:腳踏實地,虛心學習。
❗如果文章還可以,記得用你可愛的小手點贊👍關注✅,我會在第一時間回、回訪,歡迎進一步交流。