JS 拖拽事件

 1.drag等拖拽事件

拖放是由拖动与释放两部分组成,拖放事件也分为被拖动元素的相关事件,和容器的相关事件。 被拖动元素的相关事件如下所示:

被拖动元素相关事件: 

事件描述
dragstart用户开始拖动元素时触发
drag元素正在拖动时触发
dragend用户完成元素拖动后触发

容器相关事件如下所示:

事件描述
dragenter当被鼠标拖动的对象进入目标容器时触发此事件
dragover当被拖动的对象在目标容器范围内拖动时触发此事件
dragleave当被鼠标拖动的对象离开目标容器时触发此事件
drop在一个拖动过程中,释放鼠标键到目标容器时触发此事件

实现拖拽

1.首先给标签添加draggable="true"属性表明标签可拖拽。

<div draggable="true" id="7">7</div>

2.通过标签绑定dragstart事件,会在用户拖动标签时开始触发。我们将拖拽标签的id存入其中。

  // 拖动元素开始触发div.ondragstart = function (e) {e.dataTransfer.setData('Text', e.target.id)oldPar = div.parentNode // 记录父节点}

3.在放置拖动元素的容器上绑定一个 dragover 事件,这个事件用于规定在何处放置被拖动的数据。默认情况下,是无法将一格元素放置到另外一个元素里面的,所以如果需要设置允许放置,则要在 ondragover 事件中加上 e.preventDefault() 方法来阻止默认行为。

 dragBox.ondragover = function (e) {e.preventDefault()}

4. 将元素拖拽放置在容器中(在容器中松开鼠标),在在容器中绑定一个drop 事件中,同样需要调用 e.preventDefault() 方法来阻止默认行为。然后可以通过 dataTransfer.getData("Text"); 方法获取之前的 drag(event) 函数中保存的信息,也就是被拖动元素的 id。接着通过 容器dom实例.appendChild() 方法为将拖动元素作为元素容器的子元素追加到元素容器中,这样就能成功实现拖放。

     // 容器内放置拖动元素dragBox.ondrop = function (e) {e.preventDefault()let data = e.dataTransfer.getData("Text") // 这么说这是一个静态对象let newChil = document.getElementById(data)dragBox.appendChild(newChil) // 追加子元素}

案例

