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

JavaScript与SVG实现圆形拖拽时适配画布缩放的偏移量计算问题

SVG圆形拖拽偏移量适配方案(兼容画布缩放/平移场景)

核心实现逻辑

在圆形被点击按下的瞬间,将点击坐标转换为SVG内部坐标系的点,计算该点与当前圆心的偏移值,拖拽过程中用转换后的鼠标坐标减去该偏移值再更新圆心位置,即可避免圆心直接对齐鼠标指针的问题,该方案天然适配画布的缩放、平移变换,不需要额外处理变换矩阵的影响。

具体修改点

  1. 新增变量存储圆形拖拽的坐标系偏移量
  2. 在圆形的pointerdown事件中计算偏移量
  3. 在圆形拖拽逻辑中使用偏移量修正圆心位置

完整可运行代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.18 14:36:03