js实现鼠标拖拽多选功能

实现功能
在PC端的H5页面中,客户拖动鼠标可以连选多个选项

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

具体代码如下

<!DOCTYPE html>
<html><head><title>鼠标拖拽多选功能</title><script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script><style type="text/css">* {box-sizing: border-box;}ul {width: 500px;height: auto;margin: 0;padding: 20px;font-size: 0;/*需设置定位*/position: relative;}li {width: 70px;height: 70px;padding: 0;display: inline-block;vertical-align: top;font-size: 13px;border: 1px solid #d9d9d9;}#moveSelected {position: absolute;background-color: blue;opacity: 0.3;border: 1px dashed #d9d9d9;top: 0;left: 0;}.selected {background-color: pink;}</style>
</head><body><ul class="list"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><!-- 鼠标拖拽出的遮罩 (定位为  position:absolute)--><!-- 遮罩最好是在绑定了mouseover事件的元素内部,并且不要阻止遮罩的冒泡事件。这样鼠标移到了遮罩上面,依然可以利用冒泡执行父元素的mouseover事件,就不会出现遮罩只能扩大,不能缩小的情况了(亲自试过) --><div id="moveSelected"></div></ul>
</body></html>
<script type="text/javascript">$(document).ready(function () {let moveSelected = $('#moveSelected')[0];console.log(moveSelected);let flag = false; //是搜开启拖拽的标志let oldLeft = 0; //鼠标按下时的left,toplet oldTop = 0;let selectedList = []; //拖拽多选选中的块集合// 鼠标按下时开启拖拽多选,将遮罩定位并展现$(".list").mousedown(function (event) {flag = true;moveSelected.style.top = event.pageY + 'px';moveSelected.style.left = event.pageX + 'px';oldLeft = event.pageX;oldTop = event.pageY;event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡});// 鼠标移动时计算遮罩的位置,宽 高$(".list").mousemove(function (event) {if (!flag) return; //只有开启了拖拽,才进行mouseover操作if (event.pageX < oldLeft) { //向左拖moveSelected.style.left = event.pageX + 'px';moveSelected.style.width = (oldLeft - event.pageX) + 'px';} else {moveSelected.style.width = (event.pageX - oldLeft) + 'px';}if (event.pageY < oldTop) { //向上moveSelected.style.top = event.pageY + 'px';moveSelected.style.height = (oldTop - event.pageY) + 'px';} else {moveSelected.style.height = (event.pageY - oldTop) + 'px';}event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡});//鼠标抬起时计算遮罩的right 和 bottom,找出遮罩覆盖的块,关闭拖拽选中开关,清除遮罩数据$(".list").mouseup(function (event) {moveSelected.style.bottom = Number(moveSelected.style.top.split('px')[0]) + Number(moveSelected.style.height.split('px')[0]) + 'px';moveSelected.style.right = Number(moveSelected.style.left.split('px')[0]) + Number(moveSelected.style.width.split('px')[0]) + 'px';findSelected();flag = false;clearDragData();event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡});$(".list").mouseleave(function (event) {flag = false;moveSelected.style.width = 0;moveSelected.style.height = 0;moveSelected.style.top = 0;moveSelected.style.left = 0;event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡});function findSelected() {let blockList = $('.list').find('li');for (let i = 0; i < blockList.length; i++) {//计算每个块的定位信息let left = $(blockList[i]).offset().left;let right = $(blockList[i]).width() + left;let top = $(blockList[i]).offset().top;let bottom = $(blockList[i]).height() + top;let leftTwo = moveSelected.style.left.split('px')[0]let rightTwo = moveSelected.style.right.split('px')[0]let topTwo = moveSelected.style.top.split('px')[0]let bottomTwo = moveSelected.style.bottom.split('px')[0]// 判断碰撞if (!(top > bottomTwo || right < leftTwo || bottom < topTwo || left> rightTwo)) {// 碰撞的情况selectedList.push(blockList[i]);$(blockList[i]).addClass('selected');}}console.log(selectedList);}function clearDragData() {moveSelected.style.width = 0;moveSelected.style.height = 0;moveSelected.style.top = 0;moveSelected.style.left = 0;moveSelected.style.bottom = 0;moveSelected.style.right = 0;}});
</script>

考虑方便使用Vue框架的情况,新增一个Vue 3的参考代码,且如果多选时处于已经选择状态也会取消选择

//
// MutiSelectPage
// mrs-console-ui
//
// Created by gaolailong on 2024/05/23.
// Copyright © 2024 上海复微迅捷数字科技股份有限公司. All rights reserved.
//<template><div class="page"><div class="list" @mousedown="handleMouseDown" @mousemove="handleMouseMove" @mouseup="handleMouseUp"@mouseleave="handleMouseLeave"><template v-for="(item, index) in list" :key="index"><div class="can-select-div" :class="item.selected ? 'selected' : ''" :data-index="index">{{ item.text }}</div></template><!-- 鼠标拖拽出的遮罩 (定位为  position:absolute)--><!-- 遮罩最好是在绑定了mouseover事件的元素内部,并且不要阻止遮罩的冒泡事件。这样鼠标移到了遮罩上面,依然可以利用冒泡执行父元素的mouseover事件,就不会出现遮罩只能扩大,不能缩小的情况了(亲自试过) --><div id="moveSelected"></div></div></div>
</template><script lang='ts'>
import { defineComponent, onMounted, reactive } from 'vue'
export default defineComponent({name: 'MutiSelectPage',setup() {let moveSelected: HTMLElement | null = nullonMounted(() => {moveSelected = document.getElementById('moveSelected') as HTMLElement;})const list: Array<Record<string, unknown>> = reactive([])for (let index = 0; index < 10; index++) {const listItem = {text: index,selected: false}list.push(listItem)}let flag = false; //是搜开启拖拽的标志let oldLeft = 0; //鼠标按下时的left,toplet oldTop = 0;const handleMouseDown = (event: any) => {// 处理鼠标按下事件console.log('鼠标按下');if (!moveSelected) return;flag = true;moveSelected.style.top = event.pageY + 'px';moveSelected.style.left = event.pageX + 'px';oldLeft = event.pageX;oldTop = event.pageY;event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡}const handleMouseMove = (event: any) => {// 处理鼠标移动事件的逻辑if (!moveSelected) return;if (!flag) return; //只有开启了拖拽,才进行mouseover操作console.log("鼠标移动了");if (event.pageX < oldLeft) { //向左拖moveSelected.style.left = event.pageX + 'px';moveSelected.style.width = (oldLeft - event.pageX) + 'px';} else {moveSelected.style.width = (event.pageX - oldLeft) + 'px';}if (event.pageY < oldTop) { //向上moveSelected.style.top = event.pageY + 'px';moveSelected.style.height = (oldTop - event.pageY) + 'px';} else {moveSelected.style.height = (event.pageY - oldTop) + 'px';}event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡}const handleMouseUp = (event: any) => {if (!moveSelected) return;// 处理鼠标弹起事件console.log('鼠标弹起');moveSelected.style.bottom = Number(moveSelected.style.top.split('px')[0]) + Number(moveSelected.style.height.split('px')[0]) + 'px';moveSelected.style.right = Number(moveSelected.style.left.split('px')[0]) + Number(moveSelected.style.width.split('px')[0]) + 'px';findSelected();flag = false;clearDragData();event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡}const handleMouseLeave = (event: any) => {console.log('鼠标离开元素');if (!moveSelected) return;flag = false;moveSelected.style.width = '0';moveSelected.style.height = '0';moveSelected.style.top = '0';moveSelected.style.left = '0';event.preventDefault(); // 阻止默认行为event.stopPropagation(); // 阻止事件冒泡}function findSelected() {if (!moveSelected) return;let leftTwo = Number(moveSelected.style.left.split('px')[0])let rightTwo = Number(moveSelected.style.right.split('px')[0])let topTwo = Number(moveSelected.style.top.split('px')[0])let bottomTwo = Number(moveSelected.style.bottom.split('px')[0])// 使用ref获取dom有一个奇怪的现象,手动切换按天/按周时,ref获取到的值不会丢失之前的数据。除非点击外部的“确认查询”按钮。const blockList = document.getElementsByClassName('can-select-div')// console.log(blockList) // 访问DOM元素for (let i = 0; i < blockList.length; i++) {//计算每个块的定位信息let left = (blockList[i] as HTMLDivElement).offsetLeft;let right = (blockList[i] as HTMLDivElement).offsetWidth + left;let top = (blockList[i] as HTMLDivElement).offsetTop;let bottom = (blockList[i] as HTMLDivElement).offsetHeight + top;// 通过比较两个矩形(用 aDiv 和 bDiv 表示)的位置信息来判断它们是否发生碰撞。以下是该碰撞函数的实现思路:// 首先,通过获取 aDiv 和 bDiv 的位置信息,分别计算它们的上边界(t1 和 t2)、右边界(r1 和 r2)、下边界(b1 和 b2)以及左边界(l1 和 l2)。// 接下来,通过比较这些边界信息,判断两个矩形是否发生碰撞。碰撞的情况可以通过以下四个条件中的任意一个来判断:// 如果矩形 aDiv 的上边界大于矩形 bDiv 的下边界,说明 aDiv 在 bDiv 的上方,没有碰撞。// 如果矩形 aDiv 的右边界小于矩形 bDiv 的左边界,说明 aDiv 在 bDiv 的左侧,没有碰撞。// 如果矩形 aDiv 的下边界小于矩形 bDiv 的上边界,说明 aDiv 在 bDiv 的下方,没有碰撞。// 如果矩形 aDiv 的左边界大于矩形 bDiv 的右边界,说明 aDiv 在 bDiv 的右侧,没有碰撞。// 如果上述条件中的任意一个不满足,那么矩形 aDiv 和 bDiv 就发生了碰撞,函数返回 true,否则返回 false 表示没有碰撞。// 判断碰撞if (!(top > bottomTwo || right < leftTwo || bottom < topTwo || left > rightTwo)) {// 碰撞的情况console.log('碰撞了');const itemIndex = (blockList[i] as HTMLDivElement).dataset.indexif (!itemIndex) {return}const item = list[Number(itemIndex)]item.selected = !item.selectedlist.splice(Number(itemIndex), 1, item)}}}const clearDragData = () => {if (!moveSelected) return;moveSelected.style.width = '0';moveSelected.style.height = '0';moveSelected.style.top = '0';moveSelected.style.left = '0';moveSelected.style.bottom = '0';moveSelected.style.right = '0';}return {handleMouseDown,handleMouseMove,handleMouseUp,handleMouseLeave,list,}}
})
</script><style scoped>
.page {box-sizing: border-box;
}.can-select-div {width: 70px;height: 70px;padding: 0;display: inline-block;vertical-align: top;font-size: 13px;border: 1px solid #d9d9d9;
}#moveSelected {position: absolute;background-color: blue;opacity: 0.3;border: 1px dashed #d9d9d9;top: 0;left: 0;
}.selected {background-color: pink;
}
</style>

参考1、参考2

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ZISUOJ 数据结构--图及其应用

说明 主要考察建图&#xff0c;图的遍历以及求最小生成树。都还是比较简单的&#xff0c;后面就直接上代码了。 最小生成树采用prim还是kruskal算法要看题目怎么给出数据&#xff0c;如果以邻接矩阵的形式给出&#xff0c;采用prim算法比较合适&#xff0c;如果以边和边的权重的…

docker三种自定义网络(虚拟网络) overlay实现原理

docker提供了三种自定义网络驱动&#xff1a;bridge、overlay、macvlan。 bridge驱动类似默认的bridge网络模式。 overlay和macvlan是用于创建跨主机网络。 支持自定义网段、网关&#xff0c;docker network create --subnet 172.77.0.0/24 --gateway 172.77.0.1 my_n…

浅谈JMeter测试计划

浅谈JMeter测试计划 创建测试计划 当启动JMeter后&#xff0c;默认进入界面会看到一个测试计划 测试计划组件详情 在上述界面中&#xff0c;我们可以看到测试计划的组成为名称、注释、用户定义的变量、独立运行每个线程组、主线程结束后运行tearDown线程组、函数测试模式以…

科技查新中查新点的怎样进行精确提炼?

根据2015年《科技查新技术规范》&#xff1a;科技查新简称查新&#xff0c;以反映查新项目主题内容的查新点为依据&#xff0c;以计算机检索为主要手段&#xff0c;以获取密切相关文献为检索目标&#xff0c;运用综合分析和对比方法&#xff0c;对查新项目的新颖性做出文献评价…

JMETER工具:以录制手机app为例

JMETER工具&#xff1a;以录制手机app为例子 JMETER安装和环境配置 pc需要安装jdk&#xff0c;并进行jdk的环境配置&#xff0c;安装好jdk并配置好后&#xff0c;通过命令行输入java –version出现以下界面就表示安装成功&#xff1a; &#xff08;对应的jdk版本不可太低&…

深度学习——图像分类(CNN)—训练模型

训练模型 1.导入必要的库2.定义超参数3.读取训练和测试标签CSV文件4.确保标签是字符串类型5.显示两个数据框的前几行以了解它们的结构6.定义图像处理参数7.创建图像数据生成器8.设置目录路径9.创建训练和验证数据生成器10.构建模型11.编译模型12.训练模型并收集历史13.绘制损失…

excel转pdf并且加水印,利用ByteArrayOutputStream内存流不产生中间文件

首先先引入包&#xff1a;加水印和excel转PDF的 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.12</version></dependency><dependency><groupId>org.apache.poi&l…

jenkins插件之xunit

安装jenkins插件 搜索xunit并安装 项目配置 配置 - Build Steps 您的项目 - 配置 - Build Steps, 新增 Run with timeout 超时时间根据实际情况配置 Build Step选择 执行SHELL 填写一下命令&#xff0c;这个命令是docker中执行phpunit单元测试&#xff0c;请根据你的实际…

FPGA学习笔记之Nios II(一)简单介绍及新建工程及下载

系列文章目录 文章目录 系列文章目录前言QsysNios IIhello world 实例Platform DesignNios II程序设计 前言 利用Quartus中的Qsys工具&#xff0c;可以实现在FPGA里面跑嵌入式的功能 Qsys Altera 公司将主控制器、数字信号处理模块、存储器及其控制模块、各种接口协议等模块&…

亚马逊测评还能做吗?

只能说测评不是唯一的手段&#xff0c;但是推销量的一把好手。首先测评能让listing快速成长&#xff0c;短期内有望成为爆款&#xff0c;速度快&#xff0c;利润高&#xff0c;回款快。相对其他推广&#xff0c;测评无疑是有效&#xff0c;省培养listing的方法。其次新品前期太…

聊聊 JSON Web Token (JWT) 和 jwcrypto 的使用

哈喽大家好&#xff0c;我是咸鱼。 最近写的一个 Python 项目用到了 jwcrypto 这个库&#xff0c;这个库是专门用来处理 JWT 的&#xff0c;JWT 全称是 JSON Web Token &#xff0c;JSON 格式的 Token。 今天就来简单入门一下 JWT。 官方介绍&#xff1a;https://jwt.io/intr…

RH850F1KM-S4-100Pin_ R7F7016453AFP MCAL Gpt 配置

1、Gpt组件包含的子配置项 GptDriverConfigurationGptDemEventParameterRefsGptConfigurationOfOptApiServicesGptChannelConfigSet2、GptDriverConfiguration 2.1、GptAlreadyInitDetCheck 该参数启用/禁用Gpt_Init API中的GPT_E_ALREADY_INITIALIZED Det检查。 true:开启Gpt_…

JS核心语法【流程控制语句、函数】;DOM【查找元素、操作元素、事件】--学习JavaEE的day48

day48 JS核心技术 JS核心语法 继day47 注意&#xff1a;用到控制台输出、弹窗 流程控制语句 If else、For、For-in(遍历数组时&#xff0c;跟Java是否一样【java没有】)、While、Do while、break、continue 案例&#xff1a; 1.求1-100之间的偶数之和 <!DOCTYPE html> …

Android消息机制回顾(Handler、Looper、MessageQueue源码解析)

回顾&#xff1a; Android消息机制 Android消息机制主要指的是Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作机制。 介绍 通过Handler 消息机制来解决线程之间通信问题&#xff0c;或者用来切换线程。特别是在更新UI界面时&#xff0c;确保了线程间的数…

5.23 学习总结

一.项目优化&#xff08;语音通话&#xff09; 实现步骤&#xff1a; 1.用户发送通话申请&#xff0c;并处理通话请求&#xff0c;如果同意&#xff0c;为两个用户之间进行连接。 2.获取到电脑的麦克风和扬声器&#xff0c;将获取到的语音信息转换成以字节数组的形式传递。 …

基于FPGA的图像直方图均衡化处理verilog实现,包含tb测试文件和MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 FPGA的仿真图如下&#xff1a; 将数据导入MATLAB&#xff0c;对比结果如下&#xff1a; 2.算法运行软件版本 MATLAB2022a vivado2019.2 3.部分…

【Android安全】AOSP版本对应编号| AOSP版本适配Pixel或Nexus型号 | 驱动脚本下载地址

AOSP版本对应编号 https://source.android.com/docs/setup/about/build-numbers?hlzh-cn#source-code-tags-and-builds 例如android-8.1.0_r1 对应的编号是OPM1.171019.011 可以适配Pixel 2 XL AOSP驱动脚本下载 编译AOSP时&#xff0c;需要Google的驱动&#xff0c;后面才…

Jenkins 构建 Maven 项目:项目和服务器在一起的情况

bash.sh内容 #!/bin/bash#删除历史数据 rm -rf ruoyi-admin.jar# appname$1 appnamevideo.xxxxx.com #获取传入的参数 echo "arg:$appname"#获取正在运行的jar包pid # pidps -ef | grep $1 | grep java -jar | awk {printf $2} pidps -ef | grep $appname | grep ja…

高铁VR虚拟全景展示提升企业实力和形象

步入VR的神奇世界&#xff0c;感受前所未有的汽车展示体验。VR虚拟现实技术以其独特的沉浸式模拟&#xff0c;让你仿佛置身于真实展厅之中&#xff0c;尽情探索汽车的每一处细节。 一、定制化展示&#xff0c;随心所欲 VR汽车虚拟展厅打破空间束缚&#xff0c;让汽车制造商能够…

从零开始傅里叶变换

从零开始傅里叶变换 1 Overview2 傅里叶级数2.1 基向量2.2 三角函数系表示 f ( t ) f(t) f(t)2.2.1 三角函数系的正交性2.2.2 三角函数系的系数 2.3 复指数函数系表示 f ( t ) f(t) f(t)2.3.1 复指数函数系的系数2.3.2 复指数函数系的正交性 2.4 傅里叶级数总结 3 傅里叶变换…