最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue3 拖拽hooks(可兼容移動(dòng)端)和自定義指令拖拽的實(shí)現(xiàn)代碼

 更新時(shí)間:2024年01月25日 15:38:24   作者:暮冬十七  
這篇文章主要介紹了vue3 拖拽hooks(可兼容移動(dòng)端)和自定義指令拖拽的實(shí)現(xiàn)代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考借鑒價(jià)值,需要的朋友參考下吧

具體目標(biāo)

1、拖拽功能完好使用
2、不入侵業(yè)務(wù)
3、邊界值比如不能拖拽出瀏覽器外

整體架構(gòu)流程

三步走
鼠標(biāo)按下:將鼠標(biāo)按下時(shí)的位置記錄并與被拖拽元素的左上角位置進(jìn)行比較,以計(jì)算出鼠標(biāo)按下點(diǎn)相對于被拖拽元素的偏移,
鼠標(biāo)移動(dòng):根據(jù)鼠標(biāo)指針的移動(dòng)來更新被拖拽元素的位置,確保元素跟隨鼠標(biāo)的移動(dòng)。
鼠標(biāo)抬起:移除事件

具體代碼實(shí)現(xiàn)

方案一 hooks寫法

// 創(chuàng)建useDraggable.ts
export const useDraggable = (): Ref<HTMLDivElement | null> => {
  // 聲明一個(gè) ref,用于存儲 div 元素的引用
  const divRef = ref<HTMLDivElement | null>(null)
  // 聲明一些變量,用于存儲鼠標(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 // 是否正在拖拽中
  // 禁用頁面滾動(dòng)的函數(shù)
  const disablePageScroll = () => {
    document.body.style.overflow = 'hidden'
  }
  // 啟用頁面滾動(dòng)的函數(shù)
  const enablePageScroll = () => {
    document.body.style.overflow = 'auto'
  }
  // 開始拖拽,禁用頁面滾動(dòng)
  const startDragging = () => {
    isDragging = true
    disablePageScroll()
  }
  // 停止拖拽,啟用頁面滾動(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)松開或觸摸結(jié)束事件
  const handleMouseUp = () => {
    // 停止拖拽,恢復(fù)點(diǎn)擊事件
    stopDragging()
    // 移除鼠標(biāo)移動(dòng)事件和觸摸移動(dòng)事件的監(jiān)聽器
    document.removeEventListener('touchmove', handleMouseMove)
    document.removeEventListener('mousemove', handleMouseMove)
  }
  // 處理鼠標(biāo)按下或觸摸開始事件
  const handleMouseDown = (event: MouseEvent | TouchEvent) => {
    if (!divRef.value) return
    // 獲取鼠標(biāo)點(diǎn)擊或觸摸點(diǎn)相對于 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
    // 開始拖拽,添加鼠標(biāo)移動(dòng)和觸摸移動(dòng)事件監(jiān)聽器
    startDragging()
    document.addEventListener('mousemove', handleMouseMove, {
      passive: false, // 阻止默認(rèn)滾動(dòng)行為
    })
    document.addEventListener('touchmove', handleMouseMove, {
      passive: false, // 阻止默認(rèn)滾動(dòng)行為
    })
    // 添加鼠標(biāo)松開和觸摸結(jié)束事件監(jiān)聽器
    document.addEventListener('mouseup', handleMouseUp)
    document.addEventListener('touchend', handleMouseUp)
  }
  // 在組件掛載時(shí),添加鼠標(biāo)按下和觸摸開始事件監(jiān)聽器
  onMounted(() => {
    if (divRef.value) {
      divRef.value.addEventListener('mousedown', handleMouseDown)
      divRef.value.addEventListener('touchstart', handleMouseDown)
    }
  })
  // 在組件卸載時(shí),移除事件監(jiān)聽器
  onUnmounted(() => {
    if (divRef.value) {
      divRef.value.removeEventListener('mousedown', handleMouseDown)
      divRef.value.removeEventListener('touchstart', handleMouseDown)
    }
    document.removeEventListener('mouseup', handleMouseUp)
    document.removeEventListener('touchend', handleMouseUp)
  })
  // 返回 div 元素的引用,可以在組件中使用該引用來創(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>

方案二 自定義指令寫法

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)和松開事件
        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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue操作數(shù)組的幾種常用方法小結(jié)

    Vue操作數(shù)組的幾種常用方法小結(jié)

    本文主要介紹了Vue操作數(shù)組的幾種常用方法小結(jié),主要包括map、filter、forEach、find 和 findIndex 、some 和 every、includes、Array.from這幾種方法,感興趣的可以了解一下
    2023-09-09
  • 使用Vue?控制元素顯示隱藏的方法和區(qū)別

    使用Vue?控制元素顯示隱藏的方法和區(qū)別

    這篇文章主要介紹了Vue??控制元素顯示隱藏的方法和區(qū)別,需要的朋友可以參考下
    2022-12-12
  • vue面試??贾甤omputed是如何實(shí)現(xiàn)的

    vue面試??贾甤omputed是如何實(shí)現(xiàn)的

    對于每天都在用的計(jì)算屬性(computed),小編猜大家肯定也想窺探其奧妙與原理對吧,所以這篇文章就來講講computed是如何實(shí)現(xiàn)的吧,感興趣的小伙伴可以學(xué)習(xí)一下
    2023-08-08
  • Vue3 el-switch @change事件在初始化時(shí)會(huì)自動(dòng)調(diào)用問題處理的多種方法

    Vue3 el-switch @change事件在初始化時(shí)會(huì)自動(dòng)調(diào)用問題處理的多種方法

    本文介紹Vue3中Switch切換確認(rèn)彈窗的兩種實(shí)現(xiàn)方法:方法一通過before-change攔截器精準(zhǔn)控制(推薦);方法二用初始化狀態(tài)標(biāo)識兼容舊項(xiàng)目,并提供統(tǒng)一封裝方案,適用于ElementPlus/NaiveUI等組件,感興趣的朋友一起看看吧
    2025-07-07
  • Vue登錄攔截 登錄后繼續(xù)跳轉(zhuǎn)指定頁面的操作

    Vue登錄攔截 登錄后繼續(xù)跳轉(zhuǎn)指定頁面的操作

    這篇文章主要介紹了Vue登錄攔截 登錄后繼續(xù)跳轉(zhuǎn)指定頁面的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • vue之input輸入框防抖debounce函數(shù)的使用方式

    vue之input輸入框防抖debounce函數(shù)的使用方式

    這篇文章主要介紹了vue之input輸入框防抖debounce函數(shù)的使用方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • vue利用vant組件實(shí)現(xiàn)輪播圖效果

    vue利用vant組件實(shí)現(xiàn)輪播圖效果

    vant組件適用于移動(dòng)端項(xiàng)目,目前項(xiàng)目開源,是市面上做的比較好的開源項(xiàng)目,功能比較強(qiáng)大,本文小編就來為大家介紹一下如何利用vant實(shí)現(xiàn)輪播圖效果吧
    2023-10-10
  • vue路由三種傳參方式詳細(xì)講解

    vue路由三種傳參方式詳細(xì)講解

    在Vue中有多種方式可以進(jìn)行路由跳轉(zhuǎn)并傳遞參數(shù),這篇文章主要給大家介紹了關(guān)于vue路由三種傳參方式的相關(guān)資料,文中介紹的方法分別包括params傳參、query傳參(顯示傳參)以及props傳參(路由組件傳參),,需要的朋友可以參考下
    2023-10-10
  • 深度解析Vue3狀態(tài)管理中Hook與Store的區(qū)別

    深度解析Vue3狀態(tài)管理中Hook與Store的區(qū)別

    在?Vue?3?生態(tài)中,狀態(tài)管理是構(gòu)建可維護(hù)應(yīng)用的核心挑戰(zhàn),?本文將深入剖析Hook與Store的本質(zhì)區(qū)別,重點(diǎn)探討?Hook?實(shí)現(xiàn)數(shù)據(jù)共享的可行性,并提供實(shí)用的選型策略
    2026-05-05
  • Vue.js?前端路由和異步組件介紹

    Vue.js?前端路由和異步組件介紹

    這篇文章主要介紹了Vue.js?前端路由和異步組件介紹,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09

最新評論

通山县| 利辛县| 宝清县| 佛山市| 满洲里市| 马关县| 五河县| 呼玛县| 鄂尔多斯市| 醴陵市| 长沙县| 余姚市| 柳林县| 娄底市| 开原市| 抚州市| 定安县| 靖西县| 乡城县| 新源县| 资源县| 古交市| 韶山市| 盐源县| 酉阳| 集贤县| 克拉玛依市| 江阴市| 余干县| 沐川县| 菏泽市| 临海市| 张掖市| 澎湖县| 乌鲁木齐市| 玉溪市| 高安市| 鲜城| 炉霍县| 南昌市| 大兴区|