JS 拖拽事件

news2024/11/24 20:05:14

 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个元素是否碰撞,如果碰撞的情况非常多,我们可以考虑没碰撞的情况,下面满足任意一种情况就是没碰撞的。

 实现代码

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

  // 碰撞检测事件
    // 拖拽元素为node2
    function 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 <=20
            if (_left >= 1 && _left <= 15) {
                obj.left = _left
            } else {
                obj.left = 0
            }
            // 右空隙 大于10
            if (_right >= 1 && _right <= 15) {
                obj.right = _right
            } else {
                obj.right = 0
            }
            // 上空隙 大于10
            if (_top >= 1 && _top <= 15) {
                obj.top = _top
            } else {
                obj.top = 0
            }
            // 下空隙 大于10
            if (_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)
        }
        // 遍历删除div1
        divs1 = 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 } = isKnock
                let { offsetLeft, offsetTop, offsetHeight, offsetWidth } = div

                if (left > 0) {
                    let _left;
                    let left1 = offsetLeft - left;
                    _left = left1 >= 0 ? left1 : _left
                    div.style.left = _left + 'px'
                }
                if (right > 0) {
                    let _right;
                    let right1 = div1.offsetLeft + div1.offsetWidth + right;
                    _right = right1 <= body.offsetWidth - offsetWidth ? right1 : _right
                    div.style.left = _right + 'px'
                }
                if (top > 0) {
                    let _top;
                    let top1 = offsetTop - top
                    _top = top1 >= 0 ? top1 : _top
                    div.style.top = _top + 'px'
                }
                if (bottom > 0) {
                    let _bottom;
                    let bottom1 = div1.offsetTop + div1.offsetHeight + bottom
                    _bottom = bottom1 <= body.offsetHeight - offsetHeight ? bottom1 : _bottom
                    div.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)
        }
        // 遍历删除div1
        divs1 = 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
            // div
            let { offsetLeft, offsetTop, offsetHeight, offsetWidth } = div

            if (left > 0) {
                let _left;
                let left1 = offsetLeft - left;
                _left = left1 >= 0 ? left1 : _left
                div.style.left = _left + 'px'
            }
            if (right > 0) {
                let _right;
                let right1 = div1.offsetLeft + div1.offsetWidth + right;
                _right = right1 <= body.offsetWidth - offsetWidth ? right1 : _right
                div.style.left = _right + 'px'
            }
            if (top > 0) {
                let _top;
                let top1 = offsetTop - top
                _top = top1 >= 0 ? top1 : _top
                div.style.top = _top + 'px'
            }
            if (bottom > 0) {
                let _bottom;
                let bottom1 = div1.offsetTop + div1.offsetHeight + bottom
                _bottom = bottom1 <= body.offsetHeight - offsetHeight ? bottom1 : _bottom
                div.style.top = _bottom + 'px'
            }
        }
    }

    // js拖动事件
    function drag(obj, divs) {
        //当鼠标在被拖拽元素上按下,开始拖拽
        obj.onmousedown = function (event) {
            event = event || window.event;
            //鼠标在元素中的偏移量等于 鼠标的clientX - 元素的offsetLeft
            let { offsetLeft, offsetTop } = obj
            // 设置小于0判断,防止移除边界
            var ol = event.clientX - offsetLeft;
            var ot = event.clientY - offsetTop

            let _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.属性 = "属性值";
                // 判断 > =0
                obj.style.left = _left + "px";
                obj.style.top = _top + "px";
            };
            //为document绑定一个鼠标松开事件onmouseup
            document.onmouseup = function () {
                document.onmousemove = null;
                document.onmouseup = null;
            };
            return false;
        };
    }

    // 碰撞检测事件
    // 拖拽元素为node2
    function 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.coloradmin.cn/o/1040777.html

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

相关文章

高性能系统的性能优化技巧:从专家的经验中学习 | 开源日报 No.40

binhnguyennus/awesome-scalability Stars: 48.3k License: MIT 这个项目是一个阅读列表&#xff0c;用于说明可扩展、可靠和高性能大规模系统的模式。该项目通过知名工程师的文章和可信参考资料来解释概念&#xff0c;并从为数以百万计甚至十亿用户提供服务的经过实战验证的系…

74、SpringBoot 整合 Spring Data JDBC

总结&#xff1a;用起来跟 Spring Data JPA 差不多 什么是 JdbcTemplate&#xff1f;&#xff08;Template译为模板&#xff09; Spring 框架对 JDBC 进行封装&#xff0c;使用 JdbcTemplate 方便实现对数据库操作 ★ Spring Data JDBC 既不需要JPA、Hibernate这种ORM框架&a…

uniapp:tabBar点击后设置动画效果

APP端不支持dom操作&#xff0c;也不支持active伪类&#xff0c;绞尽脑汁也没办法给uniapp原生的tabBar点击加动画效果&#xff0c;所以最终只能舍弃原生tabBar&#xff0c;改用自定义tabBar。 自定义tabBar的原理是&#xff0c;页面的上部分分别是tabBar对应的页面组件&#…

论文速览【序列模型 seq2seq】—— 【Ptr-Net】Pointer Networks

标题&#xff1a;Pointer Networks文章链接&#xff1a;Pointer Networks参考代码&#xff08;非官方&#xff09;&#xff1a;keon/pointer-networks发表&#xff1a;NIPS 2015领域&#xff1a;序列模型&#xff08;RNN seq2seq&#xff09;改进 / 深度学习解决组合优化问题【…

StableAudio-大模型创作音乐的工具

音乐产业即将发生革命。 今天Stability AI&#xff0c;开源人工智能工具和模型之王&#xff0c;例如Stable DIffusion和StableLM&#xff0c;推出Stable Audio&#xff0c;其首款用于音乐和声音生成的人工智能产品。 音乐行业是出了名的难以进入。即使您有才华和动力&#x…

