How can I make my n-resize display correctly?

2020-07-30 03:39发布

问题:

Here is modified code originally tooken from this post.

Resizing the rectangle on the Right middle, bottom middle and right bottom control point is fine as I don't need to change the translate coordinates in order to make the resize work. Now my problem as you can see is that my resizing on the top middle (same for the left middle, left bottom...) of my rectangle when the rotate angle is different from 0 doesn't work as it visually changes position when I resize it.

I really don't know how to change that so any help is highly appreciated.

note: change angle to 0 in the input field and you'll see the code working properly here's the JSFiddle link.

var input = document.getElementById("rotate_input");
var rotate = document.getElementById("rotate");
var rightMiddle = document.getElementById("rm");
var topMiddle = document.getElementById("tm");
var translate = document.getElementById("trslt");
var scale = document.getElementById("scale");
var svg = document.getElementById("main");

var rotateString = rotate.getAttribute('transform');

var controlrm = false;
var controltm = false;

var origRectWidth = 100;
var origRectHeight = 100;
var updatedRectWidth = origRectWidth;
var updatedRectHeight = origRectHeight;

var xScale = 1;
var yScale = 1;

var translateX = 100;
var translateY = 100;

var relevantMouseMoveDist = 0;

var rotateAnleDeg = 30;
var rectangleAngle = parseInt(rotateString.slice(rotateString.indexOf("(") + 1)) * Math.PI / 180; // retrieve the angle from the DOM

var newMousePosn;
var oldMousePosn;

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

function resizeTopMiddle()
{
  updatedRectHeight -= relevantMouseMoveDistSin;
  yScale = updatedRectHeight/origRectHeight;
  //get the new Y position
  translateY = translateY + relevantMouseMoveDistSin;
  scale.setAttribute('transform', 'scale(' + xScale + ',' + yScale + ')');
  translate.setAttribute('transform', 'translate(' + translateX + ',' + translateY + ')');
}

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
    relevantMouseMoveDistCos = mouseMoveDist * Math.cos(angleDifference);
    relevantMouseMoveDistSin = mouseMoveDist * Math.sin(angleDifference);
    
    // resize the rectangle if necessary
    if (controlrm)
      resizeRightMiddle();
    else if (controltm)
      resizeTopMiddle();
  } else {
  
    // establish the mouse pos'n during the first mousemove event
    newMousePosn = {x: e.clientX, y: e.clientY};
  }
  
});

svg.addEventListener("mouseup"  , function(e){
  controlrm = false;
  controltm = false;});

rightMiddle.addEventListener("mousedown", function(e){controlrm = true ;});

topMiddle.addEventListener("mousedown", function(e){controltm = true ;});

