mousedown拖拽功能(vue3+ts)

news2024/11/24 23:42:54

因为项目有rem适配,使用第三方插件无法处理适配问题,所有只能自己写拖拽功能了
拖拽一般都会想到按下,移动,放开,但是本人亲测,就在div绑定一个按下事件就行了(在事件里面写另外两个事件),另外两个绑上,会莫名其妙卡死,那种莫名其妙的问题

推荐几个开发调试时使用的第三方拖动插件吧,虽然没用上,但是他们是真的好vue-drag-resizevuedraggable,其中前者更轻量化,后者功能更全

主要功能:

效果图
在这里插入图片描述

界面:(就是大的父盒子包着几个小盒子,盒子里面有图片和文字)

        <div class="range" id="range" ref="range">
          <div
            class="iconItem"
            v-for="(item, index) in pointList"
            :key="index"
            @mousedown.stop.prevent.native="mousedown($event, item)"
            :style="{
              left: item.dx + 'px',
              top: item.dy + 'px',
              'z-index': item.zIndex,
            }"
          >
            <!--
            @mousemove.stop.prevent.native="mousemove($event, item)"
            @mouseup.stop.prevent.native="mouseup($event, item)"
             -->
            <img
              draggable="false"
              :src="typeList[item.type].src"
              :alt="typeList[item.type].name + item.EName"
            />
            <span>{{ typeList[item.type].name + item.EName }}</span>
          </div>
        </div>

逻辑

<script setup lang="ts">
import { ref, reactive, watch, computed, Ref } from "vue";
import { mapPunctuation } from "@/utils/youran";
let rem = ref(0.005208); // 10/1920  做好功能给上面的left top乘上去就行了 left: item.dx * rem + 'px'

const range: Ref = ref(null);

// 这里只是把存在文件里的base64图片文件取出来,
let typeList = reactive([
  {
    type: 1,
    src: "",
    name: "球机-摄像头",
  },
  {
    type: 2,
    src: "",
    name: "抢机-摄像头",
  },
  {
    type: 3,
    src: "",
    name: "无源打卡设备",
  },
  {
    type: 4,
    src: "",
    name: "无源打卡设备",
  },
  {
    type: 5,
    src: "",
    name: "反向控制",
  },
]);

typeList.forEach((item, index) => {
  item.src = mapPunctuation[index].src;
});

let pointList = ref([
  {
    fId: "111",
    type: 1,
    EId: "",
    EName: "",
    dx: 0,
    dy: 0,
    zIndex: 2,
  },
]);

// 鼠标事件
let downType = ref(false);
let disX = 0;
let disY = 0;
let odiv: any = null;
let mousedown = (e: any, item: any) => {
  downType.value = true;
  console.log("按下事件");
  odiv = e.target;
  disX = e.clientX - odiv.offsetLeft;
  disY = e.clientY - odiv.offsetTop;

  document.onmousemove = (e) => {
    console.log("移动事件");
    //计算元素位置(需要判断临界值)
    let left = e.clientX - disX;
    let top = e.clientY - disY;
    let { offsetHeight: pondModelHeight, offsetWidth: pondModelWidth } =
      range.value;
    let { offsetHeight: sonNodeHeight, offsetWidth: sonNodeWidth } = odiv;
    // 左上角(left)
    if (left < 0) {
      left = 0;
    }
    if (top < 0) {
      top = 0;
    }
    // 左下角
    if (top > pondModelHeight - sonNodeHeight) {
      top = pondModelHeight - sonNodeHeight;
    }
    if (left > pondModelWidth - sonNodeWidth) {
      left = pondModelWidth - sonNodeWidth;
    }
    item.dx = left;
    item.dy = top;
    item.zIndex = 999;
  };
  document.onmouseup = (e) => {
    console.log("放开事件");
    document.onmousemove = null;
    document.onmouseup = null;
    item.zIndex = 1;
    odiv = null;
  };
};
</script>

