前言
使用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.offsetLeftconst disY = e.clientY - header.offsetTopconst screenWidth = document.body.clientWidth // document.body的可见区域宽度const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取)const containerWidth = container.offsetWidth // 对话框宽度const containerheight = container.offsetHeight // 对话框高度const minContainerLeft = container.offsetLeftconst maxContainerLeft = screenWidth - container.offsetLeft - containerWidthconst minContainerTop = container.offsetTopconst maxContainerTop = screenHeight - container.offsetTop - containerheight// 左偏移距离let styL = sty(container, 'left')if (styL === 'auto') {styL = '0px' // 兼容IE写法}// 上偏移距离let styT = sty(container, 'top')// 注意在IE中,第一次获取到的值为组件自带50%,移动之后赋值为pxif (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 - disXlet 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 = nulldocument.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.offsetLeftconst disY = e.clientY - header.offsetTopconst screenWidth = document.body.clientWidth // document.body的可见区域宽度const screenHeight = document.documentElement.clientHeight // 可见区域高度(应为body高度,可某些环境下无法获取)const containerWidth = container.offsetWidth // 对话框宽度const containerheight = container.offsetHeight // 对话框高度const minContainerLeft = container.offsetLeftconst maxContainerLeft = screenWidth - container.offsetLeft - containerWidthconst minContainerTop = container.offsetTopconst maxContainerTop = screenHeight - container.offsetTop - containerheight// 左偏移距离let styL = sty(container, 'left')if (styL === 'auto') {styL = '0px' // 兼容IE写法}// 上偏移距离let styT = sty(container, 'top')// 注意在IE中,第一次获取到的值为组件自带50%,移动之后赋值为pxif (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 - disXlet 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 = nulldocument.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>