JS drag event

 1.drag and other drag events

Drag and drop consists of two parts: drag and release . Drag and drop events are also divided into events related to the dragged element and events related to the container . The related events of the dragged element are as follows:

Events related to dragged elements: 

event describe
drag start Fires when the user starts dragging an element
drag Fires when the element is being dragged
carrying Fires after the user completes dragging the element

Container related events are as follows:

event describe
more bearable This event is triggered when the object being dragged by the mouse enters the target container
Dragover This event is triggered when the dragged object is dragged within the target container.
dragleave This event is triggered when the object being dragged by the mouse leaves the target container.
drop During a dragging process, this event is triggered when the mouse button is released to the target container.

Implement drag and drop

1. First add the draggable="true" attribute to the label to indicate that the label can be dragged.

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

2. Bind the dragstart event through the label, which will be triggered when the user drags the label. We store the id of the drag tag in it.

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

3. Bind a dragover event to the container where the dragged element is placed. This event is used to specify where to place the dragged data. By default, one grid element cannot be placed inside another element, so if you need to allow placement, add the e.preventDefault() method to the ondragover event to prevent the default behavior.

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

4. Drag and drop the element into the container (release the mouse in the container). When binding a drop event in the container, you also need to call the e.preventDefault() method to prevent the default behavior. Then you can use the dataTransfer.getData("Text"); method to obtain the information saved in the previous drag(event) function, which is the id of the dragged element. Then, the drag element is appended to the element container as a child element of the element container through the .appendChild() method of the container dom instance, so that drag and drop can be successfully implemented.

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

        }

Case

Show results

 Code

<!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 and other events realize dragging and collision

Let’s explain it directly with a case:

Show results

ps : There is still a bug, but I can’t find it yet.

Implement drag and drop

To implement this function, we need 3 events:

Premise: The child elements are relatively positioned, and the container is absolutely positioned, out of the document flow.

1. Hold down the mouse (mousedown), get the offsetLeft and offsetTop of the current element, the clientX and clientY of the current mouse, and use the mouse coordinates - the offsetLeft and offsetTop attributes of the element to find the distance between the mouse and the element boundary .

2. Move the mouse (monusemove), obtain the clientX and clientY of the mouse in the moved element, subtract the distance from the mouse to the element boundary calculated above, find the top and left values ​​of the element movement, and assign them to the element.

3. Release the mouse (monuseup) and clear the mouse movement event.

Implement collision and squeeze out other elements

There are two elements below. Determine whether the two elements collide. If there are many collisions, we can consider the case of no collision. If any of the following conditions is met, there is no collision.

 Implement code

We detect whether the elements collide and the gap between the elements reaches a certain distance; we return the gap distance.

  // 碰撞检测事件
    // 拖拽元素为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
        }

 Set the bd function, pass in the dragging div and the div array, traverse the div array, and then call the knock() method above to determine whether there is a collision;

Drag the div to the distance between the divs of other tags and set the distance to other divs to achieve collision and knock away other elements.

// 给拖拽中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'
                }
            }
        }
    }

Complete code

<!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>

Reference:  https://blog.csdn.net/horizon12/article/details/108650346

Guess you like

Origin blog.csdn.net/Qhx20040819/article/details/133235163
Recommended