vue3实现鼠标左键拖拽画矩形框框选功能

2023-11-15

vue3 + elementuiPlus 实现鼠标左键拖拽画矩形框 框选列表功能,仿照桌面框选功能
效果如图:

vue3鼠标框选

代码:

<template>
    <div class="box-img"  @mousedown="handleMouseDown">
        <div class="mask" v-show="positionList.is_show_mask" :style="'width:' + mask_width + 'left:' + mask_left + 'height:' + mask_height + 'top:' + mask_top"></div>
        <el-checkbox-group v-model="selectedState" class="checkboxGroup">
            <el-checkbox v-for="(item, index)  in tableData" :key="index"  :label="item.id" :class="selectedState.includes(item.id) ? 'checkbox aa-active' : 'checkbox'" @change="checkItem($event,item)">
                <img style="width: 100% ;height: 100%" :src="handleAssetsImages(item.img)" alt="">
                <span class="num" v-show="selectedState.includes(item.id)">{{(selectedState.indexOf(item.id) + 1)}}</span>
            </el-checkbox>
        </el-checkbox-group>
    </div>
</template>

<script lang="ts" setup>
    import {ref,reactive,onMounted,computed} from 'vue'

    // 假设这个是上边小div的列表,后台数据请求回来的吗,假设已经有数据了,里面有唯一标识符id
    const tableData = reactive([
        {id: 'abcd1',  data:'cccc', img: '1.png'},
        {id: 'abcd2',  data:'dddd', img: '1.png'},
        {id: 'abcd3',  data:'eeee', img: '1.png'},
        {id: 'abcd4',  data:'ffff', img: '1.png'},
        {id: 'abcd5',  data:'gggg', img: '1.png'},
        {id: 'abcd6',  data:'hhhh', img: '1.png'},
        {id: 'abcd7',  data:'iiii', img: '1.png'},
        {id: 'abcd8',  data:'jjjj', img: '1.png'},
        {id: 'abcd9',  data:'kkkk', img: '1.png'},
        {id: 'abcd10', data:'llll', img: '1.png'}
    ])

    const selectedState = ref([]);// 上方div图片是否被多选中,如果多选了就把id放到这个列表里面,当然可以不用,直接在相应的列表对象加一个参数isSelected也行

    const positionList = reactive({
        is_show_mask: false,
        box_screen_left: 0,
        box_screen_top: 0,
        start_x: 0,
        start_y: 0,
        end_x: 0,
        end_y: 0
    })
    //分别计算遮罩层的位置,大小
    const  mask_width = computed(()=> {
        return `${Math.abs(positionList.end_x - positionList.start_x)}px;`;
    })
    const  mask_height = computed(()=> {
        return `${Math.abs(positionList.end_y - positionList.start_y)}px;`;
    })
    const mask_left = computed(() =>{
        return `${Math.min(positionList.start_x, positionList.end_x) - positionList.box_screen_left}px;`;
    })
    const mask_top = computed(()=> {
        return `${Math.min(positionList.start_y, positionList.end_y) - positionList.box_screen_top}px;`;
    })

    //循环动态加载图片
    const handleAssetsImages=(name)=>{
        return new URL(`/src/assets/img/${name}`, import.meta.url).href;
    }
    //单击多选框选中
    const checkItem = (val,item)=>{
        console.log(val,item);

        let str = item.id ;
        let i = selectedState.value.indexOf(str)  // 判断选中列表中是否包含这个点击的div
        if (i < 0) {
            selectedState.value.push(str) ;  // 如果不包含就加进去
        } else {
            selectedState.value.splice(i, 1);  // 如果包含就删
        }
    }



    //鼠标按下事件
    const  handleMouseDown = (event)=> {
        positionList.is_show_mask = true;
        positionList.start_x = event.clientX;
        positionList.start_y = event.clientY;
        positionList.end_x = event.clientX;
        positionList.end_y = event.clientY;
        document.body.addEventListener("mousemove", handleMouseMove); //监听鼠标移动事件
        document.body.addEventListener("mouseup", handleMouseUp); //监听鼠标抬起事件
    }

    function  handleMouseMove(event) {
        positionList.end_x = event.clientX;
        positionList.end_y = event.clientY;
    }

    function handleMouseUp() {
        document.body.removeEventListener("mousemove", handleMouseMove);
        document.body.removeEventListener("mouseup", handleMouseUp);
        positionList.is_show_mask = false;
        handleDomSelect();
        resSetXY();
    }

    function handleDomSelect() {
        const dom_mask = window.document.querySelector(".mask");
        //getClientRects()每一个盒子的边界矩形的矩形集合
        const rect_select = dom_mask.getClientRects()[0];
        console.log(rect_select);
        const add_list = [];
        const del_list = [];
        document.querySelectorAll(".el-checkbox-group .el-checkbox").forEach((node, index) => {
            const rects = node.getClientRects()[0];
            if (collide(rects, rect_select) === true) {
                if (selectedState.value.includes(tableData[index].id)) {
                    del_list.push(tableData[index].id);
                } else {
                    add_list.push(tableData[index].id);
                }
            }
        });
        selectedState.value = selectedState.value.concat(add_list).filter((item) => !del_list.includes(item));
    }
    //比较checkbox盒子边界和遮罩层边界最大最小值
    function  collide(rect1, rect2) {
        const maxX = Math.max(rect1.x + rect1.width, rect2.x + rect2.width);
        const maxY = Math.max(rect1.y + rect1.height, rect2.y + rect2.height);
        const minX = Math.min(rect1.x, rect2.x);
        const minY = Math.min(rect1.y, rect2.y);
        return maxX - minX <= rect1.width + rect2.width && maxY - minY <= rect1.height + rect2.height;
    }

    //清除
    function  resSetXY() {
        positionList.start_x = 0;
        positionList.start_y = 0;
        positionList.end_x = 0;
        positionList.end_y = 0;
    }

