You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:实现SVG中拖拽蓝色矩形与灰色矩形交换位置功能

SVG拖拽交换位置实现方案

要实现拖动蓝色矩形到灰色矩形上方后交换位置的功能,需要在现有拖拽逻辑基础上添加初始位置记录、碰撞检测和位置交换逻辑,具体修改如下:

1. 修改HTML结构

给可拖拽元素和交换目标添加类标识,方便JS定位:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20" onload="makeDraggable(evt)">
  <rect x="0" y="0" width="30" height="20" fill="#fafafa" />
  <!-- 蓝色矩形:可拖拽 -->
  <rect x="4" y="5" width="8" height="10" fill="#007bff" class="draggable" /> 
  <!-- 灰色矩形:交换目标 + 可拖拽(可选) -->
  <rect x="18" y="5" width="8" height="10" fill="#888" class="draggable swap-target" />
</svg>

2. 完善JS拖拽逻辑

添加初始位置记录、碰撞检测函数,修改拖拽结束逻辑实现交换:

function makeDraggable(evt) {
  var svg = evt.target;
  var selectedElement, offset;
  // 获取交换目标元素(灰色矩形)
  var swapTarget = svg.querySelector('.swap-target');

  svg.addEventListener('mousedown', startDrag);
  svg.addEventListener('mousemove', drag);
  svg.addEventListener('mouseup', endDrag);
  svg.addEventListener('mouseleave', endDrag);

  function startDrag(evt) {
    if (evt.target.classList.contains('draggable')) {
      selectedElement = evt.target;
      offset = getMousePosition(evt);
      offset.x -= parseFloat(selectedElement.getAttributeNS(null, "x"));
      offset.y -= parseFloat(selectedElement.getAttributeNS(null, "y"));
      // 记录当前元素的初始位置,用于交换或复位
      selectedElement.originalX = selectedElement.getAttributeNS(null, "x");
      selectedElement.originalY = selectedElement.getAttributeNS(null, "y");
    }
  }

  function drag(evt) {
    if (selectedElement) {
      evt.preventDefault();
      var coord = getMousePosition(evt);
      selectedElement.setAttributeNS(null, "x", coord.x - offset.x);
      selectedElement.setAttributeNS(null, "y", coord.y - offset.y);
    }
  }

  function endDrag(evt) {
    if (selectedElement && swapTarget) {
      // 判断拖拽元素是否和目标元素重叠
      if (isColliding(selectedElement, swapTarget)) {
        // 交换两者位置
        const targetCurrentX = swapTarget.getAttribute('x');
        const targetCurrentY = swapTarget.getAttribute('y');
        // 目标元素移到拖拽元素的初始位置
        swapTarget.setAttribute('x', selectedElement.originalX);
        swapTarget.setAttribute('y', selectedElement.originalY);
        // 拖拽元素移到目标元素的原位置
        selectedElement.setAttribute('x', targetCurrentX);
        selectedElement.setAttribute('y', targetCurrentY);
      } else {
        // 未碰撞则回到初始位置
        selectedElement.setAttribute('x', selectedElement.originalX);
        selectedElement.setAttribute('y', selectedElement.originalY);
      }
    }
    selectedElement = null;
  }

  // 矩形碰撞检测函数
  function isColliding(elem1, elem2) {
    const rect1 = {
      x: parseFloat(elem1.getAttribute('x')),
      y: parseFloat(elem1.getAttribute('y')),
      width: parseFloat(elem1.getAttribute('width')),
      height: parseFloat(elem1.getAttribute('height'))
    };
    const rect2 = {
      x: parseFloat(elem2.getAttribute('x')),
      y: parseFloat(elem2.getAttribute('y')),
      width: parseFloat(elem2.getAttribute('width')),
      height: parseFloat(elem2.getAttribute('height'))
    };
    // 反向判断:只要不满足"完全不重叠"的任一条件,即为重叠
    return !(rect1.x + rect1.width < rect2.x ||
             rect1.x > rect2.x + rect2.width ||
             rect1.y + rect1.height < rect2.y ||
             rect1.y > rect2.y + rect2.height);
  }

  function getMousePosition(evt) {
    var CTM = svg.getScreenCTM();
    return {
      x: (evt.clientX - CTM.e) / CTM.a,
      y: (evt.clientY - CTM.f) / CTM.d
    };
  }
}

3. 核心逻辑说明

  • 初始位置记录:在拖拽开始时,保存拖拽元素的原始坐标,确保交换时能准确定位目标位置。
  • 碰撞检测:通过判断两个矩形的边界是否重叠,确定是否触发交换动作。
  • 位置交换:拖拽结束时,若检测到碰撞,则交换两个矩形的x、y属性值;若未碰撞,拖拽元素回到初始位置。

内容的提问来源于stack exchange,提问作者Arturo Martinez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 03:45:39