Проблема правильного позиционирования точек прямоугольника при его повороте

383
20 мая 2021, 12:30

Ссылка: jsfiddle

Используемая библиотека Raphael.js и плагин Raphael.FreeTransform.

Используемые технологии: SVG, JavaScript.

Строки:

инициализация холста: 125;

функция для перемещения точек: 260.

Проблема: Если повернуть прямоугольник на определенный угол поворота и после попробовать передвинуть точки для изменения его размера, то точки будут неправильно себя вести при перемещении.

Помогите, пожалуйста, исправить данную проблему..

(function(win, Raphael) { 
    'use strict'; 
 
    function Paper() { 
        var self = this; 
 
        self.elementId = 'paper'; 
        self.width     = win.innerWidth; 
        self.height    = win.innerHeight; 
        self.paper     = {}; 
    } 
 
    Paper.prototype.Init = function(args) { 
        var self = this; 
        var elementId; 
        var width; 
        var height; 
 
        try { 
            args = args || {}; 
 
            elementId = args.id || self.elementId; 
            width     = args.width || self.width; 
            height    = args.height || self.height; 
 
            self.paper = Raphael(elementId, width, height); 
        } 
        catch(error) { 
            console.error(error); 
        } 
 
        return self; 
    }; 
 
    Paper.prototype.get = function() { 
        var paper = {}; 
 
        try { 
            paper = this.paper; 
        } 
        catch(error) { 
            console.error(error); 
        } 
 
        return paper; 
    }; 
 
    win.Paper = win.Paper || new Paper; 
})(typeof window !== 'undefined' ? window : this, Raphael); 
 
(function(win) { 
    'use strict'; 
 
    if(typeof win.Paper !== 'object') { 
        return false; 
    } 
 
    function Shape() { 
        var self = this; 
 
        self.properties = { 
            circleOptions : { 
                fill            : '#FFD100', 
                fillOpacity     : 1, 
                stroke          : '#FFD100', 
                strokeOpacity   : 1, 
                strokeWidth     : 3, 
                strokeLinecap   : 'round', 
                strokeLinejoin  : 'round', 
                strokeDasharray : 0 
            }, 
            rectOptions   : { 
                fill            : '#FFF20F', 
                fillOpacity     : 1, 
                stroke          : '#FFD103', 
                strokeOpacity   : 1, 
                strokeWidth     : 3, 
                strokeLinecap   : 'round', 
                strokeLinejoin  : 'round', 
                strokeDasharray : 0 
            } 
        }; 
    } 
 
    Shape.prototype.addPoint = function(x, y, r, properties) { 
        var self    = this; 
        var element = {}; 
 
        try { 
            x          = x || 0; 
            y          = y || 0; 
            r          = r || 5; 
            properties = properties || {}; 
 
            element = win.Paper.get().circle(x, y, r).attr({ 
                'fill'             : properties.fill || self.properties.circleOptions.fill, 
                'fill-opacity'     : properties.fillOpacity || self.properties.circleOptions.fillOpacity, 
                'stroke'           : properties.stroke || self.properties.circleOptions.stroke, 
                'stroke-opacity'   : properties.strokeOpacity || self.properties.circleOptions.strokeOpacity, 
                'stroke-width'     : properties.strokeWidth || self.properties.circleOptions.strokeWidth, 
                'stroke-linecap'   : properties.strokeLinecap || self.properties.circleOptions.strokeLinecap, 
                'stroke-linejoin'  : properties.strokeLinejoin || self.properties.circleOptions.strokeLinejoin, 
                'stroke-dasharray' : properties.strokeDasharray || self.properties.circleOptions.strokeDasharray 
            }); 
 
            element[0].setAttributeNS(null, 'id', element.id); 
        } 
        catch(error) { 
            console.error(error); 
        } 
 
        return { 
            'element' : element, 
            'coords'  : { 
                'x' : x, 
                'y' : y, 
                'r' : r 
            } 
        }; 
    }; 
 
    win.Paper.Shape = new Shape(); 
})(typeof window !== 'undefined' ? window : this); 
 
