首页 > 其他分享 >vue实现可拖拽内容区

vue实现可拖拽内容区

时间:2024-11-22 18:13:36浏览次数:3  
标签:vue const dragElement top event 内容 5px 拖拽 left

 实现效果:

 

MyDrag.vue

<template>
  <div class="box" id="drag" @mousedown="startDrag" ref="drag">
    <div v-for="(handle, index) in resizeHandles" :key="index" :class="['resize-handle', handle]"
      @mousedown="startResize"></div>
    <slot></slot>
  </div>
</template>

<script>
import { throttle } from 'lodash';
export default {
  name: 'MyDrag',
  data() {
    return {
      startX: 0,
      startY: 0,
      startLeft: 0,
      startTop: 0,
      resizeHandles: [
        'top-left',
        'top',
        'top-right',
        'right',
        'bottom-right',
        'bottom',
        'bottom-left',
        'left'
      ]
    }
  },
  computed: {
    parentRect() {
      return this.$refs.drag.parentElement.getBoundingClientRect();
    }
  },

  methods: {
    /**
     * 开始拖拽事件处理函数
     * @param {MouseEvent} event - 鼠标事件对象
     */
    startDrag(event) {
      // 获取拖拽元素
      const dragElement = this.$refs.drag;
      // 检查 dragElement 是否存在
      if (!dragElement) return;
      // 阻止默认事件行为
      event.preventDefault();
      // 获取当前操作的句柄
      const currentHandle = event.target;
      // 检查是否为缩放句柄
      const isResizeHandle = currentHandle.classList.contains("resize-handle");
      // 如果是缩放句柄,则不执行拖拽操作
      if (isResizeHandle) return;

      // 记录初始鼠标位置和元素位置
      this.startX = event.clientX;
      this.startY = event.clientY;
      this.startLeft = dragElement.offsetLeft;
      this.startTop = dragElement.offsetTop;

      // 定义拖拽事件处理函数
      const handleDrag = throttle((event) => {
        // 计算鼠标移动的距离
        const dx = event.clientX - this.startX;
        const dy = event.clientY - this.startY;
        // 计算新的元素位置
        const newLeft = this.startLeft + dx;
        const newTop = this.startTop + dy;

        // 边界条件处理
        // 检查 parentRect 是否存在
        if (!this.parentRect) return;
        // 计算最大边界位置
        const maxLeft = this.parentRect.width - dragElement.offsetWidth;
        const maxTop = this.parentRect.height - dragElement.offsetHeight;
        // 限制元素位置在边界内
        const boundedLeft = Math.max(0, Math.min(newLeft, maxLeft));
        const boundedTop = Math.max(0, Math.min(newTop, maxTop));
        // 更新元素位置
        requestAnimationFrame(() => {
          dragElement.style.left = `${boundedLeft}px`;
          dragElement.style.top = `${boundedTop}px`;
        });
      }, 16); // 限制每秒最多调用 60 次

      // 定义移除事件监听器的函数
      const removeEventListeners = () => {
        document.removeEventListener("mousemove", handleDrag);
        document.removeEventListener("mouseup", stopDrag);
      };

      // 定义停止拖拽事件处理函数
      const stopDrag = () => {
        removeEventListeners();
      };

      // 添加拖拽和释放事件监听器
      document.addEventListener("mousemove", handleDrag);
      document.addEventListener("mouseup", stopDrag);

      // 组件卸载时移除事件监听器
      this.$once('hook:beforeDestroy', removeEventListeners);
    },

    /**
     * 开始调整元素大小的方法
     * @param {MouseEvent} event - 鼠标事件对象
     */
    startResize(event) {
      // 获取需要拖动的元素
      const dragElement = this.$refs.drag;
      // 如果drag元素不存在,则直接返回
      if (!dragElement) return; // 增加异常处理

      // 阻止默认事件行为
      event.preventDefault();
      // 获取当前操作的句柄元素
      const currentHandle = event.target;
      // 获取拖动方向
      const direction = currentHandle.className.split(" ")[1];
      // 记录鼠标起始位置
      const startX = event.clientX;
      const startY = event.clientY;
      // 记录元素初始宽度和高度
      const startWidth = parseInt(dragElement.offsetWidth); // 初始转换为整数
      const startHeight = parseInt(dragElement.offsetHeight); // 初始转换为整数
      // 记录元素初始左上角位置
      const startLeft = dragElement.offsetLeft;
      const startTop = dragElement.offsetTop;

      /**
       * 调整元素大小的函数
       * @param {MouseEvent} event - 鼠标事件对象
       */
      const resize = (event) => {
        // 使用 requestAnimationFrame 优化性能
        requestAnimationFrame(() => {
          // 计算鼠标移动的距离
          const dx = event.clientX - startX;
          const dy = event.clientY - startY;
          // 初始化元素的新尺寸和位置
          let width = startWidth;
          let height = startHeight;
          let left = startLeft;
          let top = startTop;

          // 根据拖动方向调整元素的尺寸和位置
          if (direction.includes("left")) {
            width = startWidth - dx;
            left = startLeft + dx;
            // 确保元素不超过父容器的宽度
            if (this.parentRect.width - left > this.parentRect.width) return;
          }
          if (direction.includes("right")) {
            width = startWidth + dx;
            // 确保元素不超过父容器的宽度
            if (startLeft + width > this.parentRect.width) return;
          }
          if (direction.includes("top")) {
            height = startHeight - dy;
            top = startTop + dy;
            // 确保元素不超过父容器的高度
            if (this.parentRect.height - top > this.parentRect.height) return;
          }
          if (direction.includes("bottom")) {
            height = startHeight + dy;
            // 确保元素不超过父容器的高度
            if (startTop + height > this.parentRect.height) return;
          }

          // 如果计算出的宽度或高度小于等于0,则不进行调整
          if (width <= 0 || height <= 0) return;

          // 更新元素的尺寸和位置
          dragElement.style.width = width + 'px';
          dragElement.style.height = height + 'px';
          dragElement.style.left = left + 'px';
          dragElement.style.top = top + 'px';
        });
      };

      // 停止调整元素大小的方法
      const stopResize = () => {
        // 移除事件监听器
        document.removeEventListener("mousemove", resize);
        document.removeEventListener("mouseup", stopResize);
      };

      // 添加事件监听器
      document.addEventListener("mousemove", resize);
      document.addEventListener("mouseup", stopResize);

      // 确保在组件销毁时移除事件监听器
      this.$once('hook:beforeDestroy', () => {
        document.removeEventListener("mousemove", resize);
        document.removeEventListener("mouseup", stopResize);
      });
    }
  }
}
</script>
<style scoped>
.box {
  position: absolute;
  left: 50px;
  top: 50px;
  width: 100px;
  height: 20px;
  background-color: #f0f0f0;
  cursor: move;
}

