How can I make my cursor synced with the control p

2019-08-20 02:02发布

问题:

I am creating an editor. I would like the basic functions on my objects which are rotate/resize and translate.

I've managed to do the three of them but the only problem is now my mouse position doesn't follow the control points (the problem gets even worst for the other control points not shown below)..

You'll find below an example for the right middle resize with a rotation angle of 30 degrees with no mouseY position, note that the mouse follows perfectly my control point when the rotation angle equals 0.

Is there a way easily solve this problem, am I going the wrong way?

Here's the jsfiddle link where you can change the rotate angle in the code to see by yourself JSiddle link. (Just click and drag the black control point to resize the object)

//convert value of range amin to amax to the range bmin to bmax;
function imap(value, amin, amax, bmin, bmax)
{
	if ((amax - amin))
		return (value - amin) * (bmax - bmin) / (amax - amin) + bmin;
	return (0);
};

//get mouse coordinates from the SVG element
function getMouse(el, e)
{
  var pt = el.createSVGPoint();
	pt.x = e.clientX;
	pt.y = e.clientY;
	var cursorpt =  pt.matrixTransform(el.getScreenCTM().inverse());
	return({x: cursorpt.x, y: cursorpt.y})
};

var controlPoint = document.getElementById("c"); //My control point element

var mouseX;
var mouseXClicked = 0;
var scaleX = 1;
var scaleY = 1;
var scaleXClicked = 1;
var control = false; // sets if resizeRightMiddle() should be executed
var rectWidth = 100; //is normally tooken with a getBBox() function
var scale = document.getElementById("scale");

function resizeRightMiddle()
{
  //convert difference between original mouse X postion on click and actual X mouse position into a scale factor
	plusX = imap(mouseX - mouseXClicked, 0, rectWidth, 0, 1);
  //add converted scale factor to the original x scale value
	resX = scaleXClicked + plusX;
	scale.setAttribute('transform', 'scale(' + resX + ',' + scaleY + ')');
	scaleX = resX;
}

var svg = document.getElementById("main");
// save Scale and X mouse coordinate on click
svg.addEventListener("mousedown", function(e){
	var coord = getMouse(svg, e);
	mouseXClicked = coord.x;
	scaleXClicked = scaleX;
});

svg.addEventListener("mousemove", function(e){
//get mouse coordinates
	var coord = getMouse(svg, e);
	mouseX = coord.x;
// resize if control element has been clicked
	if (control)
		resizeRightMiddle();
});

// desactivate resize
svg.addEventListener("mouseup", function(e){
	control = false;
});

//activate resize
controlPoint.addEventListener("mousedown", function(){
	control = true;
});
svg {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
<div>
<svg id="main" width="1000" height="300">
	<g transform="translate(66, 56)">
		<g id="rotate" transform-origin="center" transform="rotate(30)">
			<g id="scale">
  			<path fill="red" stroke="red" d="M 0 0 L 0 100 L 100 100 L 100 0Z" />
				<rect id="c" fill="black" stroke="black" x=95 y=45 width=10 height=10 />
			</g>
		</g>
	</g>
</svg>
</div>

回答1:

The code below calculates how much the mouse moves in the direction of the rectangle's orientation on each mousemove event, instead of from the beginning of the mousedown to the current mousemove. It then updates updatedRectWidth and uses that to calculate the current desired scale.

var controlPoint = document.getElementById("c");
var control = false;
var origRectWidth = 100;
var scale = document.getElementById("scale");
var relevantMouseMoveDist = 0;
var updatedRectWidth = origRectWidth;
var mouseDownPt = {};
var rotateDiv = document.getElementById("rotate");
var rotateString = rotateDiv.getAttribute('transform');
var rectangleAngle = parseInt(rotateString.slice(rotateString.indexOf("(") + 1)) * Math.PI / 180; // retrieve the angle from the DOM
var relevantMouseMoveDist;
var newMousePosn;
var oldMousePosn;

function resizeRightMiddle()
{
  updatedRectWidth += relevantMouseMoveDist;
  xScale = updatedRectWidth/origRectWidth;
  scale.setAttribute('transform', 'scale(' + xScale + ',1)');
}

var svg = document.getElementById("main");

svg.addEventListener("mousemove", function(e){

  if (newMousePosn) {
  
    // the former mouse pos'n
    oldMousePosn = {x: newMousePosn.x, y: newMousePosn.y};
    
    // the new mouse pos'n
    newMousePosn = {x: e.clientX, y: e.clientY};
    
    // the change in the mouse pos'n coordinates since the last move event
    var deltaMouseMove = {
      x: newMousePosn.x - oldMousePosn.x,
      y: newMousePosn.y - oldMousePosn.y
    };
    
    // the dir'n of this movement
    var angleOfMouseMovement = Math.atan2(deltaMouseMove.y, deltaMouseMove.x);
    
    // the absolute distance the mouse has moved
    var mouseMoveDist = Math.sqrt(
      deltaMouseMove.x * deltaMouseMove.x +
      deltaMouseMove.y * deltaMouseMove.y
    );
     
    // the difference in direction between the mouse movement and orientation of the rectangle
    var angleDifference = angleOfMouseMovement - rectangleAngle;
    
    // the portion of the mouse movement that is in the direction of the rectangle's orientation
    relevantMouseMoveDist = mouseMoveDist * Math.cos(angleDifference);
    
    // resize the rectangle if necessary
    if (control) resizeRightMiddle();
    
  } else {
  
    // establish the mouse pos'n during the first mousemove event
    newMousePosn = {x: e.clientX, y: e.clientY};
  }
  
});

svg         .addEventListener("mouseup"  , function(e){control = false;});
controlPoint.addEventListener("mousedown", function(e){control = true ;});
<div>
<svg id="main" width="1000" height="300">
	<g transform="translate(66, 56)">
		<g id="rotate" transform-origin="center" transform="rotate(40)">
			<g id="scale">
  			<path fill="red" stroke="red" d="M 0 0 L 0 100 L 100 100 L 100 0Z" />
				<rect id="c" fill="black" stroke="black" x=95 y=45 width=10 height=10 />
			</g>
		</g>
	</g>
</svg>
</div>