基于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; })();
关键修复点
- 拆分变换对象:分别维护
translateTransform(平移)和scaleTransform(缩放)两个独立的SVGTransform,避免类型覆盖问题。 - 正确计算缩放后的平移:缩放时,先计算鼠标点在元素局部坐标系的位置,缩放后重新计算平移量,确保鼠标点始终在缩放中心。
- 初始化逻辑优化:检查元素已有的变换列表,复用已存在的平移/缩放变换,避免重复创建。
内容的提问来源于stack exchange,提问作者Node.JS
相关产品推荐
相关产品推荐

