【Vue&JS】解决鼠标拖拽与点击事件冲突&元素在指定div中拖拽移动场景
vue
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
项目地址:https://gitcode.com/gh_mirrors/vu/vue
免费下载资源
·
拖动的元素在鼠标按下时通过时间差给元素添加一个属性(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>
<!-- 系统左侧菜单容器 -->
<div
class="boyun-menu"
id="boyun-menu"
v-if="isBoYun"
:style="{ height: subAppHeight }"
></div>
<div
class="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.$store.commit('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.$store.commit('app/TOGGLE_SUBAPPACTIVE', bol);
// 如果是子应用关闭系统菜单
if (bol) {
this.$store.commit('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.$store.commit('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>
GitHub 加速计划 / vu / vue
207.54 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:2 个月前 )
73486cb5
* chore: fix link broken
Signed-off-by: snoppy <michaleli@foxmail.com>
* Update packages/template-compiler/README.md [skip ci]
---------
Signed-off-by: snoppy <michaleli@foxmail.com>
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 4 个月前
e428d891
Updated Browser Compatibility reference. The previous currently returns HTTP 404. 5 个月前
更多推荐
已为社区贡献1条内容
所有评论(0)