• JS 拖拽事件


     1.drag等拖拽事件

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

    被拖动元素相关事件: 

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

    容器相关事件如下所示:

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

    实现拖拽

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

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

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

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

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

    1. dragBox.ondragover = function (e) {
    2. e.preventDefault()
    3. }

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

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

    案例

    效果展示

     代码实现

    1. html>
    2. <html>
    3. <head>
    4. <meta charset="utf-8">
    5. <title>拖拽title>
    6. <style type="text/css">
    7. .drag-box {
    8. display: flex;
    9. }
    10. .drag-box ul div {
    11. margin: 10px 0;
    12. width: 100px;
    13. height: 100px;
    14. border: 1px solid black;
    15. text-align: center;
    16. line-height: 100px;
    17. font-size: 20px;
    18. font-weight: bolder;
    19. background-image: linear-gradient(transparent,
    20. rgba(0, 0, 0, 0.5));
    21. transition: all 2s;
    22. }
    23. style>
    24. head>
    25. <body>
    26. <div class="drag-box">
    27. <ul>
    28. <div draggable="true" id="1">1div>
    29. ul>
    30. <ul>
    31. <div draggable="true" id="2">2div>
    32. ul>
    33. <ul>
    34. <div draggable="true" id="3">3div>
    35. ul>
    36. <ul>
    37. <div draggable="true" id="4">4div>
    38. ul>
    39. <ul>
    40. <div draggable="true" id="5">5div>
    41. ul>
    42. <ul>
    43. <div draggable="true" id="6">6div>
    44. ul>
    45. <ul>
    46. <div draggable="true" id="7">7div>
    47. ul>
    48. <ul>
    49. <div draggable="true" id="8">8div>
    50. ul>
    51. <ul>
    52. <div draggable="true" id="9">9div>
    53. ul>
    54. div>
    55. body>
    56. <script>
    57. let dragBoxs = document.querySelectorAll('.drag-box>ul')
    58. let divs = document.querySelectorAll('.drag-box div')
    59. let oldPar;
    60. for (const div of divs) {
    61. // 拖动元素开始触发
    62. div.ondragstart = function (e) {
    63. e.dataTransfer.setData('Text', e.target.id)
    64. oldPar = div.parentNode // 记录父节点
    65. }
    66. // 拖动元素过程中触发
    67. div.ondrag = function (e) {
    68. e.target.style.opacity = "0"
    69. oldPar.style.display = "none"
    70. }
    71. // 拖动元素完成后触发
    72. div.ondragend = function (e) {
    73. e.target.style.opacity = "1"
    74. console.log(1);
    75. oldPar.style.display = "block"
    76. }
    77. }
    78. // 容器事件
    79. for (const dragBox of dragBoxs) {
    80. // 拖动元素在目标容器内触发
    81. dragBox.ondragover = function (e) {
    82. e.preventDefault()
    83. }
    84. // 容器内放置拖动元素
    85. dragBox.ondrop = function (e) {
    86. e.preventDefault()
    87. let data = e.dataTransfer.getData("Text") // 这么说这是一个静态对象
    88. let newChil = document.getElementById(data)
    89. let oldChil = dragBox.children[0]
    90. // 判断盒子是否空元素
    91. if (oldChil) {
    92. dragBox.appendChild(newChil) // 追加新的子元素,删除旧的子元素
    93. dragBox.removeChild(oldChil)
    94. oldPar.appendChild(oldChil)
    95. } else {
    96. dragBox.appendChild(newChil)
    97. }
    98. }
    99. }
    100. script>
    101. 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个元素是否碰撞,如果碰撞的情况非常多,我们可以考虑没碰撞的情况,下面满足任意一种情况就是没碰撞的。

     实现代码

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

    1. // 碰撞检测事件
    2. // 拖拽元素为node2
    3. function knock(node1, node2) {
    4. var l1 = node1.offsetLeft;
    5. var r1 = node1.offsetLeft + node1.offsetWidth;
    6. var t1 = node1.offsetTop;
    7. var b1 = node1.offsetTop + node1.offsetHeight;
    8. var l2 = node2.offsetLeft;
    9. var r2 = node2.offsetLeft + node2.offsetWidth;
    10. var t2 = node2.offsetTop;
    11. var b2 = node2.offsetTop + node2.offsetHeight;
    12. // 左空隙
    13. let _left = l2 - r1
    14. // 右空隙
    15. let _right = l1 - r2
    16. // 上空隙
    17. let _top = t2 - b1
    18. // 下空隙
    19. let _bottom = t1 - b2
    20. // 没碰撞
    21. let obj = {};
    22. // 没碰撞;大于1px 小于15px应该顶撞他;其余情况没考虑,统一设为 0px(为啥1 -15 之间,鼠标移动太快,检测有延迟)
    23. if (l2 > r1 || r2 < l1 || t2 > b1 || b2 < t1) {
    24. // 左空隙 >=10 <=20
    25. if (_left >= 1 && _left <= 15) {
    26. obj.left = _left
    27. } else {
    28. obj.left = 0
    29. }
    30. // 右空隙 大于10
    31. if (_right >= 1 && _right <= 15) {
    32. obj.right = _right
    33. } else {
    34. obj.right = 0
    35. }
    36. // 上空隙 大于10
    37. if (_top >= 1 && _top <= 15) {
    38. obj.top = _top
    39. } else {
    40. obj.top = 0
    41. }
    42. // 下空隙 大于10
    43. if (_bottom >= 1 && _bottom <= 15) {
    44. obj.bottom = _bottom
    45. } else {
    46. obj.bottom = 0
    47. }
    48. return obj
    49. } else {
    50. // 上面理论上,元素之间有空隙;
    51. return true
    52. }

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

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

    1. // 给拖拽中div绑定碰撞函数,并根据返回值,拖拽div与其他div之间隔出空隙
    2. function bd(div1, divs) {
    3. let c_divs = []
    4. for (const div of divs) {
    5. c_divs.push(div)
    6. }
    7. // 遍历删除div1
    8. divs1 = c_divs.filter(div =>
    9. div1 != div
    10. )
    11. // 记录与他碰撞的div元素和是否碰撞
    12. for (const div of divs1) {
    13. let item = {}
    14. let isKnock = knock(div, div1)
    15. // 拿出2个div间隙
    16. if (isKnock != true) {
    17. let { left, right, top, bottom } = isKnock
    18. let { offsetLeft, offsetTop, offsetHeight, offsetWidth } = div
    19. if (left > 0) {
    20. let _left;
    21. let left1 = offsetLeft - left;
    22. _left = left1 >= 0 ? left1 : _left
    23. div.style.left = _left + 'px'
    24. }
    25. if (right > 0) {
    26. let _right;
    27. let right1 = div1.offsetLeft + div1.offsetWidth + right;
    28. _right = right1 <= body.offsetWidth - offsetWidth ? right1 : _right
    29. div.style.left = _right + 'px'
    30. }
    31. if (top > 0) {
    32. let _top;
    33. let top1 = offsetTop - top
    34. _top = top1 >= 0 ? top1 : _top
    35. div.style.top = _top + 'px'
    36. }
    37. if (bottom > 0) {
    38. let _bottom;
    39. let bottom1 = div1.offsetTop + div1.offsetHeight + bottom
    40. _bottom = bottom1 <= body.offsetHeight - offsetHeight ? bottom1 : _bottom
    41. div.style.top = _bottom + 'px'
    42. }
    43. }
    44. }
    45. }

    完整代码

    1. html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    6. <style>
    7. body {
    8. position: absolute;
    9. height: 98vh;
    10. width: 98vw;
    11. border: 2px solid black;
    12. }
    13. div {
    14. position: absolute;
    15. margin: 5px;
    16. width: 100px;
    17. height: 100px;
    18. background-color: rebeccapurple;
    19. }
    20. style>
    21. <title>页面拖动和碰撞title>
    22. head>
    23. <body>
    24. <div>1div>
    25. <div>2div>
    26. <div>3div>
    27. body>
    28. <script>
    29. let divs = document.getElementsByTagName("div")
    30. let body = document.getElementsByTagName("body")[0]
    31. for (const div of divs) {
    32. drag(div, divs)
    33. // 帮主div绑定与其他div的碰撞事件
    34. }
    35. // for in 只能遍历对象
    36. // 给div 和其他div绑定碰撞函数
    37. function bd(div1, divs) {
    38. let c_divs = []
    39. for (const div of divs) {
    40. c_divs.push(div)
    41. }
    42. // 遍历删除div1
    43. divs1 = c_divs.filter(div =>
    44. div1 != div
    45. )
    46. // 记录与他碰撞的div元素和是否碰撞
    47. for (const div of divs1) {
    48. let item = {}
    49. let isKnock = knock(div, div1)
    50. // 拿出2个div间隙
    51. let { left, right, top, bottom } = isKnock
    52. // div
    53. let { offsetLeft, offsetTop, offsetHeight, offsetWidth } = div
    54. if (left > 0) {
    55. let _left;
    56. let left1 = offsetLeft - left;
    57. _left = left1 >= 0 ? left1 : _left
    58. div.style.left = _left + 'px'
    59. }
    60. if (right > 0) {
    61. let _right;
    62. let right1 = div1.offsetLeft + div1.offsetWidth + right;
    63. _right = right1 <= body.offsetWidth - offsetWidth ? right1 : _right
    64. div.style.left = _right + 'px'
    65. }
    66. if (top > 0) {
    67. let _top;
    68. let top1 = offsetTop - top
    69. _top = top1 >= 0 ? top1 : _top
    70. div.style.top = _top + 'px'
    71. }
    72. if (bottom > 0) {
    73. let _bottom;
    74. let bottom1 = div1.offsetTop + div1.offsetHeight + bottom
    75. _bottom = bottom1 <= body.offsetHeight - offsetHeight ? bottom1 : _bottom
    76. div.style.top = _bottom + 'px'
    77. }
    78. }
    79. }
    80. // js拖动事件
    81. function drag(obj, divs) {
    82. //当鼠标在被拖拽元素上按下,开始拖拽
    83. obj.onmousedown = function (event) {
    84. event = event || window.event;
    85. //鼠标在元素中的偏移量等于 鼠标的clientX - 元素的offsetLeft
    86. let { offsetLeft, offsetTop } = obj
    87. // 设置小于0判断,防止移除边界
    88. var ol = event.clientX - offsetLeft;
    89. var ot = event.clientY - offsetTop
    90. let _left = 0;
    91. let _top = 0;
    92. //为document绑定一个onmousemove事件,鼠标移动事件
    93. document.onmousemove = function (event) {
    94. bd(obj, divs) // 元素移动调用碰撞函数
    95. event = event || window.event;
    96. var left = event.clientX - ol;
    97. var top = event.clientY - ot;
    98. let { offsetWidth, offsetHeight } = body
    99. // 防止元素移出容器
    100. _left = left >= 0 && left <= offsetWidth - obj.offsetWidth ? left : _left
    101. _top = top >= 0 && top <= offsetHeight - obj.offsetHeight ? top : _top
    102. //修改元素的位置 修改元素的位置只能通过 元素.style.属性 = "属性值";
    103. // 判断 > =0
    104. obj.style.left = _left + "px";
    105. obj.style.top = _top + "px";
    106. };
    107. //为document绑定一个鼠标松开事件onmouseup
    108. document.onmouseup = function () {
    109. document.onmousemove = null;
    110. document.onmouseup = null;
    111. };
    112. return false;
    113. };
    114. }
    115. // 碰撞检测事件
    116. // 拖拽元素为node2
    117. function knock(node1, node2) {
    118. var l1 = node1.offsetLeft;
    119. var r1 = node1.offsetLeft + node1.offsetWidth;
    120. var t1 = node1.offsetTop;
    121. var b1 = node1.offsetTop + node1.offsetHeight;
    122. var l2 = node2.offsetLeft;
    123. var r2 = node2.offsetLeft + node2.offsetWidth;
    124. var t2 = node2.offsetTop;
    125. var b2 = node2.offsetTop + node2.offsetHeight;
    126. // 左空隙
    127. let _left = l2 - r1
    128. // 右空隙
    129. let _right = l1 - r2
    130. // 上空隙
    131. let _top = t2 - b1
    132. // 下空隙
    133. let _bottom = t1 - b2
    134. // 没碰撞
    135. let obj = {};
    136. // 没碰撞;大于1px 小于15px应该顶撞他;其余情况没考虑,统一设为 0px(为啥1 -15 之间,鼠标移动太快,检测有延迟)
    137. if (l2 > r1 || r2 < l1 || t2 > b1 || b2 < t1) {
    138. // 左空隙
    139. if (_left >= 1 && _left <= 15) {
    140. obj.left = _left
    141. } else {
    142. obj.left = 0
    143. }
    144. // 右空隙
    145. if (_right >= 1 && _right <= 15) {
    146. obj.right = _right
    147. } else {
    148. obj.right = 0
    149. }
    150. // 上空隙
    151. if (_top >= 1 && _top <= 15) {
    152. obj.top = _top
    153. } else {
    154. obj.top = 0
    155. }
    156. // 下空隙
    157. if (_bottom >= 1 && _bottom <= 15) {
    158. obj.bottom = _bottom
    159. } else {
    160. obj.bottom = 0
    161. }
    162. return obj
    163. } else {
    164. // 上面理论上,元素之间有空隙;
    165. return true
    166. }
    167. }
    168. script>
    169. html>

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

  • 相关阅读:
    关于前端地图笔记
    软考高级系统架构设计师系列之:深入理解设计模式
    Ribbon负载均衡算法
    网络服务退出一个问题的解析
    在 emacs 中如何将窗口的垂直分割改为水平分割
    idea 创建maven父子工程(spring cloud 组件教程大全 一)
    黑马JVM总结(九)
    水生植物拉丁文及缩写
    ubuntu系统安装配置gitlab+Jenkins+发布持续集成持续部署保姆级教程。
    【C++】const关键字的作用
  • 原文地址:https://blog.csdn.net/Qhx20040819/article/details/133235163