admin 管理员组

文章数量: 887007

【VueJS】解决鼠标拖拽与点击事件冲突元素在制定div中拖拽移动场景

拖动的元素在鼠标按下时通过时间差给元素添加一个属性(drag-flag),若超过规定时间则认为是在拖拽,属性设置为true, 否则设置为false。点击方法中通过获取这个属性判断是否要执行点击后的指令。

html:

<div class="drag-area"><div v-if="isShowHelp" class="help-control"ref="helpControl"draggable="true"@mousedown.stop="moveLineFun"@click="openWin">帮助</div></div>

JS:

openWin() {// 解决鼠标拖拽与点击事件冲突const isDrag = this.$refs.helpControl.getAttribute('drag-flag');if (isDrag === 'true') {return false;}this.setIsOpenDocWin(true);},// 拖拽监听鼠标按下
moveLineFun(e) {const { target } = e;const moveBoxObj = document.getElementsByClassName('drag-area')[0]; // 最大的框,自带相对定位属性const moveObj = document.getElementsByClassName('help-control')[0]; // 移动的元素const moveBoxObjMaxWidth = moveBoxObj.clientWidth; // 得到点击时该元素所在大容器的宽const moveBoxObjMaxHeight = moveBoxObj.clientHeight; // 得到点击时该元素所在大容器的高const moveLineObjOffsetLeft = moveObj.offsetLeft; // 得到点击时该元素的左边距const moveLineObjOffsetTop = moveObj.offsetTop; // 得到点击时该元素的上边距const moveStartX = e.clientX; // 得到当前鼠标点击的x位置const moveStartY = e.clientY; // 得到当前鼠标点击的x位置const leftMin = 0;const leftMax = moveBoxObjMaxWidth - moveObj.clientWidth; // 左边距最大值const topMax = moveBoxObjMaxHeight - moveObj.clientHeight; // 上边距最大值// 解决鼠标拖拽与点击事件冲突:时间差moveObj.setAttribute('drag-flag', false);const dragStartTime = new Date().getTime();document.onmousemove = () => {const dragEndTime = new Date().getTime();if (dragEndTime - dragStartTime > 200) {target.setAttribute('drag-flag', true);}};// 绑定鼠标移动时的计算function moveFun(e1) {e1.preventDefault();const mouseMoveDistance = e1.clientX - moveStartX; // 鼠标滑动距离(正则是往右;负则是往左)const mouseMoveDistanceY = e1.clientY - moveStartY; // 鼠标滑动距离(正则是往下;负则是往上)moveObj.style.positon = 'absolute'; // 给元素添加绝对定位属性let styleLeft = moveLineObjOffsetLeft + mouseMoveDistance; // 左边距 = 元素初始(左边距)位置 + 鼠标滑动的距离let styleTop = moveLineObjOffsetTop + mouseMoveDistanceY; // 左边距 = 元素初始(左边距)位置 + 鼠标滑动的距离console.log('move');if (styleLeft <= leftMin) {styleLeft = leftMin;} else if (styleLeft > leftMax) {styleLeft = leftMax;}if (styleTop <= moveObj.clientHeight) {styleTop = 0;} else if (styleTop > topMax) {styleTop = topMax;}moveObj.style.left = `${styleLeft}px`; // 赋值拖动的线的左边距离moveObj.style.top = `${styleTop}px`; // 赋值拖动的线的左边距离}// 取消计算绑定function stopFun() {document.removeEventListener('mousemove', moveFun); // 取消监听事件,鼠标开始移动document.removeEventListener('mouseup', stopFun); // 取消监听事件,鼠标停止移动}clearTimeout(this.clickTimeId);document.addEventListener('mousemove', moveFun); // 添加监听事件,鼠标开始移动document.addEventListener('mouseup', stopFun); // 添加监听事件,鼠标停止移动
},
},

场景:帮助按钮吸附在页面右侧,hover按钮时展开,点击时展开浮窗文档,可对按钮进行右侧的竖向拖拽