css:本来不该放出来,但是我在这里踩坑了,觉得其他人也会(img图片有默认的拖拽,很难禁止,所以拿一个伪元素直接放在img上面,不给点img就不会踩坑)

      .range {
        width: 960px;
        height: 540px;
        background-color: pink;
        position: relative;
        .iconItem {
          position: absolute;
          left: 10px;
          top: 10px;
          z-index: 2;
          display: flex;
          align-items: center;
          cursor: move;
          user-select: none;
          width: 32px;
          height: 32px;
          background: yellow;
          img {
            width: 32px;
            height: 32px;
          }
          // 关键
          &::before {
            content: " ";
            width: 100%;
            height: 100%;
            position: absolute;
            top: 0;
            left: 0;
            z-index: 3;
          }
          &:hover {
            // span {
            //   display: block;
            // }
          }
          span {
            display: none;
            font-size: 12px;
            font-family: YouSheBiaoTiHei;
            color: red;
          }
        }
      }

完整代码:(建议按照上面的一点点复制吧,有几个文件是外部的base64图片)

<template>
  <div class="PastureMap">
    <div class="mapContent">
      <div class="mapBox">
        <div class="range" id="range" ref="range">
          <div
            class="iconItem"
            v-for="(item, index) in pointList"
            :key="index"
            @mousedown.stop.prevent.native="mousedown($event, item)"
            :style="{
              left: item.dx + 'px',
              top: item.dy + 'px',
              'z-index': item.zIndex,
            }"
          >
            <!--
            @mousemove.stop.prevent.native="mousemove($event, item)"
            @mouseup.stop.prevent.native="mouseup($event, item)"
             -->
            <img
              draggable="false"
              :src="typeList[item.type].src"
              :alt="typeList[item.type].name + item.EName"
            />
            <span>{{ typeList[item.type].name + item.EName }}</span>
          </div>
        </div>
      </div>
      <div class="operationPanel">
        <div class="addIConCard">
          <div class="title">
            <span>新增图标</span>
          </div>
          <div class="box">
            <div class="bgImg">
              <div class="left">
                <span>背景图:</span>
              </div>
              <div class="right">
                <button>选择图片</button>
                <span>建议尺寸:960*540</span>
              </div>
            </div>
            <div class="iconBtnForm">
              <div class="cell">
                <div class="left">
                  <span>圈舍</span>
                </div>
                <div class="right">
                  <input type="text" placeholder="请选择圈舍" />
                </div>
              </div>
              <div class="cell">
                <div class="left">
                  <span>设备编号</span>
                </div>
                <div class="right">
                  <input type="text" placeholder="请输入设备编号" />
                </div>
              </div>
              <div class="cell">
                <div class="left">
                  <span>类型</span>
                </div>
                <div class="right">
                  <input type="text" placeholder="请选择类型" />
                </div>
              </div>
            </div>
            <div class="addBtn">
              <button>新增</button>
            </div>
          </div>
        </div>
        <div class="iconList">
          <div class="item" v-for="(item, index) in pointList" :key="index">
            <div class="left">
              <span>类型</span>
            </div>
            <div class="right">
              <input type="text" placeholder="名称" />
            </div>
            <div class="del">
              <img src="" alt="del" />
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, watch, computed, Ref } from "vue";
import { mapPunctuation } from "@/utils/youran";
let rem = ref(0.005208); // 10/1920

const range: Ref = ref(null);
let typeList = reactive([
  {
    type: 1,
    src: "",
    name: "球机-摄像头",
  },
  {
    type: 2,
    src: "",
    name: "抢机-摄像头",
  },
  {
    type: 3,
    src: "",
    name: "无源打卡设备",
  },
  {
    type: 4,
    src: "",
    name: "无源打卡设备",
  },
  {
    type: 5,
    src: "",
    name: "反向控制",
  },
]);

