You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用IntersectionObserver检测拖拽元素与未添加.panel--expanded类的.panel元素重叠失败的问题排查

正确姿势:拖拽时实时做碰撞检测

要实现“拖拽元素碰到未展开panel就提示”,咱们得在拖拽过程中实时计算两个元素的边界框,然后判断它们是否相交。具体怎么做呢?

1. 核心方法:用getBoundingClientRect()拿元素边界

每个元素调用getBoundingClientRect()都会返回一个包含topleftrightbottom的对象,通过这些值就能判断两个矩形是否重叠:

function isOverlapping(rectA, rectB) {
  // 只有x轴和y轴都有交集,才算重叠
  return rectA.right > rectB.left &&
         rectA.left < rectB.right &&
         rectA.bottom > rectB.top &&
         rectA.top < rectB.bottom;
}

2. 绑定拖拽事件,实时检测

因为你用了原生draggable属性,咱们可以监听drag事件(拖拽过程中会持续触发),在回调里更新位置+检测碰撞:

完整可运行代码

<div class="panel">panel without panel--expanded class</div>
<div class="draggable" draggable="true">draggable element</div>
<div class="panel panel--expanded">panel with panel--expanded class</div>
.panel {
  background-color: lightblue;
  height: 100px;
  margin: 50px 0px;
  width: 500px;
}
.panel--expanded {
  background-color: coral;
}
.draggable {
  background-color: lightgreen;
  box-shadow: 5px 5px 5px 5px lightgray;
  height: 50px;
  width: 300px;
  position: absolute; /* 让元素能自由拖拽移动 */
  cursor: grab;
}
.draggable:active {
  cursor: grabbing;
}
const draggable = document.querySelector('.draggable');
const targetPanels = document.querySelectorAll('.panel:not(.panel--expanded)');
let isAlerted = false; // 避免重复弹窗的标记

// 拖拽开始时记录初始偏移
draggable.addEventListener('dragstart', (e) => {
  const rect = draggable.getBoundingClientRect();
  // 记录鼠标相对于元素左上角的位置
  e.dataTransfer.setData('text/plain', ''); // 原生拖拽必须设置数据,否则可能失效
  draggable.dataset.initialX = e.clientX - rect.left;
  draggable.dataset.initialY = e.clientY - rect.top;
});

// 拖拽过程中实时更新位置+检测碰撞
draggable.addEventListener('drag', (e) => {
  // 计算拖拽后的位置
  const x = e.clientX - Number(draggable.dataset.initialX);
  const y = e.clientY - Number(draggable.dataset.initialY);
  draggable.style.left = `${x}px`;
  draggable.style.top = `${y}px`;

  // 获取拖拽元素的边界框
  const dragRect = draggable.getBoundingClientRect();

  // 检查每个目标panel是否重叠
  targetPanels.forEach(panel => {
    const panelRect = panel.getBoundingClientRect();
    if (isOverlapping(dragRect, panelRect) && !isAlerted) {
      alert('OVERLAPPING!');
      isAlerted = true;
      // 1秒后重置标记,允许再次触发
      setTimeout(() => isAlerted = false, 1000);
    }
  });
});

// 碰撞检测工具函数
function isOverlapping(rectA, rectB) {
  return rectA.right > rectB.left &&
         rectA.left < rectB.right &&
         rectA.bottom > rectB.top &&
         rectA.top < rectB.bottom;
}

几个小细节说明

  • .draggable加了position: absolute:原生draggable默认不会改变元素位置,必须手动设置定位才能让它跟着鼠标动。
  • 加了isAlerted标记:避免拖拽过程中连续弹出alert,影响体验。
  • 如果不想用原生拖拽,换成mousedown/mousemove/mouseup实现自定义拖拽,逻辑完全一样,只是事件不同。

内容的提问来源于stack exchange,提问作者webta.st.ic

火山引擎 最新活动