vue3實現(xiàn)對自定義組件自由拖動效果
在數(shù)據(jù)可視化看板開發(fā)中,組件的自由拖拽功能能極大提升交互體驗。本文將基于Vue3生態(tài),從零開始解析如何實現(xiàn)一個支持自由拖拽的容器組件。
一、核心實現(xiàn)原理
1.組件定位基礎(chǔ)
使用position: fixed實現(xiàn)絕對定位,通過動態(tài)樣式綁定left/top控制位置,@mousedown事件來觸發(fā)拖曳效果。
<template>
<div class="left-admin" @mousedown="startDrag" :style="{ left: panelPosition.x + 'px', top: panelPosition.y + 'px' }">
</div>
</template>
<style lang="less" scoped>
.left-admin {
position: fixed;
cursor: move;
}
</style>2.拖曳過程實現(xiàn)
(1)定義初始屏幕寬度、位置及判斷條件
const panelWidth = 443//初始寬度
const panelPosition = ref({//初始位置
x: 8,
y: 80
// x: 0.5,
// y: 0.5
})
let isDragging = false//是否拖拽中
let startX = 0//開始X坐標(biāo)
let startY = 0//開始Y坐標(biāo)
let initialX = 0//初始X坐標(biāo)
let initialY = 0//初始Y坐標(biāo)(2)鼠標(biāo)按下事件
當(dāng)鼠標(biāo)進(jìn)行拖動時,獲取獲取鼠標(biāo)的X、Y坐標(biāo),并且綁定鼠標(biāo)的移動和釋放事件
// 鼠標(biāo)按下事件
const startDrag = e => {
isDragging = true
startX = e.clientX//獲取鼠標(biāo)X坐標(biāo)
startY = e.clientY//獲取鼠標(biāo)Y坐標(biāo)
initialX = panelPosition.value.x//獲取初始X坐標(biāo)
initialY = panelPosition.value.y//獲取初始Y坐標(biāo)
document.addEventListener('mousemove', onDrag)//綁定鼠標(biāo)移動事件
document.addEventListener('mouseup', stopDrag)//綁定鼠標(biāo)釋放事件
}(3)鼠標(biāo)移動事件
// 鼠標(biāo)移動事件
const onDrag = e => {
// 防止拖拽過程中鼠標(biāo)點擊導(dǎo)致的頁面閃爍
if (!isDragging) return
//計算移動距離
const dx = e.clientX - startX
const dy = e.clientY - startY
// 添加邊界限制
let newX = initialX + dx
let newY = initialY + dy
newX = Math.max(newX, 8)//最小值
newX = Math.min(newX, window.innerWidth - panelWidth - 8)//最大值
panelPosition.value = {//更新位置數(shù)據(jù)
x: newX,
y: newY
}
}(4)鼠標(biāo)釋放事件
// 鼠標(biāo)釋放事件
const stopDrag = () => {
isDragging = false
document.removeEventListener('mousemove', onDrag)// 鼠標(biāo)移動事件
document.removeEventListener('mouseup', stopDrag)// 鼠標(biāo)釋放事件
}(5)組件卸載時清理事件
// 組件卸載時清理事件
onBeforeUnmount(() => {
document.removeEventListener('mousemove', onDrag)
document.removeEventListener('mouseup', stopDrag)
})二、全部代碼
以下代碼是實際項目中使用到的,重要的過程代碼對應(yīng)以上核心實現(xiàn)原理這里,可根據(jù)自行項目需要進(jìn)行摘取
<template>
<div class="left-admin" @mousedown="startDrag" :style="{ left: panelPosition.x + 'px', top: panelPosition.y + 'px' }">
<!-- <div class="top-title">
<h1>各行政區(qū)檢修工單統(tǒng)計(本月度)</h1>
</div> -->
<div class="admin-top">
<h1>{{ adminTitle }}</h1>
<img src="@/assets/images/page/close.svg" class="close-btn" @click="closeLeftAdmin" />
</div>
<div class="chart">
<radarChart :indicatorData="indicatorData" :radarData="radarData" :chartColor="props.chartColor" />
</div>
</div>
</template>
<script setup>
import { onMounted, ref, onBeforeUnmount, getCurrentInstance } from 'vue'
import radarChart from '@/components/chart/radar-chart.vue'
import { getDevRepairCount } from '@/api/home/home-left.js'
const props = defineProps(['chartColor'])
const emit = defineEmits(['closeLeftAdmin'])
const context = getCurrentInstance()?.appContext.config.globalProperties
const dayjs = context?.$dayjs
const adminTitle = ref('各行政區(qū)檢修工單統(tǒng)計(本月度)')
const indicatorData = ref([
// { name: '張家港市' },
// { name: '吳江區(qū)' },
// { name: '吳中區(qū)' },
// { name: '姑蘇區(qū)' },
// { name: '虎丘區(qū)' },
// { name: '相城區(qū)' },
// { name: '昆山市' },
// { name: '太倉市' },
// { name: '常熟市' }
])
const radarData = ref([
// { value: [1200, 3000, 4000, 1000, 30000, 3000, 35000, 50000, 18000] }
// { name: '已開工', value: [5200, 2000, 30000, 2000, 10000, 18000] },
// { name: '未開工', value: [3200, 2000, 5000, 2000, 10000, 18000] },
// { name: '已竣工', value: [2200, 2000, 5000, 2000, 10000, 18000] },
// { name: '當(dāng)值開竣工', value: [4200, 3000, 20000, 35000, 50000, 18000] },
// { name: '改期和延期', value: [5000, 14000, 28000, 26000, 42000, 21000] }
])
const closeLeftAdmin = () => {
emit('closeLeftAdmin')
}
// 新增狀態(tài)
// const panelPosition = ref({ x: 8, y: 80 })
const panelWidth = 443
const panelPosition = ref({
x: 8,
y: 80
// x: 0.5,
// y: 0.5
})
let isDragging = false
let startX = 0
let startY = 0
let initialX = 0
let initialY = 0
// 鼠標(biāo)按下事件
const startDrag = e => {
isDragging = true
startX = e.clientX
startY = e.clientY
initialX = panelPosition.value.x
initialY = panelPosition.value.y
document.addEventListener('mousemove', onDrag)
document.addEventListener('mouseup', stopDrag)
}
// 鼠標(biāo)移動事件
const onDrag = e => {
if (!isDragging) return
const dx = e.clientX - startX
const dy = e.clientY - startY
let newX = initialX + dx
let newY = initialY + dy
newX = Math.max(newX, 8)
newX = Math.min(newX, window.innerWidth - panelWidth - 8)
panelPosition.value = {
x: newX,
y: newY
}
}
// 鼠標(biāo)釋放事件
const stopDrag = () => {
isDragging = false
document.removeEventListener('mousemove', onDrag)
document.removeEventListener('mouseup', stopDrag)
}
// 組件卸載時清理事件
onBeforeUnmount(() => {
document.removeEventListener('mousemove', onDrag)
document.removeEventListener('mouseup', stopDrag)
})
</script>
<style lang="less" scoped>
.left-admin {
position: fixed;
cursor: move;
}
</style>到此這篇關(guān)于vue3實現(xiàn)對自定義組件自由拖動效果的文章就介紹到這了,更多相關(guān)vue自定義組件自由拖動內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue生成token保存在客戶端localStorage中的方法
本篇文章主要介紹了vue生成token保存在客戶端localStorage中的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
Vue3+Antd實現(xiàn)彈框顯示內(nèi)容并加入復(fù)制按鈕
這篇文章主要介紹了Vue3+Antd實現(xiàn)彈框顯示內(nèi)容并加入復(fù)制按鈕,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-12-12
vue3 setup點擊跳轉(zhuǎn)頁面的實現(xiàn)示例
本文主要介紹了vue3 setup點擊跳轉(zhuǎn)頁面的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-10-10
vue-element-admin登錄攔截設(shè)置白名單方式
這篇文章主要介紹了vue-element-admin登錄攔截設(shè)置白名單方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
vue v-viewer組件使用示例詳解(v-viewer輪播圖)
這篇文章主要介紹了vue v-viewer組件使用示例詳解(v-viewer輪播圖),本文結(jié)合實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-02-02
Vue中Axios從遠(yuǎn)程/后臺讀取數(shù)據(jù)
今天小編就為大家分享一篇關(guān)于Vue中Axios從遠(yuǎn)程/后臺讀取數(shù)據(jù),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01
vue?+?element-plus自定義表單驗證(修改密碼業(yè)務(wù))的示例
這篇文章主要介紹了vue?+?element-plus自定義表單驗證(修改密碼業(yè)務(wù)),本文通過實例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2025-04-04