(function(win) { 
  var groupElements; 
  var radius = 10; 
  var pathData; 
  var rectangle; 
  var point1; 
  var point2; 
  var point3; 
  var point4; 
  var _objects = { 
    'paths'  : {}, 
    'points' : {} 
  }; 
  var path = {}; 
  var pointElements = {}; 
  var customizationOptions = { 
    fill           : '#FFFFFF', 
    fillOpacity    : 1, 
    stroke         : '#000000', 
    strokeOpacity  : 0.5, 
    strokeWidth    : 1, 
    strokeLinecap  : 'round', 
    strokeLinejoin : 'round' 
  }; 
  var freeTransformOptions          = { 
    attrs     : { 
      'fill'            : customizationOptions.fill, 
      'fill-opacity'    : customizationOptions.fillOpacity, 
      'stroke'          : customizationOptions.stroke, 
      'stroke-opacity'  : customizationOptions.strokeOpacity, 
      'stroke-width'    : customizationOptions.strokeWidth, 
      'stroke-linecap'  : customizationOptions.strokeLinecap, 
      'stroke-linejoin' : customizationOptions.strokeLinejoin 
    }, 
    draw      : [], 
    distance  : 0, 
    drag      : [], 
    keepRatio : [], 
    range     : { 
      rotate : [-180, 180], 
      scale  : [-99999, 99999] 
    }, 
    rotate    : [], 
    scale     : [], 
    size      : 0, 
    snap      : { 
      drag   : 0, 
      rotate : 0, 
      scale  : 0 
    }, 
    snapDist  : { 
      drag   : 0, 
      rotate : 0, 
      scale  : 0 
    } 
  }; 
  var freeTransformObj; 
  var resizeModule; 
   
  // style options 
  win.Paper.Shape.properties.rectOptions.strokeOpacity                = 0.5; 
  win.Paper.Shape.properties.rectOptions.fillOpacity                  = 0.2; 
  win.Paper.Shape.properties.circleOptions.radius                     = 10; 
  win.Paper.Shape.properties.circleOptions.strokeWidth                = 0; 
  win.Paper.Shape.properties.circleOptions.fillOpacity                = 0.7; 
   
  // rotate options 
  freeTransformOptions.distance = 1.3; 
  freeTransformOptions.size     = 5; 
  freeTransformOptions.rotate   = ['axisX', 'axisY']; 
 
  win.Paper.Init({ 
    id     : 'paper', 
    width  : 1024, 
    height : 1024 
  }); 
   
  pathData = [['M', 105, 105], ['L', 250, 105], ['L', 250, 150], ['L', 105, 150], ['Z']]; 
  rectangle = win.Paper.get().path(pathData).attr({ 
    'fill'             : win.Paper.Shape.properties.rectOptions.fill, 
    'fill-opacity'     : win.Paper.Shape.properties.rectOptions.fillOpacity, 
    'stroke'           : win.Paper.Shape.properties.rectOptions.stroke, 
    'stroke-opacity'   : win.Paper.Shape.properties.rectOptions.strokeOpacity, 
    'stroke-width'     : win.Paper.Shape.properties.rectOptions.strokeWidth, 
    'stroke-linecap'   : win.Paper.Shape.properties.rectOptions.strokeLinecap, 
    'stroke-linejoin'  : win.Paper.Shape.properties.rectOptions.strokeLinejoin, 
    'stroke-dasharray' : win.Paper.Shape.properties.rectOptions.strokeDasharray 
	}); 
  point1 = win.Paper.Shape.addPoint(pathData[0][1], pathData[0][2], radius).element; 
  point2 = win.Paper.Shape.addPoint(pathData[1][1], pathData[1][2], radius).element; 
  point3 = win.Paper.Shape.addPoint(pathData[2][1], pathData[2][2], radius).element; 
  point4 = win.Paper.Shape.addPoint(pathData[3][1], pathData[3][2], radius).element; 
   
  groupElements = win.Paper.get().set(); 
  groupElements.push(rectangle); 
  groupElements.push(point1); 
  groupElements.push(point2); 
  groupElements.push(point3); 
  groupElements.push(point4); 
   
  for(var key in groupElements) { 
    if(groupElements.hasOwnProperty(key)) { 
      if(groupElements[key].type === 'path') { 
        path['element'] = groupElements[key]; 
 
        _objects.paths[groupElements[key].id] = { 
          path          : path, 
          pointElements : pointElements 
        }; 
      } 
      else { 
        if(groupElements[key].type === 'circle') { 
          pointElements[groupElements[key].id] = { 
            path         : path, 
            pointElement : groupElements[key], 
            index        : key - 1 
          }; 
 
          _objects.points[groupElements[key].id] = pointElements[groupElements[key].id]; 
        } 
      } 
    } 
  } 
   
  freeTransformObj =  win.Paper.get().freeTransform(groupElements, freeTransformOptions); 
  resizeModule = new resize(); 
   
  groupElements.drag(function onmove(dx, dy, x, y, event) { 
    resizeModule.move(dx, dy); 
  }, function onstart(x, y, event) { 
    resizeModule.start(x, y, event); 
  }, function onend(event) { 
    resizeModule.end(event); 
  }); 
   
  function resize() { 
  	var pathElement  = {}; 
    var currentElement; 
    var path         = []; 
    var index        = 0; 
    var pointElement = {}; 
    var posX         = 0; 
    var posY         = 0; 
 
    this.start = function(x, y, event) { 
      if(currentElement = event.target || event.srcElement) { 
        if(_objects.points[currentElement.id]) { 
          pointElement = _objects.points[currentElement.id].pointElement; 
          pathElement  = _objects.points[pointElement.id].path.element; 
          index        = _objects.points[pointElement.id].index; 
 
          posX = pointElement.attrs.cx; 
          posY = pointElement.attrs.cy; 
 
          path   = pathElement.getPath(); 
        } 
      } 
    }; 
 
    this.move = function(dx, dy) { 
      var x           = 0; 
      var y           = 0; 
      var pointElements; 
      var bbox; 
      var middlePoint = {}; 
      var point; 
 
      if(currentElement.nodeName === 'circle' && path.length) { 
        x = posX + dx; 
        y = posY + dy; 
         
        pointElement.attr({cx : x}); 
        path[index][1] = x; 
 
        pointElement.attr({cy : y}); 
        path[index][2] = y; 
 
        pathElement.attr({ 
          path : path 
        }); 
      } 
    }; 
 
    this.end = function(event) {}; 
  } 
})(typeof window !== 'undefined' ? window : this);
#paper { 
  min-height : 768px; 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.2.8/raphael.js"></script> 
