vue3 拖拽hooks(可兼容移動(dòng)端)和自定義指令拖拽的實(shí)現(xiàn)代碼
具體目標(biāo)
1、拖拽功能完好使用
2、不入侵業(yè)務(wù)
3、邊界值比如不能拖拽出瀏覽器外
整體架構(gòu)流程
三步走
鼠標(biāo)按下:將鼠標(biāo)按下時(shí)的位置記錄并與被拖拽元素的左上角位置進(jìn)行比較,以計(jì)算出鼠標(biāo)按下點(diǎn)相對(duì)于被拖拽元素的偏移,
鼠標(biāo)移動(dòng):根據(jù)鼠標(biāo)指針的移動(dòng)來(lái)更新被拖拽元素的位置,確保元素跟隨鼠標(biāo)的移動(dòng)。
鼠標(biāo)抬起:移除事件
具體代碼實(shí)現(xiàn)
方案一 hooks寫(xiě)法
// 創(chuàng)建useDraggable.ts export const useDraggable = (): Ref<HTMLDivElement | null> => { // 聲明一個(gè) ref,用于存儲(chǔ) div 元素的引用 const divRef = ref<HTMLDivElement | null>(null) // 聲明一些變量,用于存儲(chǔ)鼠標(biāo)或觸摸位置以及拖拽狀態(tài) let offsetX = 0 // 鼠標(biāo)點(diǎn)擊或觸摸點(diǎn)距離 div 左側(cè)的偏移 let offsetY = 0 // 鼠標(biāo)點(diǎn)擊或觸摸點(diǎn)距離 div 頂部的偏移 let isDragging = false // 是否正在拖拽中 // 禁用頁(yè)面滾動(dòng)的函數(shù) const disablePageScroll = () => { document.body.style.overflow = 'hidden' } // 啟用頁(yè)面滾動(dòng)的函數(shù) const enablePageScroll = () => { document.body.style.overflow = 'auto' } // 開(kāi)始拖拽,禁用頁(yè)面滾動(dòng) const startDragging = () => { isDragging = true disablePageScroll() } // 停止拖拽,啟用頁(yè)面滾動(dòng),并稍后重新啟用點(diǎn)擊事件 const stopDragging = () => { isDragging = false enablePageScroll() setTimeout(() => { if (divRef.value) { divRef.value.style.pointerEvents = 'auto' } }, 100) } // 處理鼠標(biāo)移動(dòng)或觸摸移動(dòng)事件 const handleMouseMove = (event: MouseEvent | TouchEvent) => { requestAnimationFrame(() => { if (isDragging && divRef.value) { const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY const x = clientX - offsetX const y = clientY - offsetY // 阻止事件傳播,避免干擾正常滾動(dòng) event.stopPropagation() event.preventDefault() // 獲取瀏覽器窗口的最大可視區(qū)域?qū)挾群透叨? const maxX = window.innerWidth - (divRef.value.clientWidth || 0) const maxY = window.innerHeight - (divRef.value.clientHeight || 0) // 設(shè)置 div 的位置,確保不超出窗口范圍 divRef.value.style.left = `${Math.min(maxX, Math.max(0, x))}px` divRef.value.style.top = `${Math.min(maxY, Math.max(0, y))}px` // 禁用 div 上的點(diǎn)擊事件,以避免拖拽時(shí)觸發(fā)點(diǎn)擊事件 divRef.value.style.pointerEvents = 'none' } }) } // 處理鼠標(biāo)松開(kāi)或觸摸結(jié)束事件 const handleMouseUp = () => { // 停止拖拽,恢復(fù)點(diǎn)擊事件 stopDragging() // 移除鼠標(biāo)移動(dòng)事件和觸摸移動(dòng)事件的監(jiān)聽(tīng)器 document.removeEventListener('touchmove', handleMouseMove) document.removeEventListener('mousemove', handleMouseMove) } // 處理鼠標(biāo)按下或觸摸開(kāi)始事件 const handleMouseDown = (event: MouseEvent | TouchEvent) => { if (!divRef.value) return // 獲取鼠標(biāo)點(diǎn)擊或觸摸點(diǎn)相對(duì)于 div 左側(cè)和頂部的偏移 offsetX = 'touches' in event ? event.touches[0].clientX - divRef.value.offsetLeft : event.clientX - divRef.value.offsetLeft offsetY = 'touches' in event ? event.touches[0].clientY - divRef.value.offsetTop : event.clientY - divRef.value.offsetTop // 開(kāi)始拖拽,添加鼠標(biāo)移動(dòng)和觸摸移動(dòng)事件監(jiān)聽(tīng)器 startDragging() document.addEventListener('mousemove', handleMouseMove, { passive: false, // 阻止默認(rèn)滾動(dòng)行為 }) document.addEventListener('touchmove', handleMouseMove, { passive: false, // 阻止默認(rèn)滾動(dòng)行為 }) // 添加鼠標(biāo)松開(kāi)和觸摸結(jié)束事件監(jiān)聽(tīng)器 document.addEventListener('mouseup', handleMouseUp) document.addEventListener('touchend', handleMouseUp) } // 在組件掛載時(shí),添加鼠標(biāo)按下和觸摸開(kāi)始事件監(jiān)聽(tīng)器 onMounted(() => { if (divRef.value) { divRef.value.addEventListener('mousedown', handleMouseDown) divRef.value.addEventListener('touchstart', handleMouseDown) } }) // 在組件卸載時(shí),移除事件監(jiān)聽(tīng)器 onUnmounted(() => { if (divRef.value) { divRef.value.removeEventListener('mousedown', handleMouseDown) divRef.value.removeEventListener('touchstart', handleMouseDown) } document.removeEventListener('mouseup', handleMouseUp) document.removeEventListener('touchend', handleMouseUp) }) // 返回 div 元素的引用,可以在組件中使用該引用來(lái)創(chuàng)建可拖拽的元素 return divRef }
hooks的使用方法如下
<template> <div ref="draggableDiv" class="it-layout-aside" >古德古德~</div> </template> <script setup lang="tsx"> import { useDraggable } from '~/hooks/useDraggable' const draggableDiv = useDraggable() </script> <style lang="stylus" scoped> .it-layout-aside flexCenter() position fixed bottom 100px right 10px width 60px height 60px border-radius 50% background rgba(0,0,0,.5) opacity 0.8 color #fff font-size 40px cursor move &:hover opacity 1 </style>
方案二 自定義指令寫(xiě)法
const vDraggable = { mounted(el: HTMLElement) { let offsetX = 0 let offsetY = 0 let isDragging = false el.addEventListener('mousedown', event => { isDragging = false offsetX = event.clientX - el.offsetLeft offsetY = event.clientY - el.offsetTop const handleMouseMove = (e: MouseEvent) => { if (!isDragging && (Math.abs(event.clientX - offsetX) > 5 || Math.abs(event.clientY - offsetY) > 5)) { isDragging = true } if (isDragging) { const x = e.clientX - offsetX const y = e.clientY - offsetY el.style.left = `${Math.min(window.innerWidth - el.clientWidth, Math.max(0, x))}px` el.style.top = `${Math.min(window.innerHeight - el.clientHeight, Math.max(0, y))}px` el.style.pointerEvents = 'none' } } const handleMouseUp = () => { // 設(shè)置拖動(dòng)狀態(tài)為false isDragging = false setTimeout(() => { el.style.pointerEvents = 'auto' }, 100) // 移除鼠標(biāo)移動(dòng)和松開(kāi)事件 document.removeEventListener('mousemove', handleMouseMove) // eslint-disable-next-line @typescript-eslint/no-unused-vars document.removeEventListener('mouseup', handleMouseUp) } }) }, }
自定義指令的方法使用如下
<template> <div v-draggable class="it-layout-aside" >你潮嘛~</div> </template> <style lang="stylus" scoped> .it-layout-aside flexCenter() position fixed bottom 100px right 10px width 60px height 60px border-radius 50% background rgba(0,0,0,.5) opacity 0.8 color #fff font-size 40px cursor move &:hover opacity 1 </style>
到此這篇關(guān)于vue3 拖拽hooks(可兼容移動(dòng)端)和自定義指令拖拽的文章就介紹到這了,更多相關(guān)vue3 拖拽hooks內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue實(shí)現(xiàn)豎屏滾動(dòng)公告效果
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)豎屏滾動(dòng)公告效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04SpringBoot+Vue前后端分離,使用SpringSecurity完美處理權(quán)限問(wèn)題的解決方法
這篇文章主要介紹了SpringBoot+Vue前后端分離,使用SpringSecurity完美處理權(quán)限問(wèn)題,需要的朋友可以參考下2018-01-01vue3使用高德地圖,自定義點(diǎn)標(biāo)記、默認(rèn)點(diǎn)聚合樣式、點(diǎn)擊點(diǎn)標(biāo)記獲取信息
這篇文章主要介紹了vue3使用高德地圖,自定義點(diǎn)標(biāo)記、默認(rèn)點(diǎn)聚合樣式、點(diǎn)擊點(diǎn)標(biāo)記獲取信息的相關(guān)知識(shí),本文結(jié)合示例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-01-01Vue項(xiàng)目頁(yè)面跳轉(zhuǎn)時(shí)瀏覽器窗口上方顯示進(jìn)度條功能
這篇文章主要介紹了Vue項(xiàng)目頁(yè)面跳轉(zhuǎn)時(shí)瀏覽器窗口上方顯示進(jìn)度條功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03vue2.0 獲取從http接口中獲取數(shù)據(jù),組件開(kāi)發(fā),路由配置方式
今天小編就為大家分享一篇vue2.0 獲取從http接口中獲取數(shù)據(jù),組件開(kāi)發(fā),路由配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11