typeList.forEach((item, index) => {
  item.src = mapPunctuation[index].src;
});

let pointList = ref([
  {
    fId: "111",
    type: 1,
    EId: "",
    EName: "",
    dx: 0,
    dy: 0,
    zIndex: 2,
  },
]);

// 鼠标事件
let downType = ref(false);
let disX = 0;
let disY = 0;
let odiv: any = null;
let mousedown = (e: any, item: any) => {
  downType.value = true;
  console.log("按下事件");
  odiv = e.target;
  disX = e.clientX - odiv.offsetLeft;
  disY = e.clientY - odiv.offsetTop;

  document.onmousemove = (e) => {
    console.log("移动事件");
    //计算元素位置(需要判断临界值)
    let left = e.clientX - disX;
    let top = e.clientY - disY;
    let { offsetHeight: pondModelHeight, offsetWidth: pondModelWidth } =
      range.value;
    let { offsetHeight: sonNodeHeight, offsetWidth: sonNodeWidth } = odiv;
    // 左上角(left)
    if (left < 0) {
      left = 0;
    }
    if (top < 0) {
      top = 0;
    }
    // 左下角
    if (top > pondModelHeight - sonNodeHeight) {
      top = pondModelHeight - sonNodeHeight;
    }
    if (left > pondModelWidth - sonNodeWidth) {
      left = pondModelWidth - sonNodeWidth;
    }
    item.dx = left;
    item.dy = top;
    item.zIndex = 999;
  };
  document.onmouseup = (e) => {
    console.log("放开事件");
    document.onmousemove = null;
    document.onmouseup = null;
    item.zIndex = 1;
    odiv = null;
  };
};
</script>

