【Vue&JS】解决鼠标拖拽与点击事件冲突&元素在制定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>
    <!-- 系统左侧菜单容器 -->
    <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>
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这个问题可能是由于鼠标事件和 iframe 渲染事件的冲突导致的。您可以尝试在拖拽时将 iframe 隐藏或者将其 z-index 设置为 -1,以避免冲突。另外,您可以使用 CSS3 的 transform 属性来实现拖拽,这样可以减少因为重排和重绘导致的卡顿。具体做法如下: 1. 在拖拽开始时,将 iframe 的 z-index 设置为 -1 或者将其隐藏起来: ``` iframe { z-index: -1; /* 或者 */ display: none; } ``` 2. 使用 CSS3 的 transform 属性来实现拖拽: ``` <div class="draggable" @mousedown="handleMouseDown"> <!-- your content here --> </div> <script> export default { data() { return { dragging: false, startX: 0, startY: 0, translateX: 0, translateY: 0, }; }, methods: { handleMouseDown(event) { this.dragging = true; this.startX = event.clientX; this.startY = event.clientY; }, handleMouseMove(event) { if (this.dragging) { this.translateX = event.clientX - this.startX; this.translateY = event.clientY - this.startY; this.$refs.draggable.style.transform = `translate(${this.translateX}px, ${this.translateY}px)`; } }, handleMouseUp(event) { this.dragging = false; }, }, mounted() { document.addEventListener('mousemove', this.handleMouseMove); document.addEventListener('mouseup', this.handleMouseUp); }, beforeDestroy() { document.removeEventListener('mousemove', this.handleMouseMove); document.removeEventListener('mouseup', this.handleMouseUp); }, }; </script> <style scoped> .draggable { position: absolute; top: 0; left: 0; cursor: move; } </style> ``` 注意,这里的 translate 属性是相对于元素自身的位置进行位移,而不是相对于父元素的位置进行位移。如果您需要相对于父元素进行位移,可以使用 position 和 top/left 属性来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值