求助:实现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
相关产品推荐
相关产品推荐

