Как мне правильно настроить отображение n-resize?

Вот модифицированный код, изначально взятый из этого поста.

Изменение размера прямоугольника в правой средней, нижней средней и правой нижней контрольной точке хорошо, так как мне не нужно изменять координаты перевода, чтобы заставить изменение размера работать. Теперь моя проблема, как вы можете видеть, состоит в том, что мое изменение размера в верхней средней (то же самое для левой середины, левого нижнего...) моего прямоугольника, когда угол поворота отличается от 0, не работает, так как визуально меняет положение, когда я изменить его размер

Я действительно не знаю, как это изменить, поэтому любая помощь высоко ценится.

примечание: измените угол на 0 в поле ввода, и вы увидите, что код работает правильно, вот ссылка JSFiddle.

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"/>

2 ответа

Решение

Там довольно много и довольно сложно объяснить шаги, но по существу:

  • Перевезу Translate после Rotate в пределах svg, Это облегчало изменение размера объекта без необходимости учитывать направление, на которое будет влиять вращение.
  • Я использовал 3 значения, чтобы применить вращение transform="rotate(0 0 0)", Вторые два дают центр вращения.
  • Изменил размеры коробки, чтобы они были центрированы на 0. Не строго необходимо, но я предпочел думать о математике таким образом.

Я реализовал стороны и один угол, я оставлю это вам, чтобы следовать шаблону - для верхнего левого угла это просто захват кода сверху и кода слева и объединение их, как я уже сделал в верхней части Правильно

Я вижу, что вращение не всегда происходит вокруг текущей центральной точки бокса, поэтому я предполагаю, что вы захотите посмотреть на исправление.

Внешняя демонстрация (аналогично фрагменту стека ниже): 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" />

Не на 100% работает, но, надеюсь, это поможет вам упростить и направить вас в правильном направлении. Оформить заказ на эту функцию, которая использует матрицу SVG-преобразования.
https://codepen.io/anon/pen/xrpPBy

Вы можете обновить свою функцию следующим образом:

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

  generalTX(xScale, yScale, rotateAnleDeg )
}

Вот пример с вашим примером использования: https://codepen.io/RTakes/pen/xrpLEe

Надеюсь, это поможет.

Другие вопросы по тегам