</script>
<style lang="less">
    .box-img{
        display: flex;
        align-items: center;
        justify-content: space-around;
        position: relative;
        height: 100%;
        width: 100%;

        .mask {
            position: absolute;
            background: #409eff;
            opacity: 0.4;
            z-index: 100;
        }
    }
    .checkboxGroup{
        display: flex;
        align-items: center;
        flex-wrap: wrap;

        .el-checkbox{
            margin:10px;
        }
        .el-checkbox__input {
            white-space: nowrap;
            cursor: pointer;
            outline: 0;
            display: inline-flex;
            position: absolute;
            right: 5px;
            top: 5px;
        }

        .el-checkbox__label{
            padding:0;
            width: 100%;
            height: 100%;
        }
        .el-checkbox__input .el-checkbox__inner:after{
            border-color: #00ffff;
        }
        .el-checkbox__input.is-checked .el-checkbox__inner{
            border: 1px solid #00ffff;
        }
        .el-checkbox__inner{
            border: 1px solid #fff;
        }
    }
    .checkbox{
        border: 1px solid #999;
        width: 100px;
        height: 100px;
        display: flex;
        align-items: center;
        justify-content: space-around;
        position: relative;
    }
    .checkbox-active{
        border: 1px solid #00ffff;
    }
    .num{
        position: absolute;
        right: 25px;
        top: 5px;
        color: #00ffff;

    }
</style>


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vue3实现鼠标左键拖拽画矩形框框选功能 的相关文章