RFID技术在质量控制和生产追溯中的关键应用

在现代制造业中&#xff0c;质量控制和生产追溯是确保产品质量和合规性的关键环节。RFID技术已经成为实现这一目标的强大工具。本文将探讨RFID技术在质量控制和生产追溯中的关键应用&#xff0c;以及如何利用它来提高生产效率、确保产品质量和满足合规性要求。 生产过程追溯 …

Android11 适配

一、修改targetSdkVersion为30 将build.gradle的目标版本targetSdkVersion修改为30&#xff08;Android 11&#xff09; targetSdkVersion 30Android11的改变改变主要影响以Adnroid11 为目标版本的应用&#xff08;targetSdkVersion>30才有影响&#xff09;&#xff0c;和所…

OpenCV实现模板匹配和霍夫线检测,霍夫圆检测

一&#xff0c;模板匹配 1.1代码实现 import cv2 as cv import numpy as np import matplotlib.pyplot as plt from pylab import mplmpl.rcParams[font.sans-serif] [SimHei]#图像和模板的读取 img cv.imread("cat.png") template cv.imread(r"E:\All_in\o…

18672-2014 枸杞 学习记录

声明 本文是学习GB-T 18672-2014 枸杞. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了枸杞的质量要求、试验方法、检验规则、标志、包装、运输和贮存。 本标准适用于经干燥加工制成的各品种的枸杞成熟果实。 2 规范性引用文件…

无代码解决信息孤岛,云表实现软件开发"书同文,车同轨"

什么是信息孤岛&#xff1f;信息孤岛就是一个组织或系统内部的信息资源无法与其他部分或外部系统共享、互操作&#xff0c;从而使得这些信息无法在整个组织或系统中发挥最大作用的现象。这种现象通常发生在不同部门、不同业务领域或不同系统之间&#xff0c;导致信息重复、浪费…

精彩回顾 | 迪捷软件亮相2023世界智能网联汽车大会

2023年9月24日&#xff0c;2023世界智能网联汽车大会&#xff08;以下简称大会&#xff09;在北京市圆满落幕。迪捷软件北京参展之行圆满收官。 本次大会由工业和信息化部、公安部、交通运输部、中国科学技术协会、北京市人民政府联合主办&#xff0c;是我国首个经国务院批准的…

【编码魔法师系列_构建型1.2 】工厂方法模式(Factory Method)

学会设计模式&#xff0c;你就可以像拥有魔法一样&#xff0c;在开发过程中解决一些复杂的问题。设计模式是由经验丰富的开发者们&#xff08;GoF&#xff09;凝聚出来的最佳实践&#xff0c;可以提高代码的可读性、可维护性和可重用性&#xff0c;从而让我们的开发效率更高。通…

基于微信小程序的竞赛管理平台设计与实现(开题报告+任务书+源码+lw+ppt +部署文档+讲解)

文章目录 前言运行环境说明学生微信端的主要功能有&#xff1a;竞赛负责人的主要功能&#xff1a;管理员的主要功能有&#xff1a;具体实现截图详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考论文参考源码获取 前…

33 排序链表

排序链表 题解1 STL - multiset题解2 归并【自顶向下】题解3 归并【自底向上】自底向上&#xff1a;子串长度 l 从1开始&#xff0c;合并后的串长度*2&#xff0c;11 -> 22 -> 44 ->... 给你链表的头结点 head &#xff0c;请将其按 升序 排列并返回 排序后的链表 …

如何选择一款高性价比的便携式明渠流量计

如何选择一款精度高、测量准确、易操作的便携式明渠流量计 如何选择一款精度高、测量准确、易操作的便携式明渠流量计 便携式明渠流量计&#xff1a;是一款对现有在线水监测系统中流量监测的对比装置。该便携式明渠流量计实现了比对在线系统的液位误差及流量误差。引导式的操作…

基于微信小程序的背单词学习激励系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言用户微信端的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉…

2023年思维100秋季赛报名中,比赛安排、阶段、形式和5年真题资源

家有小学生的魔都的爸爸妈妈们&#xff0c;上海市含金量比较高的奥数比赛——思维100秋季比赛正在报名中。如果你想让你的孩子多个证书、多个经历、以赛促学&#xff0c;来了解一下吧。 一、思维100比赛是什么&#xff1f; 思维100是原来的“中环杯”&#xff0c;全称"中…

ChatGPT实战-Embeddings打造定制化AI智能客服

本文介绍Embeddings的基本概念&#xff0c;并使用最少但完整的代码讲解Embeddings是如何使用的&#xff0c;帮你打造专属AI聊天机器人&#xff08;智能客服&#xff09;&#xff0c;你可以拿到该代码进行修改以满足实际需求。 ChatGPT的Embeddings解决了什么问题&#xff1f; …

蓝桥杯 题库 简单 每日十题 day10

01 最少砝码 最少砝码 问题描述 你有一架天平。现在你要设计一套砝码&#xff0c;使得利用这些砝码 可以出任意小于等于N的正整数重量。那么这套砝码最少需要包含多少个砝码&#xff1f; 注意砝码可以放在天平两边。 输入格式 输入包含一个正整数N。 输出格式 输出一个整数代表…

面部情绪识别Facial Emotion Recognition:从表情到情绪的全面解析与代码实现

面部情绪识别&#xff08;FER&#xff09;是指根据面部表情对人类情绪进行识别和分类的过程。通过分析面部特征和模式&#xff0c;机器可以有依据地推测一个人的情绪状态。这一面部识别子领域是一个高度跨学科的领域&#xff0c;它借鉴了计算机视觉、机器学习和心理学的见解。 …