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

基于transform实现SVG可缩放遇阻:拖拽正常缩放失效

问题原因及修复方案

你的缩放功能失效,核心问题是错误地复用了同一个SVGTransform对象同时处理平移和缩放。SVG的单个SVGTransform只能表示一种变换类型(平移/缩放/旋转等),调用setScale会把原本的平移变换覆盖成缩放变换,导致后续逻辑混乱,缩放也无法正确生效。

修复后的代码

var makeDraggable = (function() {
    function makeDraggable(svg, element) {
        var selected = false;
        var translateTransform, scaleTransform;
        var container = svg.parentElement;

        // 初始化平移变换
        var transforms = element.transform.baseVal;
        if (transforms.length === 0 || !Array.from(transforms).some(t => t.type === SVGTransform.SVG_TRANSFORM_TRANSLATE)) {
            translateTransform = svg.createSVGTransform();
            translateTransform.setTranslate(0, 0);
            transforms.appendItem(translateTransform);
        } else {
            translateTransform = Array.from(transforms).find(t => t.type === SVGTransform.SVG_TRANSFORM_TRANSLATE);
        }

        // 初始化缩放变换
        if (!Array.from(transforms).some(t => t.type === SVGTransform.SVG_TRANSFORM_SCALE)) {
            scaleTransform = svg.createSVGTransform();
            scaleTransform.setScale(1, 1);
            transforms.appendItem(scaleTransform);
        } else {
            scaleTransform = Array.from(transforms).find(t => t.type === SVGTransform.SVG_TRANSFORM_SCALE);
        }

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

        function getMousePosition(evt) {
            var CTM = svg.getScreenCTM();
            if (evt.touches) { evt = evt.touches[0]; }
            // 计算鼠标在SVG坐标系下的原始位置(不受元素变换影响)
            return {
                x: (evt.clientX - CTM.e) / CTM.a,
                y: (evt.clientY - CTM.f) / CTM.d
            };
        }

        function startDrag(evt) {
            selected = element;
            var coord = getMousePosition(evt);
            // 计算鼠标相对于元素当前平移后的偏移量
            offset = {
                x: coord.x - translateTransform.matrix.e,
                y: coord.y - translateTransform.matrix.f
            };
        }

        function drag(evt) {
            if (selected) {
                evt.preventDefault();
                var coord = getMousePosition(evt);
                translateTransform.setTranslate(coord.x - offset.x, coord.y - offset.y);
            }
        }

        function endDrag(evt) {
            selected = false;
        }

        function zoom(evt) {
            evt.preventDefault();

            var currentScale = scaleTransform.matrix.a;
            var scaleStep = evt.deltaY > 0 ? 0.8 : 1.25;
            var newScale = Math.max(0.1, Math.min(currentScale * scaleStep, 10));
            
            var mousePos = getMousePosition(evt);
            // 计算缩放前后,鼠标点在元素局部坐标系中的位置变化,调整平移量
            var preScaleX = (mousePos.x - translateTransform.matrix.e) / currentScale;
            var preScaleY = (mousePos.y - translateTransform.matrix.f) / currentScale;
            
            // 更新缩放
            scaleTransform.setScale(newScale, newScale);
            
            // 调整平移,保持鼠标点位置不变
            var newTranslateX = mousePos.x - preScaleX * newScale;
            var newTranslateY = mousePos.y - preScaleY * newScale;
            translateTransform.setTranslate(newTranslateX, newTranslateY);
        }
    }

    return makeDraggable;
})();

关键修复点

  1. 拆分变换对象:分别维护translateTransform(平移)和scaleTransform(缩放)两个独立的SVGTransform,避免类型覆盖问题。
  2. 正确计算缩放后的平移:缩放时,先计算鼠标点在元素局部坐标系的位置,缩放后重新计算平移量,确保鼠标点始终在缩放中心。
  3. 初始化逻辑优化:检查元素已有的变换列表,复用已存在的平移/缩放变换,避免重复创建。

内容的提问来源于stack exchange,提问作者Node.JS

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 18:07:05