<script src="https://alias.io/raphael/free_transform/raphael.free_transform/raphael.free_transform.js"></script> 
 
<div id="paper"></div>

Answer 1

В случае с поворотом координата при перемещении мышью зависит от обоих смещений(по X и по Y), необходимо учесть угол поворота, как то так:

this.move = function(dx, dy) {
    ...
    let angle = groupElements.freeTransform.attrs.rotate/180*Math.PI;
    y = posY + dy*Math.cos(angle) - dx*Math.sin(angle);
    x = posX + dy*Math.sin(angle) + dx*Math.cos(angle);
    ...
}

(function(win, Raphael) { 
    'use strict'; 
 
    function Paper() { 
        var self = this; 
 
        self.elementId = 'paper'; 
        self.width     = win.innerWidth; 
        self.height    = win.innerHeight; 
        self.paper     = {}; 
    } 
 
    Paper.prototype.Init = function(args) { 
        var self = this; 
        var elementId; 
        var width; 
        var height; 
 
        try { 
            args = args || {}; 
 
            elementId = args.id || self.elementId; 
            width     = args.width || self.width; 
            height    = args.height || self.height; 
 
            self.paper = Raphael(elementId, width, height); 
        } 
        catch(error) { 
            console.error(error); 
        } 
 
        return self; 
    }; 
 
    Paper.prototype.get = function() { 
        var paper = {}; 
 
        try { 
            paper = this.paper; 
        } 
        catch(error) { 
            console.error(error); 
        } 
 
        return paper; 
    }; 
 
    win.Paper = win.Paper || new Paper; 
})(typeof window !== 'undefined' ? window : this, Raphael); 
 