<style lang="less" scoped>
.PastureMap {
  height: 100%;
  .mapContent {
    display: flex;
    height: 100%;
    .mapBox {
      flex: 1;
      height: 100%;
      .range {
        width: 960px;
        height: 540px;
        background-color: pink;
        position: relative;
        .iconItem {
          position: absolute;
          left: 10px;
          top: 10px;
          z-index: 2;
          display: flex;
          align-items: center;
          cursor: move;
          user-select: none;
          width: 32px;
          height: 32px;
          background: yellow;
          img {
            width: 32px;
            height: 32px;
          }
          &::before {
            content: " ";
            width: 100%;
            height: 100%;
            position: absolute;
            top: 0;
            left: 0;
            z-index: 3;
          }
          &:hover {
            // span {
            //   display: block;
            // }
          }
          span {
            display: none;
            font-size: 12px;
            font-family: YouSheBiaoTiHei;
            color: red;
          }
        }
      }
    }

    .operationPanel {
      width: 270px;
      .addIConCard {
        .title {
          span {
          }
        }
        .box {
          .bgImg {
            display: flex;
            align-items: center;
            .left {
            }
            .right {
            }
          }
          .iconBtnForm {
            .cell {
              display: flex;
              align-items: center;
              .left {
                span {
                }
              }
              .right {
                input {
                }
              }
            }
          }
        }
      }
      .iconList {
        .item {
          display: flex;
          align-items: center;
          position: relative;
          .left {
            span {
            }
          }
          .right {
            input {
            }
          }
          .del {
            position: absolute;
            top: 0;
            right: 0;
          }
        }
      }
    }
  }
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/849482.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端架构师的具体职责范围(合集)

前端架构师的具体职责范围1 职责&#xff1a; 1、前端技术选型、架构搭建、制定前端开发规范&#xff0c;并编制相关文档 2、负责搭建前端框架、通用组件方案制定、性能优化相关工作; 3、维护和升级本地开发环境&#xff0c;提高开发效率&#xff0c;提升开发质量; 4、推动…

【深度学习注意力机制系列】—— ECSKNet注意力机制(附pytorch实现)

SKNet&#xff08;Selective Kernel Network&#xff09;是一种用于图像分类和目标检测等任务的深度神经网络架构&#xff0c;其核心创新是引入了选择性的多尺度卷积核&#xff08;Selective Kernel&#xff09;以及一种新颖的注意力机制&#xff0c;从而在不增加网络复杂性的情…

2.安装Docker-ce

一、删除之前安装的docker(若之前未安装过&#xff0c;此步骤省略…) 进入centos根目录执行以下命令&#xff08;\ 是linux系统种命令换行符&#xff0c;如果命令过长&#xff0c;可以用\来换行&#xff09; yum remove docker \ docker-client \ docker-client-latest \ doc…

BL302嵌入式ARM控制器进行SQLite3数据库操作的实例演示

本文主要讲述了在钡铼技术BL302嵌入式arm控制器上运行 SQLite3 数据库的命令示例。SQLite3 是一个轻型的嵌入式数据库&#xff0c;不需要安装数据库服务器进程&#xff0c;占用资源低且处理速度快。 首先&#xff0c;需要将对应版本的 SQLite3 文件复制到设备的 /usr/ 目录下&…

MyCat管理及监控——zookeeper及MyCat-web安装

1.MyCat管理 2.MyCat-eye 3.zookeeper安装 第一步&#xff1a;解压 第二部&#xff1a; 切换目录&#xff0c;创建data文件夹 第三步&#xff1a;修改zookeeper配置文件 这样zookeeper安装及配置就完成了 4.MyCat-web安装 注意mycat-web要与zookeeper关联&#xff0c;…

单元测试到底是什么?应该怎么做?

一、什么是单元测试&#xff1f; 单元测试&#xff08;unit testing&#xff09;&#xff0c;是指对软件中的最小可测试单元进行检查和验证。至于“单元”的大小或范围&#xff0c;并没有一个明确的标准&#xff0c;“单元”可以是一个函数、方法、类、功能模块或者子系统。单…

Kafka3.0.0版本——Broker(上下线)示例

目录 一、Broker&#xff08;上下线&#xff09;示例1.1、三台服务器信息1.2、先启动zookeeper集群&#xff0c;再启动kafka集群1.3、zookeeper客户端工具prettyZoo查看brokers中ids1.4、停止某一台kafka服务&#xff0c;再次查看brokers中ids1.5、重新启动停止的kafka服务&…

笔记——听听前辈们的教学评一体化

精选课程内容 强而有力的知识 做中学&#xff0c;用中学&#xff0c;创中学。 这个技术很难做 关于支架的新理解 有价值 有意义 和 趣味性 权衡&#xff0c;不能为了趣味性舍弃价值 举例说明文 被教成了文学作品 导致所教所学 悄然发生了偏移。 所以教学评如何一直&#xff…

使用Beautiful Soup等三种方式定制Jmeter测试脚本

目录 背景介绍 实现思路 把脚本数据读出&#xff0c;使用正则表达式&#xff08;re库&#xff09;匹配关键数据进行修改 把脚本数据读出&#xff0c;使用BeautifulSoup的xml解析功能解析后修改 通过Beautiful Soup Beautiful Soup 具体实现 使用string.Template字符替换…

Jupyter Notebook 遇上 NebulaGraph,可视化探索图数据库

在之前的《手把手教你用 NebulaGraph AI 全家桶跑图算法》中&#xff0c;除了介绍了 ngai 这个小工具之外&#xff0c;还提到了一件事有了 Jupyter Notebook 插件: https://github.com/wey-gu/ipython-ngql&#xff0c;可以更便捷地操作 NebulaGraph。 本文就手把手教你咋在 J…

BpBinder与PPBinder调用过程——Android开发Binder IPC通信技术

在Android系统中&#xff0c;进程间通信&#xff08;IPC&#xff09;是一个非常重要的话题。Android系统通过Binder IPC机制实现进程间通信&#xff0c;而Binder IPC通信技术则是Android系统中最为重要的进程间通信技术之一。本文将介绍Binder IPC通信技术的原理&#xff0c;并…

实测有效Window10系统解决文件名过长无法删除或移动问题

问题&#xff1a;window10家庭版&#xff0c;文件名字太长无法对其进行操作 如图 PS&#xff1a;什么注册表方法&#xff0c;压缩方法都没效果 解决&#xff1a; 打开 注册编辑器 进入路径 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem 修改 LongP…

Jenkins 修改默认管理员帐号

1、新增一个新的超级管理员用户&#xff0c;并验证能正常登录 2、进入 Jenkins 用户管理目录&#xff1a; /data/software/jenkins/users 3、修改超级管理文件夹的名称为其他名称&#xff0c;如&#xff1a;mv admin_*** ifadm_*** 4、重启Jenkins容器

snowboy+sherpa-onnx+Rasa+Coqui实现语音音箱【语音助手】

背景 本系列主要目标初步完成一款智能音箱的基础功能&#xff0c;包括语音唤醒、语音识别(语音转文字)、处理用户请求&#xff08;比如查天气等&#xff0c;主要通过rasa自己定义意图实现&#xff09;、语音合成(文字转语音)功能。 coqui主要在项目中完成接收rasa响应的内容&…

Postgresql源码(111)dms框架进程信号发送与处理流程

信号处理整体流程 信号从bgworker发出后&#xff0c;主进程将ParallelMessagePending置为true&#xff0c;下次CHECK_FOR_INTERRUPTS()时&#xff0c;会进入信号处理逻辑中&#xff1a;HandleParallelMessages。进入信号处理逻辑后&#xff0c;首先遍历所有现存的ParallelCont…

护网专题简单介绍

护网专题简单介绍 一、护网红蓝队介绍1.1、网络安全大事件1.2、护网行动由来1.3、护网行动中的角色二、红队介绍2.1、红队所需技能2.2、红队攻击流程 三、蓝队介绍3.1、蓝队所需技能3.2、蓝队防守四阶段3.3、蓝队前期准备 四、常见安全厂商介绍4.1、常见安全厂商 五、常见安全产…

Softing工业获得自动化产品安全开发流程认证

Softing工业获得了TV Sd颁发的IEC 62443-4-1产品安全开发流程认证。 &#xff08;IEC 62443-4-1认证确保网络安全&#xff09; 截至2023年6月&#xff0c;位于德国哈尔和纽伦堡的工厂以及罗马尼亚克卢日的Softing工业研发部门已获得IEC 62443-4-1:2018标准的认证。该认证流程由…

Ajax-概念、Http协议、Ajax请求及其常见问题

Ajax Ajax概念Ajax优缺点HTTP协议请求报文响应报文 Ajax案例准备工作express基本使用创建一个服务器 发送AJAX请求GET请求POST请求JSON响应 Ajax请求出现的问题IE缓存问题Ajax请求超时与网络异常处理Ajax手动取消请求Ajax重复发送请求问题 Ajax概念 AJAX 全称为Asynchronous J…

《孙子兵法》快速概览,有哪些章节?趣讲《孙子兵法》【第2讲】

《孙子兵法》快速概览&#xff0c;有哪些章节&#xff1f;趣讲《孙子兵法》【第2讲】 《孙子兵法》十一家注是一个有名的版本&#xff0c;十一家注是曹操、杜牧等十一人注释&#xff0c;曹操是真正的军事家&#xff0c;是名副其实的大咖。总共三卷十三篇&#xff0c;比较难记住…

Unity 3D中使用tilemap创建关卡地图,瓦片间隙有漏缝

我们使用一张图片来作为Sprite图集&#xff0c;创建地形图&#xff1a; 运行后&#xff0c;会发现&#xff0c;瓦片之间似乎总是有间距。 检查了图片发现&#xff0c;并不是图片边界存在间隙。 最后发现问题是出在图片资源中的线性过滤属性值&#xff1a; 在设计界面就能够看…