JavaScript与SVG实现圆形拖拽时适配画布缩放的偏移量计算问题
SVG圆形拖拽偏移量适配方案(兼容画布缩放/平移场景)
核心实现逻辑
在圆形被点击按下的瞬间,将点击坐标转换为SVG内部坐标系的点,计算该点与当前圆心的偏移值,拖拽过程中用转换后的鼠标坐标减去该偏移值再更新圆心位置,即可避免圆心直接对齐鼠标指针的问题,该方案天然适配画布的缩放、平移变换,不需要额外处理变换矩阵的影响。
具体修改点
- 新增变量存储圆形拖拽的坐标系偏移量
- 在圆形的
pointerdown事件中计算偏移量 - 在圆形拖拽逻辑中使用偏移量修正圆心位置
完整可运行代码
HTML代码
<div id="around"> <svg id="canvas" style="border: 1px solid blue;"> <g id="viewport"> <rect x="100" y="100" width="400" height="200" fill="red"/> <circle r="10" cx="600" cy="600" fill="blue"/> </g> </svg> </div>
CSS代码
#around{ display: flex; width: 100%; height: 400px; padding: 20px; border: 1px dashed orange; } #canvas{ flex: 1; height: auto; }
JS代码(已修改偏移量逻辑)
var svgCanvas = document.getElementById("canvas"); var viewPort = document.getElementById("viewport"); var drag = false; var dragged = false; var offset = { x: 0, y: 0 }; var factor = .1; var matrix = new DOMMatrix(); var whatToMove = function (event) { }; svgCanvas.addEventListener('pointerdown', function (event) { drag = true; whatToMove = moveViewPort; offset = { x: event.clientX, y: event.clientY }; }); document.addEventListener('pointermove', function (event) { if (event.buttons == 1) { whatToMove(event); } }); var moveViewPort = function (event) { if (drag) { var tx = event.clientX - offset.x; var ty = event.clientY - offset.y; offset = { x: event.clientX, y: event.clientY }; matrix.preMultiplySelf(new DOMMatrix() .translateSelf(tx, ty)); viewPort.style.transform = matrix.toString(); dragged = true; } }; svgCanvas.addEventListener('pointerup', function (event) { if (!dragged) { var c = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); var point = new DOMPoint(event.offsetX, event.offsetY); var tPoint = matrix.inverse().transformPoint(point); c.cx.baseVal.value = tPoint.x; c.cy.baseVal.value = tPoint.y; c.r.baseVal.value = 10; c.style.fill = "blue"; var cdrag = false; // 新增:存储圆形拖拽偏移量 var circleOffset = { x: 0, y: 0 }; c.addEventListener('pointerdown', function (event) { event.stopPropagation(); cdrag = true; whatToMove = moveCircle; // 新增:计算按下时鼠标与圆心的SVG坐标系偏移 var rect = svgCanvas.getBoundingClientRect(); var x = event.clientX - rect.left; var y = event.clientY - rect.top; var clickPoint = matrix.inverse().transformPoint(new DOMPoint(x, y)); circleOffset.x = clickPoint.x - c.cx.baseVal.value; circleOffset.y = clickPoint.y - c.cy.baseVal.value; }); var moveCircle = function (event) { if (cdrag) { var rect = svgCanvas.getBoundingClientRect(); var x = event.clientX - rect.left; var y = event.clientY - rect.top; var point_1 = new DOMPoint(x, y); var tPoint_1 = matrix.inverse().transformPoint(point_1); // 修改:减去偏移量再赋值给圆心 c.cx.baseVal.value = tPoint_1.x - circleOffset.x; c.cy.baseVal.value = tPoint_1.y - circleOffset.y; } }; c.addEventListener('pointerup', function (event) { event.stopPropagation(); cdrag = false; whatToMove = function () { }; }); viewPort.appendChild(c); } drag = false; whatToMove = function () { }; dragged = false; }); document.addEventListener('wheel', function (event) { event.preventDefault(); var zoom = event.deltaY > 0 ? -1 : 1; var scale = 1 + factor * zoom; offset = { x: event.offsetX, y: event.offsetY }; matrix.preMultiplySelf(new DOMMatrix() .translateSelf(offset.x, offset.y) .scaleSelf(scale, scale) .translateSelf(-offset.x, -offset.y)); viewPort.style.transform = matrix.toString(); }, { passive:false });
内容的提问来源于stack exchange,提问作者Bojan Vukasovic
相关产品推荐
相关产品推荐