(function(win) { 
    'use strict'; 
 
    if(typeof win.Paper !== 'object') { 
        return false; 
    } 
 
    function Shape() { 
        var self = this; 
 
        self.properties = { 
            circleOptions : { 
                fill            : '#FFD100', 
                fillOpacity     : 1, 
                stroke          : '#FFD100', 
                strokeOpacity   : 1, 
                strokeWidth     : 3, 
                strokeLinecap   : 'round', 
                strokeLinejoin  : 'round', 
                strokeDasharray : 0 
            }, 
            rectOptions   : { 
                fill            : '#FFF20F', 
                fillOpacity     : 1, 
                stroke          : '#FFD103', 
                strokeOpacity   : 1, 
                strokeWidth     : 3, 
                strokeLinecap   : 'round', 
                strokeLinejoin  : 'round', 
                strokeDasharray : 0 
            } 
        }; 
    } 
 
    Shape.prototype.addPoint = function(x, y, r, properties) { 
        var self    = this; 
        var element = {}; 
 
        try { 
            x          = x || 0; 
            y          = y || 0; 
            r          = r || 5; 
            properties = properties || {}; 
 
            element = win.Paper.get().circle(x, y, r).attr({ 
                'fill'             : properties.fill || self.properties.circleOptions.fill, 
                'fill-opacity'     : properties.fillOpacity || self.properties.circleOptions.fillOpacity, 
                'stroke'           : properties.stroke || self.properties.circleOptions.stroke, 
                'stroke-opacity'   : properties.strokeOpacity || self.properties.circleOptions.strokeOpacity, 
                'stroke-width'     : properties.strokeWidth || self.properties.circleOptions.strokeWidth, 
                'stroke-linecap'   : properties.strokeLinecap || self.properties.circleOptions.strokeLinecap, 
                'stroke-linejoin'  : properties.strokeLinejoin || self.properties.circleOptions.strokeLinejoin, 
                'stroke-dasharray' : properties.strokeDasharray || self.properties.circleOptions.strokeDasharray 
            }); 
 
            element[0].setAttributeNS(null, 'id', element.id); 
        } 
        catch(error) { 
            console.error(error); 
        } 
 
        return { 
            'element' : element, 
            'coords'  : { 
                'x' : x, 
                'y' : y, 
                'r' : r 
            } 
        }; 
    }; 
 
    win.Paper.Shape = new Shape(); 
})(typeof window !== 'undefined' ? window : this); 
 
