Как я могу синхронизировать курсор с контрольной точкой во время изменения размера?

Я создаю редактор. Я хотел бы, чтобы основные функции на моих объектах, которые вращаются / изменяют размер и переводят.

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

Ниже вы найдете пример правого среднего изменения размера с углом поворота 30 градусов без положения mouseY, обратите внимание, что мышь отлично следует моей контрольной точке, когда угол поворота равен 0.

Есть ли способ легко решить эту проблему, я иду не в ту сторону?

Вот ссылка jsfiddle, где вы можете изменить угол поворота в коде, чтобы увидеть ссылку JSiddle самостоятельно. (Просто нажмите и перетащите черную контрольную точку, чтобы изменить размер объекта)

//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 ответ

Решение

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

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>

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