.resize-handle {
  position: absolute;
  width: 5px;
  height: 5px;
  background-color: #000;
  cursor: pointer;
}

.top-left {
  top: -5px;
  left: -5px;
  cursor: nw-resize;
}

.top {
  top: -5px;
  left: calc(50% - 5px);
  cursor: ns-resize;
}

.top-right {
  top: -5px;
  right: -5px;
  cursor: ne-resize;
}

.right {
  top: calc(50% - 5px);
  right: -5px;
  cursor: ew-resize;
}

.bottom-right {
  bottom: -5px;
  right: -5px;
  cursor: se-resize;
}

.bottom {
  bottom: -5px;
  left: calc(50% - 5px);
  cursor: ns-resize;
}

.bottom-left {
  bottom: -5px;
  left: -5px;
  cursor: sw-resize;
}

.left {
  top: calc(50% - 5px);
  left: -5px;
  cursor: ew-resize;
}
</style>

 使用组件:

<template>
  <div id="app">
        <div class="container">
      <MyDrag >
        <div>你好</div>
      </MyDrag>
    </div>
  </div>
</template>

<script>
import MyDrag from './components/MyDrag.vue'

export default {
  name: 'App',
  components: {
     MyDrag,
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* text-align: center; */
  color: #2c3e50;
}

.container {
  width: 500px;
  height: 500px;
  border: 1px solid blue;
  position: relative;
  margin: 0 auto;
}

</style>

标签:vue,const,dragElement,top,event,内容,5px,拖拽,left
From: https://blog.csdn.net/m0_53956340/article/details/143980275

相关文章

  • vue快捷工具组件:小地图导航、全屏、刷新
    vue快捷工具组件:小地图导航、全屏、刷新效果代码<!--*@description快捷工具!--><template><divclass="quick-tools":style="{...quickToolsStyle}"><templatev-for="valintools"><tooltipv-if=......
  • 盘点Vue3 watch的一些关键时刻能够大显身手的功能
    前言watch这个API大家应该都不陌生,在Vue3版本中给watch增加不少有用的功能,比如deep选项支持传入数字、pause、resume、stop方法、once选项、onCleanup函数。这些功能大家平时都不怎么用得上,但是在一些特定的场景中,他们能够起大作用,这篇文章欧阳就来带你盘点一下这些功能。d......
  • Vue项目--从安装到发布
    一、安装node1、下载nvm我这里用的nvm(node.jsversionmanagement)一个node.js的版本管理工具,可以安装和切换不同版本的node.js。已经有或者只想安装node.js可跳过下载链接:https://github.com/coreybutler/nvm-windows/releases我的百度资源:https://pan.baidu.com/s/1r_9DN......
  • 硬件零基础到高薪就业学习路线(含学习视频书籍网站推荐及各就业方向需额外学习内容)
    一、基础学习电子技术基础学习内容:数字电路:基本的数字逻辑门(与、或、非、与非、异或等),学习组合逻辑电路(编码器、解码器等)和时序逻辑电路(触发器、寄存器、状态机等)模拟电路:学习基本的电路元件(如电阻、电容、电感等)及其在各种电路(如放大电路、滤波电路、振荡电路等)中的应......
  • 一键复制功能,可复制dom中的所有内容包括图片文本
     1.使用clipboardAPI//复制内容copyInfo(message){letelement=this.$refs.contentContainer//dom元素constrange=document.createRange();range.selectNode(element);window.getSelection().removeAllRanges();//clearcurrent......
  • 基于Springboot+Vue的在线考试系统 (含源码数据库)
    1.开发环境开发系统:Windows10/11架构模式:MVC/前后端分离JDK版本:JavaJDK1.8开发工具:IDEA数据库版本:mysql5.7或8.0数据库可视化工具:navicat服务器:SpringBoot自带apachetomcat主要技术:Java,Springboot,mybatis,mysql,vue2.视频演示地址3.功能该系统......
  • 基于Nodejs+Vue的游戏点单陪玩系统 (含源码数据库)
    1.开发环境开发系统:Windows10/11架构模式:MVC/前后端分离JDK版本:JavaJDK1.8开发工具:IDEA数据库版本:mysql5.7或8.0数据库可视化工具:navicat服务器:SpringBoot自带apachetomcat主要技术:Java,Springboot,mybatis,mysql,vue2.视频演示地址3.功能该系统......
  • 基于Springboot+Vue的汽车销售系统 (含源码数据库)
    1.开发环境开发系统:Windows10/11架构模式:MVC/前后端分离JDK版本:JavaJDK1.8开发工具:IDEA数据库版本:mysql5.7或8.0数据库可视化工具:navicat服务器:SpringBoot自带apachetomcat主要技术:Java,Springboot,mybatis,mysql,vue2.视频演示地址3.功能这个系......
  • SpringBoot3+Vue3+AntDesign电商后台管理系统 | 小蚂蚁云
     项目介绍基于SpringBoot3、SpringSecurity、MybatisPlus、Vue3、TypeScript、Vite、AntDesign、MySQL等技术栈实现的单体前后端分离后台管理系统;后端基于Java语言采用SpringBoot3、SpringSecurity、MybatisPlus、MySQL等主流技术栈,前端基于Vue3、TypeScript、Vite等技术栈实......
  • SpringBoot3+Vue3+AntDesign单体多模块项目实践 | 小蚂蚁云
     项目介绍基于SpringBoot3、SpringSecurity、MybatisPlus、Vue3、TypeScript、Vite、AntDesign、MySQL等技术栈实现的单体前后端分离后台管理系统;后端基于Java语言采用SpringBoot3、SpringSecurity、MybatisPlus、MySQL等主流技术栈,前端基于Vue3、TypeScript、Vite等技术栈实......