(function(win) { 
  var groupElements; 
  var radius = 10; 
  var pathData; 
  var rectangle; 
  var point1; 
  var point2; 
  var point3; 
  var point4; 
  var _objects = { 
    'paths'  : {}, 
    'points' : {} 
  }; 
  var path = {}; 
  var pointElements = {}; 
  var customizationOptions = { 
    fill           : '#FFFFFF', 
    fillOpacity    : 1, 
    stroke         : '#000000', 
    strokeOpacity  : 0.5, 
    strokeWidth    : 1, 
    strokeLinecap  : 'round', 
    strokeLinejoin : 'round' 
  }; 
  var freeTransformOptions          = { 
    attrs     : { 
      'fill'            : customizationOptions.fill, 
      'fill-opacity'    : customizationOptions.fillOpacity, 
      'stroke'          : customizationOptions.stroke, 
      'stroke-opacity'  : customizationOptions.strokeOpacity, 
      'stroke-width'    : customizationOptions.strokeWidth, 
      'stroke-linecap'  : customizationOptions.strokeLinecap, 
      'stroke-linejoin' : customizationOptions.strokeLinejoin 
    }, 
    draw      : [], 
    distance  : 0, 
    drag      : [], 
    keepRatio : [], 
    range     : { 
      rotate : [-180, 180], 
      scale  : [-99999, 99999] 
    }, 
    rotate    : [], 
    scale     : [], 
    size      : 0, 
    snap      : { 
      drag   : 0, 
      rotate : 0, 
      scale  : 0 
    }, 
    snapDist  : { 
      drag   : 0, 
      rotate : 0, 
      scale  : 0 
    } 
  }; 
  var freeTransformObj; 
  var resizeModule; 
   
  // style options 
  win.Paper.Shape.properties.rectOptions.strokeOpacity                = 0.5; 
  win.Paper.Shape.properties.rectOptions.fillOpacity                  = 0.2; 
  win.Paper.Shape.properties.circleOptions.radius                     = 10; 
  win.Paper.Shape.properties.circleOptions.strokeWidth                = 0; 
  win.Paper.Shape.properties.circleOptions.fillOpacity                = 0.7; 
   
  // rotate options 
  freeTransformOptions.distance = 1.3; 
  freeTransformOptions.size     = 5; 
  freeTransformOptions.rotate   = ['axisX', 'axisY']; 
 
  win.Paper.Init({ 
    id     : 'paper', 
    width  : 1024, 
    height : 1024 
  }); 
   
  pathData = [['M', 105, 105], ['L', 250, 105], ['L', 250, 150], ['L', 105, 150], ['Z']]; 
  rectangle = win.Paper.get().path(pathData).attr({ 
    'fill'             : win.Paper.Shape.properties.rectOptions.fill, 
    'fill-opacity'     : win.Paper.Shape.properties.rectOptions.fillOpacity, 
    'stroke'           : win.Paper.Shape.properties.rectOptions.stroke, 
    'stroke-opacity'   : win.Paper.Shape.properties.rectOptions.strokeOpacity, 
    'stroke-width'     : win.Paper.Shape.properties.rectOptions.strokeWidth, 
    'stroke-linecap'   : win.Paper.Shape.properties.rectOptions.strokeLinecap, 
    'stroke-linejoin'  : win.Paper.Shape.properties.rectOptions.strokeLinejoin, 
    'stroke-dasharray' : win.Paper.Shape.properties.rectOptions.strokeDasharray 
	}); 
  point1 = win.Paper.Shape.addPoint(pathData[0][1], pathData[0][2], radius).element; 
  point2 = win.Paper.Shape.addPoint(pathData[1][1], pathData[1][2], radius).element; 
  point3 = win.Paper.Shape.addPoint(pathData[2][1], pathData[2][2], radius).element; 
  point4 = win.Paper.Shape.addPoint(pathData[3][1], pathData[3][2], radius).element; 
   
  groupElements = win.Paper.get().set(); 
  groupElements.push(rectangle); 
  groupElements.push(point1); 
  groupElements.push(point2); 
  groupElements.push(point3); 
  groupElements.push(point4); 
   
  for(var key in groupElements) { 
    if(groupElements.hasOwnProperty(key)) { 
      if(groupElements[key].type === 'path') { 
        path['element'] = groupElements[key]; 
 
        _objects.paths[groupElements[key].id] = { 
          path          : path, 
          pointElements : pointElements 
        }; 
      } 
      else { 
        if(groupElements[key].type === 'circle') { 
          pointElements[groupElements[key].id] = { 
            path         : path, 
            pointElement : groupElements[key], 
            index        : key - 1 
          }; 
 
          _objects.points[groupElements[key].id] = pointElements[groupElements[key].id]; 
        } 
      } 
    } 
  } 
   
  freeTransformObj =  win.Paper.get().freeTransform(groupElements, freeTransformOptions); 
  resizeModule = new resize(); 
   
  groupElements.drag(function onmove(dx, dy, x, y, event) { 
    resizeModule.move(dx, dy); 
  }, function onstart(x, y, event) { 
    resizeModule.start(x, y, event); 
  }, function onend(event) { 
    resizeModule.end(event); 
  }); 
   
  function resize() { 
  	var pathElement  = {}; 
    var currentElement; 
    var path         = []; 
    var index        = 0; 
    var pointElement = {}; 
    var posX         = 0; 
    var posY         = 0; 
 
    this.start = function(x, y, event) { 
      if(currentElement = event.target || event.srcElement) { 
        if(_objects.points[currentElement.id]) { 
          pointElement = _objects.points[currentElement.id].pointElement; 
          pathElement  = _objects.points[pointElement.id].path.element; 
          index        = _objects.points[pointElement.id].index; 
 
          posX = pointElement.attrs.cx; 
          posY = pointElement.attrs.cy; 
 
          path   = pathElement.getPath(); 
        } 
      } 
    }; 
 
    this.move = function(dx, dy) { 
      var x           = 0; 
      var y           = 0; 
      var pointElements; 
      var bbox; 
      var middlePoint = {}; 
      var point; 
 
      if(currentElement.nodeName === 'circle' && path.length) { 
        let angle = groupElements.freeTransform.attrs.rotate/180*Math.PI; 
     
        y = posY + dy*Math.cos(angle) - dx*Math.sin(angle); 
        x = posX + dy*Math.sin(angle) + dx*Math.cos(angle); 
         
        pointElement.attr({cx : x}); 
        path[index][1] = x; 
 
        pointElement.attr({cy : y}); 
        path[index][2] = y; 
 
        pathElement.attr({ 
          path : path 
        }); 
      } 
    }; 
 
    this.end = function(event) {}; 
  } 
})(typeof window !== 'undefined' ? window : this);
#paper { 
  min-height : 768px; 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.2.8/raphael.js"></script> 
<script src="https://alias.io/raphael/free_transform/raphael.free_transform/raphael.free_transform.js"></script> 
 
<div id="paper"></div>

READ ALSO
Большая ложь о приоритетах операторов в Javascript. Или и так сойдет?

Большая ложь о приоритетах операторов в Javascript. Или и так сойдет?

Во всех учебниках и уроках что я читал, всегда пишут что первым выполнится тот оператор в выражении, у которого больше приоритетИ вроде бы это...

116
Копирование строк в JS

Копирование строк в JS

Столкнулся со странной ситуацией, есть функция которая должна просто удалить все пробелы в строке:

87
Как оптимизировать код JavaScript?

Как оптимизировать код JavaScript?

Недавно начал изучать JS, для практики решил написать небольшой калькуляторВсе в нем более-менее работает, но интересует вопрос, как оптимизировать...

104