// Code for changing the rectangle in the input
input.addEventListener("change", function (){
  rotateAngleDeg = input.value;
  rotate.setAttribute("transform", "rotate(" + rotateAngleDeg + ")");
  rectangleAngle = rotateAngleDeg * Math.PI / 180;
})
svg {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
<svg id="main" width="1000" height="250">
 <g id= "trslt" transform="translate(100, 100)">
  <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="rm" fill="black" stroke="black" x=95 y=45 width=10 height=10 />
    <rect id="tm" fill="white" stroke="black" x=45 y=-5 width=10 height=10 />
   </g>
  </g>
 </g>
</svg>
<input id="rotate_input" type="text" placeholder="Change Angle"/>

回答1:

There's quite a bit to it and rather difficult to explain the steps, but essentially:

  • Moved Translate to after Rotate within the svg. That made it easier to resize the object without needing to take into account the direction that would be affected by the rotation.
  • I used 3 values to apply the rotation transform="rotate(0 0 0)". The second two give the rotation's centre.
  • Changed the box dimensions to be centred at 0. Not strictly necessary but I preferred thinking about the maths that way.

I've implemented the sides and one corner, I'll leave it with you to follow the pattern - for the Top Left it's just grabbing code from the Top and the code from the Left and combining them like I've done already within Top Right

I see the rotation doesn't always happen around the box's current centre point, so that's something that I presume you'll want to look at correcting.

External demo (same as Stack Snippet below): https://jsfiddle.net/bcjopdqn/2/

var input = document.getElementById("rotate_input");
var rotate = document.getElementById("rotate");
var rightMiddle = document.getElementById("rm");
var leftMiddle = document.getElementById("lm");
var topMiddle = document.getElementById("tm");
var bottomMiddle = document.getElementById("bm");
var topRight = document.getElementById("tr");

var translate = document.getElementById("trslt");
var scale = document.getElementById("scale");

var rotateString = rotate.getAttribute('transform');

var controlrm = false;
var controllm = false;
var controltm = false;
var controlbm = false;
var controltr = false;

var origRectWidth = 100;
var origRectHeight = 100;
var updatedRectWidth = origRectWidth;
var updatedRectHeight = origRectHeight;

var xScale = 1;
var yScale = 1;

var translateX = 100;
var translateY = 100;

var relevantMouseMoveDist = 0;
var relevantMouseMoveDistXCos, relevantMouseMoveDistXSin;
var relevantMouseMoveDistYCos, relevantMouseMoveDistYSin;

var rotateAngleDeg = 0;
var rectangleAngle = parseInt(rotateString.slice(rotateString.indexOf("(") + 1)) * Math.PI / 180; // retrieve the angle from the DOM

var newMousePosn;
var oldMousePosn;

function resizeLeftMiddle() {
  updatedRectWidth -= relevantMouseMoveDistXCos + relevantMouseMoveDistXSin;
  translateX += relevantMouseMoveDistXCos / 2 + relevantMouseMoveDistXSin / 2;
  xScale = updatedRectWidth / origRectWidth;
  scale.setAttribute('transform', 'scale(' + xScale + ',' + yScale + ')');
  translate.setAttribute('transform', 'translate(' + translateX + ',' + translateY + ')');
}

function resizeRightMiddle() {
  updatedRectWidth += relevantMouseMoveDistXCos + relevantMouseMoveDistXSin;
  translateX += relevantMouseMoveDistXCos / 2 + relevantMouseMoveDistXSin / 2;
  xScale = updatedRectWidth / origRectWidth;
  scale.setAttribute('transform', 'scale(' + xScale + ',' + yScale + ')');
  translate.setAttribute('transform', 'translate(' + translateX + ',' + translateY + ')');
}

function resizeTopMiddle() {
  updatedRectHeight -= relevantMouseMoveDistYCos + relevantMouseMoveDistYSin;
  translateY += relevantMouseMoveDistYCos / 2 + relevantMouseMoveDistYSin / 2;
  yScale = updatedRectHeight / origRectHeight;
  scale.setAttribute('transform', 'scale(' + xScale + ',' + yScale + ')');
  translate.setAttribute('transform', 'translate(' + translateX + ',' + translateY + ')');
}

function resizeBottomMiddle() {
  updatedRectHeight += relevantMouseMoveDistYCos + relevantMouseMoveDistYSin;
  translateY += relevantMouseMoveDistYCos / 2 + relevantMouseMoveDistYSin / 2;
  yScale = updatedRectHeight / origRectHeight;
  scale.setAttribute('transform', 'scale(' + xScale + ',' + yScale + ')');
  translate.setAttribute('transform', 'translate(' + translateX + ',' + translateY + ')');
}

function resizeTopRight() {
  updatedRectWidth += relevantMouseMoveDistXCos + relevantMouseMoveDistXSin;
  updatedRectHeight -= relevantMouseMoveDistYCos + relevantMouseMoveDistYSin;
  translateX += relevantMouseMoveDistXCos / 2 + relevantMouseMoveDistXSin / 2;
  translateY += relevantMouseMoveDistYCos / 2 + relevantMouseMoveDistYSin / 2;
  xScale = updatedRectWidth / origRectWidth;
  yScale = updatedRectHeight / origRectHeight;
  scale.setAttribute('transform', 'scale(' + xScale + ',' + yScale + ')');
  translate.setAttribute('transform', 'translate(' + translateX + ',' + translateY + ')');
}

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 +X and +Y orientation
    relevantMouseMoveDistXCos = deltaMouseMove.x * Math.cos(rectangleAngle);
    relevantMouseMoveDistXSin = deltaMouseMove.y * Math.sin(rectangleAngle);

    relevantMouseMoveDistYCos = deltaMouseMove.x * Math.cos(rectangleAngle + Math.PI / 2);
    relevantMouseMoveDistYSin = deltaMouseMove.y * Math.sin(rectangleAngle + Math.PI / 2);

    // resize the rectangle if necessary
    if (controlrm)
      resizeRightMiddle();
    else if (controllm)
      resizeLeftMiddle();
    else if (controltm)
      resizeTopMiddle();
    else if (controlbm)
      resizeBottomMiddle();
    else if (controltr)
      resizeTopRight();
  } else {

    // establish the mouse pos'n during the first mousemove event
    newMousePosn = {
      x: e.clientX,
      y: e.clientY
    };
  }

});

svg.addEventListener("mouseup", function(e) {
  controlrm = false;
  controllm = false;
  controltm = false;
  controlbm = false;
  controltr = false;
});

rightMiddle.addEventListener("mousedown", function(e) {
  controlrm = true;
});
leftMiddle.addEventListener("mousedown", function(e) {
  controllm = true;
});
topMiddle.addEventListener("mousedown", function(e) {
  controltm = true;
});
bottomMiddle.addEventListener("mousedown", function(e) {
  controlbm = true;
});
topRight.addEventListener("mousedown", function(e) {
  controltr = true;
});

// Code for changing the rectangle in the input
input.addEventListener("change", function() {

  rotateAngleDeg = input.value;

  var translation = (translateX + " " + translateY);
  var rotation = "rotate(" + rotateAngleDeg + " " + translation + ")";

  rotate.setAttribute("transform", rotation);
  //console.log(rotation);

  rectangleAngle = rotateAngleDeg * Math.PI / 180;
})
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="250">
    <g id="rotate" transform-origin="0 0" transform="rotate(0 0 0)">
      <g id="trslt" transform="translate(100, 100)">
        <g id="scale">
          <path fill="red" stroke="red" d="M -50 -50 L -50 50 L 50 50 L 50 -50Z" />
          <rect id="rm" fill="black" stroke="black" x=45 y=-5 width=10 height=10 />
          <rect id="lm" fill="yellow" stroke="black" x=-55 y=-5 width=10 height=10 />
          <rect id="tm" fill="white" stroke="black" x=-5 y=-55 width=10 height=10 />
          <rect id="bm" fill="orange" stroke="black" x=-5 y=45 width=10 height=10 />
          <rect id="tr" fill="blue" stroke="black" x=45 y=-55 width=10 height=10 />
        </g>
      </g>
    </g>
  </svg>
</div>
<input id="rotate_input" type="text" placeholder="Change Angle" />



回答2:

Not 100% working, but this should hopefully help you simplify and point you in the right direction. Checkout this function which uses the SVG transform matrix.
https://codepen.io/anon/pen/xrpPBy

You can update your function as so:

function resizeTopMiddle()
{
  updatedRectHeight -= relevantMouseMoveDistSin;
  yScale = updatedRectHeight/origRectHeight;

  generalTX(xScale, yScale, rotateAnleDeg )
}

Here is an example with your use case: https://codepen.io/RTakes/pen/xrpLEe

Hope that helps.