随机推荐

  • 幻想乡的日常【树状数组+离线操作】

    题目链接 给出N个点的树 编号为1 N 每次的查询为 L R 想知道编号在 L R 内的所有的结点的会被分成多少个连通块 给出一条性质 连通块数量 点数 边数 点数很方便的可以计算 就是 R L 1 那么 如何计算边数呢 我们知道 每条边有
  • 这届世界杯是不是让你出乎意料?写个足球小游戏来模拟一下!

    前言 今年世界杯有人欢喜有人愁 我想愁的人应该居多 不得不说 小日本是真菜啊 特么的 今天还是搞点咱们好玩点的 世界杯嘛 大家看看就行 大家不是都说 看国足比看相声还搞笑吗 好了 不笑了 今天给大家带来一款非常简单的足球小游戏 希望大家喜欢
  • 什么是分布式软件系统

    什么是分布式软件系统 分布式软件系统是什么意思 分布式软件系统 Distributed Software Systems 是支持分布式处理的软件系统 是在由通信网络互联的多处理机体系结构上执行任务的系统 它包括分布式操作系统 分布式程序设计
  • 精灵标注助手的安装及使用

    位置标注 分割标注 官网下载安装包 http www jinglingbiaozhu com 安装超简单 位置标注 新建 gt 位置标注 gt 选择图片文件夹 定义分类值 用英文逗号隔开 然后 创建 右下角的 可以对图片进行放大 缩小 选择
  • jenkins配置publish over ssh遇到的问题

    一 背景 目标 本篇文章主要是说明自己在配置jenkins的publish over ssh插件所遇到的问题 本次主要是windows下的jenkins通过ssh的方式访问我本地虚拟机的ubuntu系统 准备 1 在jenkins上安装pu
  • [python] Python类型提示指北

    Python3 5 版本引入了类型提示 Type Hints 它允许开发者在代码中显式地声明变量 函数 方法等的类型信息 这种类型声明不会影响 Python 解释器的运行 但可以让 IDE 和静态分析工具更好地理解代码 同时提高代码的可读性
  • MySQL——SQL注入问题

    文章目录 1 SQL注入问题 2 PreparedStatement对象 1 SQL注入问题 SQL存在漏洞 会被攻击导致数据泄露 2 PreparedStatement对象 PreparedStatement 可以防止SQL注入 效率更好
  • vue使用文件流和url下载文件

    改为使用后台返回url下载文件 方法1 这个会导致在点击下载按钮的时候 页面会跳转到奇怪的url window location href row downloadUrl 方法2 点击下载按钮 不会在新窗口打开 const download
  • 刷脸支付算法和硬件不断升级消费更有保障

    刷脸支付设备依靠3D传感摄像头进行人脸识别 其内置的点阵投影仪可以投射出3万多个肉眼不可见的红外点到用户脸部 多维度 多角度在颜色 纹理 深度等数据进行高层次对比 安全性和精准性更高 识别速度更快 尽管现在刷脸支付的安全性已经达到极高的金融
  • 【数据结构与算法】栈的实现(附源码)

    目录 一 栈的概念和结构 二 接口实现 A 初始化 Stackinit 销毁 Stackdestroy 1 Stackinit 2 Stackdestroy B 插入 Stackpush 删除 Stackpop 1 Stackpush 2
  • 接口未正确配置:wx.getLocation(暂无权限)

    原因 腾讯地理位置接口新增与相关流程 地理位置接口新增说明 由于精确地理位置接口只允许部分类目的小程序申请使用 为了满足开发者在更多场景使用地理位置接口 自 2022 年 7 月 14 日起 新增获取模糊地理位置接口 wx getFuzzy
  • 计算机网络第六章——应用层(下)

    等闲变却故人心 却道故人心易变 文章目录 用户代理就是用户和电子邮件系统之间的一个接口 通常都是运行在电脑中的一个程序 用户代理又可以称为电子邮件客户端软件 用户代理可以为用户提供一个比较友好的接口 邮件服务器作为一个服务器就需要长时间的工
  • 责任链模式(Chain of Responsibility) Java实现

    责任链模式 责任链模式 Chain of Responsibility 定义 责任链模式是一种对象的行为模式 在责任链模式里 很多对象由每一个对象对其下家的引用而连接起来形成一条链 请求在这个链上传递 直到链上的某一个对象决定处理此请求 发
  • 以太网(Ethernet)相关基础知识

    最近正好在学习以太网 感觉非常有用 进行一个总结 欢迎指正 如今 以太网已在现实中大量使用 低廉的价格和较快的速度都是它从许多网络中存活下来的因素 学校 公司中大多用得都是以太网 目录 以太网电缆 Ethernet Cabling 曼彻斯特
  • 移动端点击(click)事件延迟问题的解决方法

    移动端 click 事件会有 300ms 的延时 原因是移动端屏幕双击会缩放 double tap to zoom 页面 解决方案 1 禁用缩放 浏览器禁用默认的双击缩放行为并且去掉300ms 的点击延迟 2 利用touch事件自己封装这个
  • (mac)配置vue

    安装参考 https www jianshu com p cc722eba1f46 1 安装brew 一个安装 卸载软件的程序 https blog csdn net poppy rain article details 88406390
  • Java面试题第一季学习笔记

    Java面试题第一季 1 自增变量 2 单例设计 2 1 什么是Singleton 2 2 代码示例 3 类初始化 3 1 代码 3 2 考点 3 3 Override 重写 和Overload 重载 区别 4 方法的传递机制 4 1 代码
  • java 反射中的method.invoke()方法详解

    public class TestReflect public static void main String args String names tom tim allen alice Class
  • java关于ArrayList,Vector,LinkedList,Set及其面试题+LeetCode136两种方式实现

    ArrayList ArrayList的遍历补充 将list转换为数组 使用toArray 方法将列表转换为数组 再对数组进行遍历 Test void test01 List
  • vue3实现鼠标左键拖拽画矩形框框选功能

    vue3 elementuiPlus 实现鼠标左键拖拽画矩形框 框选列表功能 仿照桌面框选功能 效果如图 vue3鼠标框选 代码