<template><fin-main v-if="subAppActive" class="portal-sub-app-wrapper"><license-info:message="licenseMessage":isShow="isShowLicense"@close="closeLicense"></license-info><!-- 系统左侧菜单容器 --><divclass="boyun-menu"id="boyun-menu"v-if="isBoYun":style="{ height: subAppHeight }"></div><divclass="portal-sub-app":class="{ 'boyun-sub-app': isBoYun }"id="portal-sub-app":style="{ height: subAppHeight }"></div><!-- 帮助悬浮按钮 --><div class="drag-area"><div v-if="isShowHelp" class="help-control"ref="helpControl"draggable="true"@mousedown.stop="moveLineFun"@click="openWin">帮助</div></div></fin-main>
</template>
<script>
import { mapState, mapMutations, mapGetters } from 'vuex';
import LicenseInfo from '@c/license-info';
import dayjs from 'dayjs';
// import DragDoc from '@c/drag-modules/drag-doc.vue';
import { checkLicense } from '@/modules/sys/service';
import { start, isMicroSub, getMicroSub } from '@/micro';
import { aesDecrypt, random } from '@/utils/crypto';export default {data() {return {licenseMessage: '',subAppHeight: '100%',isBoYun: false,myArray: [1],createEle: null,};},components: {LicenseInfo,// DragDoc,},computed: {...mapState('app', ['subAppActive', 'isShowLicense', 'isOpenDocWin']),...mapState('user', ['subApps', '']),...mapGetters('user', ['isShowHelp']),},watch: {$route(val) {this.handleRouteChange(val);},isShowLicense(val) {if (val && this.licenseMessage.length) {this.subAppHeight = 'calc( 100% - 36px )';} else {this.subAppHeight = '100%';}},},beforeRouteEnter(to, from, next) {next((vm) => {vm.handleRouteChange.apply(vm, [to]);});},mounted() {const { register } = this.$store.state.app;if (!register) {this.$storemit('app/TOGGLE_REGISTER');const appList = this.buildAppList();console.log(appList);start({prefetch: false,appList,});}},methods: {...mapMutations('user', ['setIsOpenDocWin']),// 打开拖拽窗openWin() {// 解决鼠标拖拽与点击事件冲突const isDrag = this.$refs.helpControl.getAttribute('drag-flag');if (isDrag === 'true') {return false;}this.setIsOpenDocWin(true);},// 第一次进入以及切换其它子系统触发该方法// 判断是否访问的是子系统,如果为子系统收起portal菜单以及处理全屏路由、license校验handleRouteChange($route) {if (this.$route.path.startsWith('/boc')) {this.isBoYun = true;} else {this.isBoYun = false;}const appList = this.buildAppList();const bol = isMicroSub(appList, this.$route.path);this.$storemit('app/TOGGLE_SUBAPPACTIVE', bol);// 如果是子应用关闭系统菜单if (bol) {this.$storemit('app/TOGGLE_SIDEBAR', true);const microSub = getMicroSub(appList, $route.path);// if (EnableLicense) {this.validLicense(microSub);// }// 添加 hash模式全屏if ($route.path.startsWith(`${microSub.entry}/full`)|| ($route.hash && $route.hash.startsWith('#/full'))) {// mounted后执行setTimeout(() => {window.eventCenter.emit('SYSTEM_FULL_SCREEN');});// window.eventCenter.emit('SYSTEM_FULL_SCREEN');// eslint-disable-next-line no-underscore-dangle} else if (window.__IS_FULL_SCREEN) {window.eventCenter.emit('SYSTEM_EXIT_FULL_SCREEN');}} else {this.$router.replace({path: '/404',});}},buildAppList() {if (!process.env.isProduction) {return process.env.appList;}const singleSpaList = [];this.subApps.map((app) => {const { name, url, extend } = app;try {const info = JSON.parse(extend);const { appName, entry, container } = info;singleSpaList.push({name: appName,entry,context: `/${appName}`,container,});} catch (error) {const entry = url || `/${name}`;singleSpaList.push({name,entry,context: entry,});}});return singleSpaList;},validLicense(app) {// 随机数const seq = random(100000000);checkLicense({key: app.context,seq,}).then((res) => {try {res = res.replace(/[\r\n]/g, '');const resStr = aesDecrypt(res);const data = JSON.parse(resStr);const { result, seq: seqNext, list = [] } = data;if (seq + 1 !== seqNext) {this.$message.error('license校验数据格式错误!');return this.$router.replace({name: 'home',});}// 过期if (result === 0) {return this.$router.replace({name: 'exception_expire',});}// 未到有效期if (result === -1) {const str = list.reduce((pre, current) => {const { productName, startDate, endDate } = current;return `${pre}${productName} 有效期为 ${dayjs(startDate).format('YYYY-MM-DD',)}~${dayjs(endDate).format('YYYY-MM-DD')}`;}, '');return this.$router.replace({name: 'not_valid',params: {desc: str,},});}if (list.length) {const str = list.reduce((pre, current) => {const { productName, endDate } = current;return `${pre}${productName} 将于 ${dayjs(endDate).format('YYYY-MM-DD',)} 到期,`;}, '');this.licenseMessage = `${str}到期后产品将不能使用,请联系管理员续约!`;}} catch (error) {console.log(error);}}).catch((err) => {console.error(err);this.$router.replace({name: 'home',});});},closeLicense() {this.$storemit('app/setLicenseState', false);},// 拖拽监听鼠标按下moveLineFun(e) {const { target } = e;const moveBoxObj = document.getElementsByClassName('drag-area')[0]; // 最大的框,自带相对定位属性const moveObj = document.getElementsByClassName('help-control')[0]; // 移动的元素const moveBoxObjMaxWidth = moveBoxObj.clientWidth; // 得到点击时该元素所在大容器的宽const moveBoxObjMaxHeight = moveBoxObj.clientHeight; // 得到点击时该元素所在大容器的高const moveLineObjOffsetLeft = moveObj.offsetLeft; // 得到点击时该元素的左边距const moveLineObjOffsetTop = moveObj.offsetTop; // 得到点击时该元素的上边距const moveStartX = e.clientX; // 得到当前鼠标点击的x位置const moveStartY = e.clientY; // 得到当前鼠标点击的x位置const leftMin = 0;const leftMax = moveBoxObjMaxWidth - moveObj.clientWidth; // 左边距最大值const topMax = moveBoxObjMaxHeight - moveObj.clientHeight; // 上边距最大值// 解决鼠标拖拽与点击事件冲突:时间差moveObj.setAttribute('drag-flag', false);const dragStartTime = new Date().getTime();document.onmousemove = () => {const dragEndTime = new Date().getTime();if (dragEndTime - dragStartTime > 200) {target.setAttribute('drag-flag', true);}};// 绑定鼠标移动时的计算function moveFun(e1) {e1.preventDefault();const mouseMoveDistance = e1.clientX - moveStartX; // 鼠标滑动距离(正则是往右;负则是往左)const mouseMoveDistanceY = e1.clientY - moveStartY; // 鼠标滑动距离(正则是往下;负则是往上)moveObj.style.positon = 'absolute'; // 给元素添加绝对定位属性let styleLeft = moveLineObjOffsetLeft + mouseMoveDistance; // 左边距 = 元素初始(左边距)位置 + 鼠标滑动的距离let styleTop = moveLineObjOffsetTop + mouseMoveDistanceY; // 左边距 = 元素初始(左边距)位置 + 鼠标滑动的距离console.log('move');if (styleLeft <= leftMin) {styleLeft = leftMin;} else if (styleLeft > leftMax) {styleLeft = leftMax;}if (styleTop <= moveObj.clientHeight) {styleTop = 0;} else if (styleTop > topMax) {styleTop = topMax;}moveObj.style.top = `${styleTop}px`; // 赋值拖动的线的上边距离moveObj.style.left = `${styleLeft}px`; // 赋值拖动的线的左边距离}// 取消计算绑定function stopFun() {document.removeEventListener('mousemove', moveFun); // 取消监听事件,鼠标开始移动document.removeEventListener('mouseup', stopFun); // 取消监听事件,鼠标停止移动moveObj.style.left = null; // 清除left行内样式, 否则鼠标移开时收不回去}document.addEventListener('mousemove', moveFun); // 添加监听事件,鼠标开始移动document.addEventListener('mouseup', stopFun); // 添加监听事件,鼠标停止移动},},
};
</script><style lang="less" scoped>
.portal-sub-app-wrapper {height: 100%;padding: 0 !important;overflow-x: hidden;.drag-area {position: absolute;top: 56px;right: 0;z-index: 999;width: 52px;height: calc(~"100% - 56px");.help-control {position: absolute;top: 50%;right: -24px !important;z-index: 999;width: 44px;height: 44px;padding: 10px;border-radius: 50%;font-size: 12px;line-height: 24px;color: #fff;background: #2c68ff;cursor: pointer;}}.help-control:hover {right: 8px !important;font-size: 12px;}.license-info {position: relative;padding: 8px 16px;color: #fff;background-color: #ff9c00;&-content {display: table-cell;padding: 0 8px;.title {font-size: 14px;line-height: 18px;}}.close-icon {position: absolute;top: 12px;right: 16px;font-size: 12px;color: #fff;opacity: 1;cursor: pointer;}}.boyun-menu {width: 180px;overflow: hidden;float: left;}.boyun-sub-app {width: calc(~"100% - 180px");float: left;}
}
</style>

本文标签: VueJS解决鼠标拖拽与点击事件冲突元素在制定div中拖拽移动场景