效果展示

 代码实现

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>拖拽</title><style type="text/css">.drag-box {display: flex;}.drag-box ul div {margin: 10px 0;width: 100px;height: 100px;border: 1px solid black;text-align: center;line-height: 100px;font-size: 20px;font-weight: bolder;background-image: linear-gradient(transparent,rgba(0, 0, 0, 0.5));transition: all 2s;}</style>
</head><body><div class="drag-box"><ul><div draggable="true" id="1">1</div></ul><ul><div draggable="true" id="2">2</div></ul><ul><div draggable="true" id="3">3</div></ul><ul><div draggable="true" id="4">4</div></ul><ul><div draggable="true" id="5">5</div></ul><ul><div draggable="true" id="6">6</div></ul><ul><div draggable="true" id="7">7</div></ul><ul><div draggable="true" id="8">8</div></ul><ul><div draggable="true" id="9">9</div></ul></div>
</body>
<script>let dragBoxs = document.querySelectorAll('.drag-box>ul')let divs = document.querySelectorAll('.drag-box div')let oldPar;for (const div of divs) {// 拖动元素开始触发div.ondragstart = function (e) {e.dataTransfer.setData('Text', e.target.id)oldPar = div.parentNode // 记录父节点}// 拖动元素过程中触发div.ondrag = function (e) {e.target.style.opacity = "0"oldPar.style.display = "none"}// 拖动元素完成后触发div.ondragend = function (e) {e.target.style.opacity = "1"console.log(1);oldPar.style.display = "block"}}// 容器事件for (const dragBox of dragBoxs) {// 拖动元素在目标容器内触发dragBox.ondragover = function (e) {e.preventDefault()}// 容器内放置拖动元素dragBox.ondrop = function (e) {e.preventDefault()let data = e.dataTransfer.getData("Text") // 这么说这是一个静态对象let newChil = document.getElementById(data)let oldChil = dragBox.children[0]// 判断盒子是否空元素if (oldChil) {dragBox.appendChild(newChil) // 追加新的子元素,删除旧的子元素dragBox.removeChild(oldChil)oldPar.appendChild(oldChil)} else {dragBox.appendChild(newChil)}}}</script></html>

2. mousedown、mousemove、mouseup等事件实现拖拽与碰撞

我们直接用一个案例来解释:

效果展示

ps: 还是有bug,目前找不出来。

实现拖拽

我们实现这个功能,需要3个事件:

前提: 子元素是相对定位,容器是绝对定位,脱离文档流。

1.按住鼠标 (mousedown),获取当前元素 offsetLeft 和offsetTop,当前鼠标的clientX、clientY,用鼠标坐标 - 元素的offsetLeft、offsetTop属性,求出鼠标-元素边界的距离.

2.移动鼠标(monusemove),获取移动元素中鼠标的clientX、clientY 减去上边求出的鼠标到元素边界的距离 ,求出元素移动top、left值,赋给元素。

3.松开鼠标(monuseup),将鼠标移动事件清除清除。

实现碰撞挤开其他的元素

下面有2个元素,判断2个元素是否碰撞,如果碰撞的情况非常多,我们可以考虑没碰撞的情况,下面满足任意一种情况就是没碰撞的。

 实现代码

我们通过检测元素是否碰撞,元素之间间隙到一定距离;就将空隙距离返回。

  // 碰撞检测事件// 拖拽元素为node2function knock(node1, node2) {var l1 = node1.offsetLeft;var r1 = node1.offsetLeft + node1.offsetWidth;var t1 = node1.offsetTop;var b1 = node1.offsetTop + node1.offsetHeight;var l2 = node2.offsetLeft;var r2 = node2.offsetLeft + node2.offsetWidth;var t2 = node2.offsetTop;var b2 = node2.offsetTop + node2.offsetHeight;// 左空隙 let _left = l2 - r1// 右空隙let _right = l1 - r2// 上空隙let _top = t2 - b1// 下空隙let _bottom = t1 - b2// 没碰撞let obj = {};// 没碰撞;大于1px 小于15px应该顶撞他;其余情况没考虑,统一设为 0px(为啥1 -15 之间,鼠标移动太快,检测有延迟)if (l2 > r1 || r2 < l1 || t2 > b1 || b2 < t1) {// 左空隙 >=10 <=20if (_left >= 1 && _left <= 15) {obj.left = _left} else {obj.left = 0}// 右空隙 大于10if (_right >= 1 && _right <= 15) {obj.right = _right} else {obj.right = 0}// 上空隙 大于10if (_top >= 1 && _top <= 15) {obj.top = _top} else {obj.top = 0}// 下空隙 大于10if (_bottom >= 1 && _bottom <= 15) {obj.bottom = _bottom} else {obj.bottom = 0}return obj} else {// 上面理论上,元素之间有空隙;return true}

 设置bd函数, 将拖拽div,和div数组传入,遍历div数组,然后调用上面knock()方法,来判断是否碰撞;

将拖拽div距离其他标签的div句空隙距离,将距离设置给其他div,从而实现碰撞撞开其他元素。

// 给拖拽中div绑定碰撞函数,并根据返回值,拖拽div与其他div之间隔出空隙function bd(div1, divs) {let c_divs = []for (const div of divs) {c_divs.push(div)}// 遍历删除div1divs1 = c_divs.filter(div =>div1 != div)// 记录与他碰撞的div元素和是否碰撞for (const div of divs1) {let item = {}let isKnock = knock(div, div1)// 拿出2个div间隙if (isKnock != true) {let { left, right, top, bottom } = isKnocklet { offsetLeft, offsetTop, offsetHeight, offsetWidth } = divif (left > 0) {let _left;let left1 = offsetLeft - left;_left = left1 >= 0 ? left1 : _leftdiv.style.left = _left + 'px'}if (right > 0) {let _right;let right1 = div1.offsetLeft + div1.offsetWidth + right;_right = right1 <= body.offsetWidth - offsetWidth ? right1 : _rightdiv.style.left = _right + 'px'}if (top > 0) {let _top;let top1 = offsetTop - top_top = top1 >= 0 ? top1 : _topdiv.style.top = _top + 'px'}if (bottom > 0) {let _bottom;let bottom1 = div1.offsetTop + div1.offsetHeight + bottom_bottom = bottom1 <= body.offsetHeight - offsetHeight ? bottom1 : _bottomdiv.style.top = _bottom + 'px'}}}}

完整代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>body {position: absolute;height: 98vh;width: 98vw;border: 2px solid black;}div {position: absolute;margin: 5px;width: 100px;height: 100px;background-color: rebeccapurple;}</style><title>页面拖动和碰撞</title>
</head><body><div>1</div><div>2</div><div>3</div></body>
<script>let divs = document.getElementsByTagName("div")let body = document.getElementsByTagName("body")[0]for (const div of divs) {drag(div, divs)// 帮主div绑定与其他div的碰撞事件}// for in 只能遍历对象// 给div 和其他div绑定碰撞函数function bd(div1, divs) {let c_divs = []for (const div of divs) {c_divs.push(div)}// 遍历删除div1divs1 = c_divs.filter(div =>div1 != div)// 记录与他碰撞的div元素和是否碰撞for (const div of divs1) {let item = {}let isKnock = knock(div, div1)// 拿出2个div间隙let { left, right, top, bottom } = isKnock// divlet { offsetLeft, offsetTop, offsetHeight, offsetWidth } = divif (left > 0) {let _left;let left1 = offsetLeft - left;_left = left1 >= 0 ? left1 : _leftdiv.style.left = _left + 'px'}if (right > 0) {let _right;let right1 = div1.offsetLeft + div1.offsetWidth + right;_right = right1 <= body.offsetWidth - offsetWidth ? right1 : _rightdiv.style.left = _right + 'px'}if (top > 0) {let _top;let top1 = offsetTop - top_top = top1 >= 0 ? top1 : _topdiv.style.top = _top + 'px'}if (bottom > 0) {let _bottom;let bottom1 = div1.offsetTop + div1.offsetHeight + bottom_bottom = bottom1 <= body.offsetHeight - offsetHeight ? bottom1 : _bottomdiv.style.top = _bottom + 'px'}}}// js拖动事件function drag(obj, divs) {//当鼠标在被拖拽元素上按下,开始拖拽obj.onmousedown = function (event) {event = event || window.event;//鼠标在元素中的偏移量等于 鼠标的clientX - 元素的offsetLeftlet { offsetLeft, offsetTop } = obj// 设置小于0判断,防止移除边界var ol = event.clientX - offsetLeft;var ot = event.clientY - offsetToplet _left = 0;let _top = 0;//为document绑定一个onmousemove事件,鼠标移动事件document.onmousemove = function (event) {bd(obj, divs) // 元素移动调用碰撞函数event = event || window.event;var left = event.clientX - ol;var top = event.clientY - ot;let { offsetWidth, offsetHeight } = body// 防止元素移出容器_left = left >= 0 && left <= offsetWidth - obj.offsetWidth ? left : _left_top = top >= 0 && top <= offsetHeight - obj.offsetHeight ? top : _top//修改元素的位置 修改元素的位置只能通过 元素.style.属性 = "属性值";// 判断 > =0obj.style.left = _left + "px";obj.style.top = _top + "px";};//为document绑定一个鼠标松开事件onmouseupdocument.onmouseup = function () {document.onmousemove = null;document.onmouseup = null;};return false;};}// 碰撞检测事件// 拖拽元素为node2function knock(node1, node2) {var l1 = node1.offsetLeft;var r1 = node1.offsetLeft + node1.offsetWidth;var t1 = node1.offsetTop;var b1 = node1.offsetTop + node1.offsetHeight;var l2 = node2.offsetLeft;var r2 = node2.offsetLeft + node2.offsetWidth;var t2 = node2.offsetTop;var b2 = node2.offsetTop + node2.offsetHeight;// 左空隙 let _left = l2 - r1// 右空隙let _right = l1 - r2// 上空隙let _top = t2 - b1// 下空隙let _bottom = t1 - b2// 没碰撞let obj = {};// 没碰撞;大于1px 小于15px应该顶撞他;其余情况没考虑,统一设为 0px(为啥1 -15 之间,鼠标移动太快,检测有延迟)if (l2 > r1 || r2 < l1 || t2 > b1 || b2 < t1) {// 左空隙if (_left >= 1 && _left <= 15) {obj.left = _left} else {obj.left = 0}// 右空隙 if (_right >= 1 && _right <= 15) {obj.right = _right} else {obj.right = 0}// 上空隙 if (_top >= 1 && _top <= 15) {obj.top = _top} else {obj.top = 0}// 下空隙 if (_bottom >= 1 && _bottom <= 15) {obj.bottom = _bottom} else {obj.bottom = 0}return obj} else {// 上面理论上,元素之间有空隙;return true}}</script></html>

参考资料: https://blog.csdn.net/horizon12/article/details/108650346

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

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

相关文章

图像处理: 马赛克艺术

马赛克 第一章 马赛克的历史渊源 1.1 马赛克 艺术中的一种表面装饰&#xff0c;由紧密排列的、通常颜色各异的小块材料&#xff08;如石头、矿物、玻璃、瓷砖或贝壳&#xff09;组成。与镶嵌不同的是&#xff0c;镶嵌是将要应用的部件放置在已挖空以容纳设计的表面中&#xff0…

面试记录_

1&#xff1a;面试杉岩数据&#xff08;python开发&#xff09; 1.1.1 选择题 for(int i0;i<n;i){for(int j0;j<n;jji) } }O(n) * (O(0) O(n/1) O(n/2) O(n/3) ... O(n/n)) 在最坏情况下&#xff0c;内部循环的迭代次数为 n/1 n/2 n/3 ... n/n&#xff0c;这是…

select完成服务器并发

服务器 #include <myhead.h>#define PORT 4399 //端口号 #define IP "192.168.0.191"//IP地址//键盘输入事件 int keybord_events(fd_set readfds); //客户端交互事件 int cliRcvSnd_events(int , struct sockaddr_in*, fd_set *, int *); //客户端连接事件 …

计算机图像处理-中值滤波

非线性滤波 非线性滤波是利用原始图像跟模版之间的一种逻辑关系得到结果&#xff0c;常用的非线性滤波方法有中值滤波和高斯双边滤波&#xff0c;分别对应cv2.medianBlur(src, ksize)方法和cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]])方法。 …

20分钟---Vue2->Vue3

Vue官网地址&#xff1a;gVue.js - The Progressive JavaScript Framework | Vue.js 选项式vs组合式 vue的两种风格&#xff0c;搬运官网源码&#xff1a; 选项式 API (Options API)​ 使用选项式 API&#xff0c;我们可以用包含多个选项的对象来描述组件的逻辑&#xff0c…

[题]欧拉函数 #欧拉函数

目录 欧拉函数一、用公式求代码 二、线性筛法求欧拉函数扩展欧拉定理 欧拉函数 AcWing 873. 欧拉函数 一、用公式求 定义&#xff1a;1 ~ N 中与 N 互质的数的个数被称为欧拉函数&#xff0c;记为ϕ(N)。 怎么求呢&#xff1f;&#xff1f; 有一个公式&#xff1a; N p1a1 X…

MASA MAUI iOS 文件下载与断点续传

文章目录 背景介绍方案及代码1、新建MAUI项目2、建立NSUrlSession会话连接3、使用NSUrlSessionDownloadTask 创建下载任务4、DidWriteData 监听下载5、DidFinishDownloading 完成下载6、CancelDownload (取消/暂停)下载7、ResumeDownload 恢复下载8、杀死进程-恢复下载 效果图总…

Python 小爬虫入门 -- 爬取专栏文章标题保存到 CSV 文件中

爬取专栏文章标题保存到 CSV 文件中目标分析网页代码及理解代码段一代码段二成果展示爬取专栏文章标题保存到 CSV 文件中 目标 从一个网页上抓取数据,并保存到一个 CSV 文件中。 具体是爬取 微机系统与接口上机实验_TD PITE型 专栏里的所有 文章标题 并 保存到 csv 文件 中…

GitHub配置SSH key

GitHub配置SSH key Git配置信息并生成密钥 设置用户名和密码 设置用户名 git config --global user.name "用户名" 设置邮箱 git confir --global user.email "邮箱" 生成密钥 ssh-keygen -t rsa -C "邮箱" 查看密钥 到密钥所保存的位置 复…

嵌入式Linux应用开发-第十四章查询方式的按键驱动程序

嵌入式Linux应用开发-第十四章查询方式的按键驱动程序 第十四章 查询方式的按键驱动程序_编写框架14.1 LED驱动回顾14.2 按键驱动编写思路14.3 编程&#xff1a;先写框架14.3.1 把按键的操作抽象出一个button_operations结构体14.3.2 驱动程序的上层&#xff1a;file_operation…

(高阶) Redis 7 第16讲 预热/雪崩/击穿/穿透 缓存篇

面试题 什么是缓存预热/雪崩/击穿/穿透如何做缓存预热如何避免或减少缓存雪崩穿透和击穿的区别?穿透和击穿的解决方案出现缓存不一致时,有哪些修补方案缓存预热 理论 将需要的数据提前加载到缓存中,不需要用户使用的过程中进行数据回写。(比如秒杀活动数据等) 方案 1.…

吉力宝:智能科技鞋品牌步力宝引领传统产业创新思维

在现代经济环境下&#xff0c;市场经济下产品的竞争非常的激烈&#xff0c;如果没有营销&#xff0c;产品很可能不被大众认可&#xff0c;酒香也怕巷子深&#xff0c;许多传统产业不得不面临前所未有的挑战。而为了冲出这个“巷子”&#xff0c;许多企业需要采用创新思维&#…

NLP 03(LSTM)

一、LSTM LSTM (Long Short-Term Memory) 也称长短时记忆结构,它是传统RNN的变体,与经典RNN相比&#xff1a; 能够有效捕捉长序列之间的语义关联缓解梯度消失或爆炸现象 LSTM的结构更复杂,它的核心结构可以分为四个部分去解析: 遗忘门、输入门、细胞状态、输出门 LSTM内部结构…

MyBatisPlus(六)字段映射 @TableField

字段注解&#xff08;非主键&#xff09; TableField 用于映射对象的 属性 和表中的 字段 。 当 属性名 和 字段名 差异较大的时候&#xff0c;无法通过默认的映射关系对应起来&#xff0c;就需要指定 属性名 对应 的 字段名。 官网示例 代码实例 package com.example.web.…

【网络原理】初始网络,了解概念

文章目录 1. 网络通信1.1 局域网LAN1.2 广域网WAN 2. 基础概念2.1 IP2.2 端口号 3. 认识协议4. 五元组5. 协议分层5.1 分层的作用5.2 OSI七层模型5.3 TCP/IP五层&#xff08;四层&#xff09;模型 6. 封装和分用 1. 网络通信 计算机与计算机之间是互相独立&#xff0c;是独立模…

【小沐学前端】Node.js实现UDP和Protobuf 通信(protobuf.js)

文章目录 1、简介1.1 node1.2 Protobuf 2、下载和安装2.1 node2.2 Protobuf 3、node 代码示例3.1 HTTP3.2 UDP单播3.4 UDP广播 4、Protobuf 代码示例4.1 例子:awesome.proto 结语 1、简介 1.1 node Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。 Node.js 是一个开源…

Leetcode---364场周赛

题目列表 2864. 最大二进制奇数 2865. 美丽塔 I 2866. 美丽塔 II 2867. 统计树中的合法路径数目 一、最大二进制奇数 这题只要你对二进制有了解(学编程的不会不了解二进制吧)&#xff0c;应该问题不大&#xff0c;这题要求最大奇数&#xff0c;1.奇数&#xff1a;只要保证…

数据结构 | 二叉树

基本形状 可参照 数据结构&#xff1a;树(Tree)【详解】_数据结构 树_UniqueUnit的博客-CSDN博客 二叉树的性质 三种顺序遍历

区块链实验室(26) - 区块链期刊Blockchain: Research and Applications

Elsevier出版物“Blockchain: Research and Applications”是浙江大学编审的期刊。该期刊自2020年创刊&#xff0c;并出版第1卷。每年出版4期&#xff0c;最新期是第4卷第3期(2023年9月)。 目前没有官方的IF&#xff0c;Elsevier的引用因子Citescore是6.4。 虽然是新刊&#xf…

Android studio升级Giraffe | 2022.3.1 Patch 1踩坑

这里写自定义目录标题 not "opens java.io" to unnamed module错误报错信息解决 superclass access check failed: class butterknife.compiler.ButterKnifeProcessor$RScanner报错报错信息解决 Android studio升级Giraffe | 2022.3.1 Patch 1后&#xff0c;出现项目…