前言
使用Vue实现任意上下左右拖拽悬浮的元素,以及具有边界处理的具体实现是网上的大神实现的,也不知是谁了。俺只不过是优化了一下皮毛,写了个在Vue2或Vue3的项目中的示例,配置自定义的全局指令,主要是方便下次使用,记录一下。
一、基于Vue2框架的项目
(1)在 /src/utils/ 目录中新建 diyVueDirectives.js
/**
* 自定义拖拽指令
*/
const dragSwitch = {
bind(el, binding, vnode, oldVnode) {
// 判断是否可拖拽
if (!binding.value) {
return
}
// 获取相关元素
const container = el.querySelector('.d-d_container')
const header = el.querySelector('.d-d_container_header')
header.style.cssText += ';cursor:move;'
// 获取元素原有属性
const sty = (function () {
if ((document.body).currentStyle) {
return (dom, attr) => dom.currentStyle[attr] // 兼容IE写法
}
return (dom, attr) => getComputedStyle(dom, null)[attr]
})()
/**
* 鼠标按下事件
*/
header.onmousedown = (e) => {
const disX = e.clientX - header.offsetLeft
const disY = e.clientY - header.offsetTop
const screenWidth = document.body.clientWidth // document.body的可见区域宽度
const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取)
const containerWidth = container.offsetWidth // 对话框宽度
const containerheight = container.offsetHeight // 对话框高度
const minContainerLeft = container.offsetLeft
const maxContainerLeft = screenWidth - container.offsetLeft - containerWidth
const minContainerTop = container.offsetTop
const maxContainerTop = screenHeight - container.offsetTop - containerheight
// 左偏移距离
let styL = sty(container, 'left')
if (styL === 'auto') {
styL = '0px' // 兼容IE写法
}
// 上偏移距离
let styT = sty(container, 'top')
// 注意在IE中,第一次获取到的值为组件自带50%,移动之后赋值为px
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/%/g, '') / 100)
styT = +document.body.clientHeight * (+styT.replace(/%/g, '') / 100)
} else {
styL = +styL.replace(/px/g, '')
styT = +styT.replace(/px/g, '')
}
/**
* 鼠标移动事件
*/
document.onmousemove = function (e) {
// 通过事件委托,计算移动的距离
let left = e.clientX - disX
let top = e.clientY - disY
// 边界处理
if (-(left) > minContainerLeft) {
left = -(minContainerLeft)
} else if (left > maxContainerLeft) {
left = maxContainerLeft
}
if (-(top) > minContainerTop) {
top = -(minContainerTop)
} else if (top > maxContainerTop) {
top = maxContainerTop
}
// 移动当前元素
container.style.cssText += `;left:${
left + styL}px;top:${
top + styT}px;`
}
/**
* 鼠标松开事件
*/
document.onmouseup = function (e) {
document.onmousemove = null
document.onmouseup = null
}
return false
}
}
}
// 注册自定义拖拽指令
Vue.directive('dragSwitch', dragSwitch)
(2)在 main.js 中引入该指令集
// 引入 Vue 自定义指令集
import "@/utils/diyVueDirectives"
(3)任意在一个 vue 页面中使用 v-dragSwitch 指令即可
<template>
<div style="width: 100%; height: 100%; position: relative; overflow: hidden; background-color: #dbe8ff">
<!-- ^ 自定义拖拽模块一 -->
<div class="d-d" v-dragSwitch="true">
<div class="d-d_container" style="">
<div class="d-d_container_header">标题一</div>
</div>
</div>
<!-- / 自定义拖拽模块一 -->
<!-- ^ 自定义拖拽模块二 -->
<div class="d-d" v-dragSwitch="false">
<div class="d-d_container" style="left: 100px; top: 100px">
<div class="d-d_container_header">标题二</div>
</div>
</div>
<!-- / 自定义拖拽模块二 -->
<!-- ^ 自定义拖拽模块三 -->
<div class="d-d" v-dragSwitch="true">
<div class="d-d_container" style="right: 20px; top: calc(50% - 50px)">
<div class="d-d_container_header">标题三</div>
</div>
</div>
<!-- / 自定义拖拽模块三 -->
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style lang="less" scoped>
.d-d {
width: auto;
height: auto;
position: absolute;
.d-d_container {
width: 100px;
height: 100px;
position: fixed;
background-color: #fff;
user-select: none;
.d-d_container_header {
text-align: center;
border-bottom: 1px solid #dcdfe6;
}
}
}
</style>
二、基于Vue3框架的项目
(1)在 /src/utils/ 目录中新建 diyVueDirectives.ts
/**
* 自定义拖拽指令
*/
const dragSwitch = {
beforeMount(el: any, binding: any) {
// 判断是否可拖拽
if (!binding.value) {
return
}
// 获取相关元素
const container = el.querySelector('.d-d_container')
const header = el.querySelector('.d-d_container_header')
header.style.cssText += ';cursor:move;'
// 获取元素原有属性
const sty = (function () {
if ((document.body as any).currentStyle) {
return (dom: any, attr: any) => dom.currentStyle[attr] // 兼容IE写法
}
return (dom: any, attr: any) => getComputedStyle(dom, null)[attr]
})()
/**
* 鼠标按下事件
*/
header.onmousedown = (e: any) => {
const disX = e.clientX - header.offsetLeft
const disY = e.clientY - header.offsetTop
const screenWidth = document.body.clientWidth // document.body的可见区域宽度
const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取)
const containerWidth = container.offsetWidth // 对话框宽度
const containerheight = container.offsetHeight // 对话框高度
const minContainerLeft = container.offsetLeft
const maxContainerLeft = screenWidth - container.offsetLeft - containerWidth
const minContainerTop = container.offsetTop
const maxContainerTop = screenHeight - container.offsetTop - containerheight
// 左偏移距离
let styL = sty(container, 'left')
if (styL === 'auto') {
styL = '0px' // 兼容IE写法
}
// 上偏移距离
let styT = sty(container, 'top')
// 注意在IE中,第一次获取到的值为组件自带50%,移动之后赋值为px
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/%/g, '') / 100)
styT = +document.body.clientHeight * (+styT.replace(/%/g, '') / 100)
} else {
styL = +styL.replace(/px/g, '')
styT = +styT.replace(/px/g, '')
}
/**
* 鼠标移动事件
*/
document.onmousemove = function (e) {
// 通过事件委托,计算移动的距离
let left = e.clientX - disX
let top = e.clientY - disY
// 边界处理
if (-(left) > minContainerLeft) {
left = -(minContainerLeft)
} else if (left > maxContainerLeft) {
left = maxContainerLeft
}
if (-(top) > minContainerTop) {
top = -(minContainerTop)
} else if (top > maxContainerTop) {
top = maxContainerTop
}
// 移动当前元素
container.style.cssText += `;left:${
left + styL}px;top:${
top + styT}px;`
}
/**
* 鼠标松开事件
*/
document.onmouseup = function (e: any) {
document.onmousemove = null
document.onmouseup = null
}
return false
}
}
}
/**
* 定义指令集
*/
const diyVueDirectives = {
install: function (app: any) {
app.directive('dragSwitch', dragSwitch) // 注册自定义拖拽指令
}
}
/**
* 导出指令集
*/
export default diyVueDirectives
(2)在 main.ts 中引入该指令集
// 引入 Vue 自定义指令集并配置为全局属性
import diyVueDirectives from "@/utils/diyVueDirectives"
// 是否隐藏所有 console.log 信息打印,若注释此代码则显示,否则隐藏
// console.log = () => {}
app
.use(router)
.use(store)
.use(diyVueDirectives)
.use(ElementPlusPlugin)
.mount('#app')
(3)任意在一个 vue 页面中使用 v-dragSwitch 指令即可
<template>
<div style="width: 100%; height: 100%; position: relative; overflow: hidden; background-color: #dbe8ff">
<!-- ^ 自定义拖拽模块一 -->
<div class="d-d" v-dragSwitch="true">
<div class="d-d_container" style="">
<div class="d-d_container_header">标题一</div>
</div>
</div>
<!-- / 自定义拖拽模块一 -->
<!-- ^ 自定义拖拽模块二 -->
<div class="d-d" v-dragSwitch="true">
<div class="d-d_container" style="left: 100px; top: 100px">
<div class="d-d_container_header">标题二</div>
</div>
</div>
<!-- / 自定义拖拽模块二 -->
<!-- ^ 自定义拖拽模块三 -->
<div class="d-d" v-dragSwitch="true">
<div class="d-d_container" style="right: 20px; top: calc(50% - 50px)">
<div class="d-d_container_header">标题三</div>
</div>
</div>
<!-- / 自定义拖拽模块三 -->
</div>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style lang="less" scoped>
.d-d {
width: auto;
height: auto;
position: absolute;
.d-d_container {
width: 100px;
height: 100px;
position: fixed;
background-color: #fff;
user-select: none;
.d-d_container_header {
text-align: center;
border-bottom: 1px solid #dcdfe6;
}
}
}
</style>