Estoy tratando de escalar un círculo SVG con trackpad (moviendo dos dedos hacia arriba/abajo) y el origen de la transformación debe ser la posición del cursor. La escala funciona bien la primera vez, y en cada otro intento el círculo cambia de posición (no debería). Esto se puede ver claramente si el cursor está dentro del círculo y cerca del perímetro del mismo. A continuación se muestra el código.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" content="width=device-width"> <style> .container { position: fixed; left: 0; right: 0; top: 0; bottom: 0; } </style> </head> <body> <div class="container"> <svg id="svg" height="600" width="600"> <circle cx="300" cy="300" r="300" stroke="black" stroke-width="3" fill="white"/> </svg> </div> <script> let scale = 1; const e = document.getElementById("svg"); function wheelZoom(event) { event.preventDefault(); scale += event.deltaY * -0.01; scale = Math.min(Math.max(.5, scale), 2); x = 100*(event.clientX-e.getBoundingClientRect().x)/e.getBoundingClientRect().width; y = 100*(event.clientY-e.getBoundingClientRect().y)/e.getBoundingClientRect().height; e.style.transformOrigin = `${x}% ${y}%`; e.style.transform = `scale(${scale})`; } e.addEventListener("wheel", wheelZoom); </script> </body> </html>No estoy seguro de un par de cosas:
En la siguiente demostración, mantuve el SVG sin cambios, pero escalando el círculo en función de dónde se encuentra dentro del SVG cuando gira la rueda del mouse.
Esperemos que sea lo que estabas buscando.
// let scale = 1; const svg = document.getElementById("svg"); const circle = document.querySelector("svg circle"); // Circle transform. Inits to 1:1 scale (called an "identity transform"). var circleTransform = svg.createSVGMatrix(); // start svg.addEventListener("wheel", wheelZoom); function wheelZoom(event) { event.preventDefault(); // Get the mouse position as SVG coordinates var coords = convertScreenCoordsToSvgCoords(event.clientX, event.clientY); // Calculate an appropriate scale adjustment var scale = 1.0 + (event.deltaY * 0.001); // To scale around the mouse coords, first we transform the coordinate // system so that the origin is at the mouse coords. circleTransform = circleTransform.translate(coords.x, coords.y); // Then we apply the scale circleTransform = circleTransform.scale(scale, scale); // Finally we move the coordinate system back to where it was circleTransform = circleTransform.translate(-coords.x, -coords.y); // Now we need to update the circle's transform var transform = svg.createSVGTransform(); // An SVGTransform DOM object... transform.setMatrix(circleTransform); // set to the new circleTransform... circle.transform.baseVal.initialize(transform); // and used to update the circle transform property } function convertScreenCoordsToSvgCoords(x, y) { var pt = svg.createSVGPoint(); // An SVGPoint SVG DOM object pt.x = x; pt.y = y; // getScreenCTM tells us the combined transform that determines where // the circle is rendered. Including any viewBox. // We use the inverse of that to convert the mouse X and Y to their // equivalent values inside the SVG. pt = pt.matrixTransform(circle.getScreenCTM().inverse()); return {'x': pt.x, 'y': pt.y}; } svg { background-color: linen; } <div class="container"> <svg id="svg" height="600" width="600"> <circle cx="300" cy="300" r="300" stroke="black" stroke-width="3" fill="white"/> </svg> </div>