Svg rotation, scaling and translation with the mouse

Attempted to rotate, move, and resize with the mouse on an SVG element. You can check it out here.

Currently, I have been working on southern management, central management and turn control.

  • Spinning works fine, I can spin, stop and spin again. But after I move the element by dragging the center point, the rotation starts to blink first, and the initial rotation point is different. I believe this is because the position of the center changes after the transfer. I tried to redistribute the position of the center, but it did not work.

  • And scaling moves the element, not increases the size.

Please help me with this. I am missing some settings here.

Note. First you drew some kind of path with the mouse to get the controls.

var svg = document.querySelector('.container'); var svgns = 'http://www.w3.org/2000/svg'; var path = document.createElementNS(svgns, 'path'); svg.appendChild(path); var points = []; var Resizer_Instance = null; var boundingBox = svg.getBoundingClientRect(); var toSVGPath = function(points) { var SVGPath = ''; for (var i = 0; i < points.length; i++) { var prefix = (i == 0) ? 'M' : 'L'; SVGPath += prefix + points[i].x + ' ' + points[i].y + ' '; } return SVGPath; }; var create_mousedown = false; var createStart = function(event) { create_mousedown = true; }; var creating = function(event) { if (create_mousedown) { var point = svg.createSVGPoint(); point.x = event.clientX - boundingBox.left; point.y = event.clientY - boundingBox.top; var t = point.matrixTransform(svg.getScreenCTM().inverse()); points.push(t); path.setAttributeNS(null, 'd', toSVGPath(points)); } }; var createEnd = function(event) { create_mousedown = true; svg.removeEventListener('mousedown', createStart); svg.removeEventListener('mousemove', creating); svg.removeEventListener('mouseup', createEnd); setTimeout(function functionName() { Resizer_Instance = new Resizer(path, svg); }, 500); }; svg.addEventListener('mousedown', createStart); svg.addEventListener('mousemove', creating); svg.addEventListener('mouseup', createEnd); var Resizer = (function() { function Resizer(element) { var that = this; that.element = element; createSelector.call(that); document.addEventListener('mousemove', dragging); document.addEventListener('mouseup', dragend); } var RAD2DEG = 180 / Math.PI; function angleBetweenPoints(p1, p2) { var angle = null; if (p1.x == p2.x && p1.y == p2.y) angle = Math.PI / 2; else angle = Math.atan2(p2.y - p1.y, p2.x - p1.x); return (angle * RAD2DEG) + -90; } function controlPositions(el) { var pt = svg.createSVGPoint(); var bbox = el.getBoundingClientRect(); var matrix = el.getScreenCTM().inverse(); var halfWidth = bbox.width / 2; var halfHeight = bbox.height / 2; var placements = {}; pt.x = bbox.left; pt.y = bbox.top; placements['nw'] = pt.matrixTransform(matrix); pt.x += halfWidth; placements['n'] = pt.matrixTransform(matrix); pt.x += halfWidth; placements['ne'] = pt.matrixTransform(matrix); pt.y += halfHeight; placements['e'] = pt.matrixTransform(matrix); pt.y += halfHeight; placements['se'] = pt.matrixTransform(svg.getScreenCTM().inverse()); pt.x -= halfWidth; placements['s'] = pt.matrixTransform(matrix); pt.x -= halfWidth; placements['sw'] = pt.matrixTransform(matrix); pt.y -= halfHeight; placements['w'] = pt.matrixTransform(matrix); pt.x += halfWidth; placements['center'] = pt.matrixTransform(matrix); pt.y -= (halfHeight + 30); placements['rot'] = pt.matrixTransform(matrix); return placements; } var dragging_element = null; var dragstart = function(event) { var box = this; var context = box.context; var rootContext = context.rootContext; rootContext.current_handle_inaction = context.direction; dragging_element = box; }; var dragging = function(event) { if (!dragging_element) return; var box = dragging_element; var context = box.context; var rootContext = context.rootContext; var currentHandle = rootContext.current_handle_inaction; var control_points = rootContext.control_points; if (currentHandle === context.direction) { var point = svg.createSVGPoint(); point.x = event.clientX; point.y = event.clientY; var element = rootContext.element; var transformed = point.matrixTransform(svg.getScreenCTM().inverse()); var centerPosition = context.center; rootContext.angle = rootContext.angle || 0; rootContext.hMove = rootContext.hMove || 0; rootContext.vMove = rootContext.vMove || 0; rootContext.scaleX = rootContext.scaleX || 1; rootContext.scaleY = rootContext.scaleY || 1; switch (currentHandle) { case "rot": rootContext.angle = angleBetweenPoints(transformed, centerPosition); break; case "center": rootContext.hMove = transformed.x - centerPosition.x; rootContext.vMove = transformed.y - centerPosition.y; break; case "s": var startPos = control_points[currentHandle]; var vMove = transformed.y - startPos.y; rootContext.scaleY += (vMove > 0 ? -1 : 1) * 0.001; break; } var move_transform = "translate(" + rootContext.hMove + " " + rootContext.vMove + ")"; var rotate_transform = "rotate(" + rootContext.angle + ", " + centerPosition.x + ", " + centerPosition.y + ")"; var scale_transform = "scale(" + rootContext.scaleX + ", " + rootContext.scaleY + ")"; var transform = [move_transform, rotate_transform, scale_transform].join(' '); rootContext.element.setAttribute('transform', transform); rootContext.controlGroup.setAttribute('transform', transform); } }; var dragend = function() { if (!dragging_element) return; var box = dragging_element; var context = box.context; var rootContext = context.rootContext; delete rootContext.current_handle_inaction; // createSelector.call(rootContext); dragging_element = null; }; var adjustPositions = function() { var that = this; var control_points = that.control_points; var controlGroup = that.controlGroup; var point = svg.createSVGPoint(); for (var direction in control_points) { var dP = control_points[direction]; point.x = dP.x; point.y = dP.y; debugger; control_points[direction] = point.matrixTransform(controlGroup.getScreenCTM().inverse()); } return control_points; }; var Deg2Rad = 0.017453292519943295; var createSelector = function() { var that = this; var points = that.control_points; if (points) { points = adjustPositions.call(that); } else { points = controlPositions(that.element, svg); } that.control_points = points; var existingBoxes = {}; var controlGroup = that.controlGroup; if (!controlGroup) { controlGroup = document.createElementNS(svgns, 'g'); that.controlGroup = controlGroup; svg.appendChild(controlGroup); } that.control_boxes = that.control_boxes || {}; var line_name = "connecting-line", line_element = that.control_boxes['connecting-line']; var line_route = ["nw", "n", "rot", 'n', "ne", "e", "se", "s", "sw", "w", "nw"]; if (!line_element) { line_element = document.createElementNS(svgns, 'path'); line_element.style.cssText = "fill: none; stroke: #f41542; opacity: 0.5"; that.control_boxes[line_name] = line_element; controlGroup.appendChild(line_element); var pathString = ""; line_route.forEach(function(direction) { var point = points[direction]; var command = pathString.length === 0 ? "M" : " L "; pathString += (command + point.x + " " + point.y); }); line_element.setAttribute('d', pathString); } Object.keys(points).forEach(function(direction) { var point = points[direction]; var box = that.control_boxes[direction]; if (!box) { box = document.createElementNS(svgns, 'circle'); box.style.cssText = "fill: #5AABAB"; that.control_boxes[direction] = box; box.setAttributeNS(null, 'r', 3); box.setAttribute('handle', direction); box.addEventListener('mousedown', dragstart.bind(box)); controlGroup.appendChild(box); } box.setAttributeNS(null, 'cx', point.x); box.setAttributeNS(null, 'cy', point.y); box.context = { point: point, direction: direction, rootContext: that, center: points.center }; }); }; var prototype = { constructor: Resizer }; Resizer.prototype = prototype; return Resizer; })(); 
 path { fill: none; stroke: #42B6DF; } body, html { height: 100%; width: 100%; margin: 0; } 
 <svg class="container" version="1.1" baseProfile="full" style="position:absolute;left:0;top:0;height:100%;width:100%;-ms-transform:scale(1,1);transform:scale(1,1);-webkit-transform:scale(1,1);-moz-transform:scale(1,1);-o-transform:scale(1,1);transform:scale(1,1);-ms-transform-origin:0, 0;-webkit-transform-origin:0, 0;-moz-transform-origin:0, 0;-o-transform-origin:0, 0;transform-origin:0, 0" viewBox="-220.38356461849224 6442.3347962008365 454.7376658611161 114.54981723871151"></svg> 
+3
source share
2 answers

You are currently calculating the angle relative to the starting center of the shape (the one you just drew). This is wrong - you need to calculate the angle relative to the center of the figure after the previous move.

Fiddle

I deleted the parts that I did not change.

 var dragging = function(event) { ... if (currentHandle === context.direction) { ... var initialCenterPosition = context.center, // use the coordinates saved after last move or // initial coordinates if there are none saved previousCenterPosition = rootContext.previousCenterPosition || initialCenterPosition; ... switch (currentHandle) { case "rot": rootContext.angle = angleBetweenPoints(transformed, previousCenterPosition); break; case "center": rootContext.hMove = transformed.x - initialCenterPosition.x; rootContext.vMove = transformed.y - initialCenterPosition.y; // remember the new center coordinates rootContext.previousCenterPosition = { x: transformed.x, y: transformed.y }; break; case "s": ... } var move_transform = "translate(" + rootContext.hMove + " " + rootContext.vMove + ")"; var rotate_transform = "rotate(" + rootContext.angle + ", " + initialCenterPosition.x + ", " + initialCenterPosition.y + ")"; var scale_transform = "scale(" + rootContext.scaleX + ", " + rootContext.scaleY + ")"; ... } } 
+1
source

You cannot easily handle all three conversion operations (translate, scale, and rotate) with only three conversion functions. You must use four functions.

You must remember the starting center point of the element. Let me call it ocx and ocy. Then follow these steps:

  • move the starting center point to the beginning
  • to scale
  • make a turn
  • move the center back to the new (current) central position.

So, the transform line will look something like this:

 transform="translate(ncx,ncy) rotate(rot) scale(sx,sy) translate(-ocx,-ocy)" 

This way you can isolate all operations, and you do not need to change others when they change.

+3
source

Source: https://habr.com/ru/post/1011892/


All Articles