缩放旋转的SVG元素

问题描述 投票:0回答:1

问题

我尝试创建一个小插件,通过拖动每个现有

rect
rect
右上角的一点
circle
来缩放 SVG 元素。虽然这对于红色元素效果很好,但对于蓝色元素却无法正确执行。

所需的行为是使用左下点作为原点缩放元素,同时保持其比例。然而在蓝色元素上它也会旋转它。

蓝色元素已经旋转:

matrix(1 -0.5 0.5 1 0 0)

虽然红色元素不是:

matrix(1 0 0 1 0 0)

我的公式:

new m(a) = prev m(a) * largest movement of dx or dy as factor
new m(d) = new m(a) //to keep proportions
new m(e) = (prev m(a) - new m(a)) * prev(x) + prev m(e) //vertical position
new m(f) = (prev m(d) - new m(d)) * (prev(y) + prev(height)) + prev m(f) //horizontal position

问题

我认为蓝色元素上已经存在的旋转导致了我的问题,但我无法理解为什么。由于我只是通过比例(a,d)的差异来调整平移(e,f),所以无论现有的旋转如何,这都不起作用吗?

例如,我可以将蓝色元素包装在另一个

g
中,并在其上添加更改,而不需要旋转:

<g transform="matrix(1.2 0 0 1.2 -115 -25)">
  <rect x="380" y="320" width="40" height="40" fill="blue" transform="matrix(1 -0.5 0.5 1 0 0)"></rect>
</g>

简化示例

const
  _Drawing = document.querySelector('svg'),
  _Parent = _Drawing.querySelector('.VWS_DragAndDrop');

let
  _currentElement = null; //current "g.Dragging"

//add mousedown-event for dragging
_Drawing.onmousedown = function(event){
  if(event.target.tagName === 'rect' && event.target.parentNode.parentNode === _Parent){
    //set "g.Dragging" as dragging element
    _currentElement = event.target.parentNode;

    const
      tBBoxE = _currentElement.getBBox(),
      tBBoxR = _currentElement.referenceElement.getBBox(),
      tMatrix = _currentElement.transform.baseVal.getItem(0).matrix;

    //values on start to calculate the adjustments on move
    _currentElement.referenceOptions = {
      startX: event.clientX,
      startY: event.clientY,
      Bounds: {height: tBBoxR.height, w: tBBoxE.width * tMatrix.a, h: tBBoxE.height * tMatrix.d, x: tBBoxR.x, y: tBBoxR.y},
      Matrix: {a: tMatrix.a, b: tMatrix.b, c: tMatrix.c, d: tMatrix.d, e: tMatrix.e, f: tMatrix.f}
    }
  }
  else{
    //reset dragging element
    _currentElement = null
  }
};

//add mousemove for dragging
_Drawing.onmousemove = function(event){
  if(_currentElement){
    const
      tOriginalBBox = _currentElement.referenceOptions.Bounds,
      tOriginalMatrix = _currentElement.referenceOptions.Matrix,
      tCurrentMatrix = _currentElement.transform.baseVal.getItem(0).matrix,
      tNewMatrix = _currentElement.ownerSVGElement.createSVGMatrix(),
      tCTM = _currentElement.ownerSVGElement.getScreenCTM()
      tC1 = {x: (_currentElement.referenceOptions.startX - tCTM.e) / tCTM.a, y: (_currentElement.referenceOptions.startY - tCTM.f) / tCTM.d},
      tC2 = {x: (event.clientX - tCTM.e) / tCTM.a, y: (event.clientY - tCTM.f) / tCTM.d},
      tDX = (tC2.x-tC1.x),
      tDY = (tC2.y-tC1.y);

    //current matrix as default
    tNewMatrix.a = tCurrentMatrix.a;
    tNewMatrix.b = tCurrentMatrix.b;
    tNewMatrix.c = tCurrentMatrix.c;
    tNewMatrix.d = tCurrentMatrix.d;
    tNewMatrix.e = tCurrentMatrix.e;
    tNewMatrix.f = tCurrentMatrix.f;

    //adjust matrix around p(bottom-left) while keeping proportions
    tNewMatrix.a = Math.max(tOriginalMatrix.a/100*((tOriginalBBox.w+tDX)*100/tOriginalBBox.w), tOriginalMatrix.d/100*((tOriginalBBox.h-tDY)*100/tOriginalBBox.h));
    tNewMatrix.d = tNewMatrix.a;
    tNewMatrix.e = (tOriginalMatrix.a - tNewMatrix.a) * tOriginalBBox.x + tOriginalMatrix.e; //vertical position
    tNewMatrix.f = (tOriginalMatrix.d - tNewMatrix.d) * (tOriginalBBox.y + tOriginalBBox.height) + tOriginalMatrix.f; //horizontal position

    //set new matrix to reference-element
    _currentElement.referenceElement.transform.baseVal.getItem(0).setMatrix(tNewMatrix)

    //set new matrix to dragging-element
    _currentElement.transform.baseVal.getItem(0).setMatrix(tNewMatrix)
  }
};

//add mouseup for dragging
_Drawing.onmouseup = _Drawing.onmouseleave = function(event){
  _currentElement = null
};

//adding dragging boxes to each "rect" and "circle"
document.querySelectorAll('rect, circle').forEach(function(element){
  const tElement = element;

  //create a "g.Dragging" inside "g.VWS_DragAndDrop"
  const tG = _Parent.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'g'));
  tG.className = 'Dragging';
  tG.setAttributeNS(null, 'transform', 'matrix(1 0 0 1 0 0)')
  tG.referenceElement = tElement;

  //apply the element's matrix to the new "g.Dragging"
  tG.transform.baseVal.getItem(0).setMatrix(tElement.transform.baseVal.getItem(0).matrix)

  //create a "rect" to scale the reference-element while dragging that "rect"
  const
    tBBox = tElement.getBBox(),
    tLength = Math.floor(Math.min(tBBox.width, tBBox.height) / 10);

  const tRect = tG.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'rect'));
  tRect.setAttributeNS(null, 'x', tBBox.x + tBBox.width - tLength);
  tRect.setAttributeNS(null, 'y', tBBox.y - tLength*3);
  tRect.setAttributeNS(null, 'width', tLength * 4);
  tRect.setAttributeNS(null, 'height', tLength * 4)
})
<svg width="800" height="800" style="background-color: aqua">
  <style>
    .VWS_DragAndDrop rect{
      cursor: move;
      fill: grey;
      stroke: black
    }
  </style>

  <circle cx="150" cy="150" r="15" fill="red" transform="matrix(1 0 0 1 0 0)" />
  <rect x="300" y="300" width="40" height="40" fill="red" transform="matrix(1 0 0 1 5 5)" />
  <rect x="380" y="320" width="40" height="40" fill="blue" transform="matrix(1 -0.5 0.5 1 0 0)" />

  <g class="VWS_DragAndDrop" />
</svg>

javascript svg
1个回答
0
投票

我不确定你尝试什么存档,但矩阵根本不旋转

如果你想旋转元素使用旋转 旋转需要 3 个参数: 度数 - 旋转 x - 位置原点 y - 位置原点

const
  _Drawing = document.querySelector('svg'),
  _Parent = _Drawing.querySelector('.VWS_DragAndDrop');

let
  _currentElement = null; //current "g.Dragging"

//add mousedown-event for dragging
_Drawing.onmousedown = function(event){
  if(event.target.tagName === 'rect' && event.target.parentNode.parentNode === _Parent){
    //set "g.Dragging" as dragging element
    _currentElement = event.target.parentNode;

    const
      tBBoxE = _currentElement.getBBox(),
      tBBoxR = _currentElement.referenceElement.getBBox(),
      tMatrix = _currentElement.transform.baseVal.getItem(0).matrix;

    //values on start to calculate the adjustments on move
    _currentElement.referenceOptions = {
      startX: event.clientX,
      startY: event.clientY,
      Bounds: {height: tBBoxR.height, w: tBBoxE.width * tMatrix.a, h: tBBoxE.height * tMatrix.d, x: tBBoxR.x, y: tBBoxR.y},
      Matrix: {a: tMatrix.a, b: tMatrix.b, c: tMatrix.c, d: tMatrix.d, e: tMatrix.e, f: tMatrix.f}
    }
  }
  else{
    //reset dragging element
    _currentElement = null
  }
};

//add mousemove for dragging
_Drawing.onmousemove = function(event){
  if(_currentElement){
    const
      tOriginalBBox = _currentElement.referenceOptions.Bounds,
      tOriginalMatrix = _currentElement.referenceOptions.Matrix,
      tCurrentMatrix = _currentElement.transform.baseVal.getItem(0).matrix,
      tNewMatrix = _currentElement.ownerSVGElement.createSVGMatrix(),
      tCTM = _currentElement.ownerSVGElement.getScreenCTM()
      tC1 = {x: (_currentElement.referenceOptions.startX - tCTM.e) / tCTM.a, y: (_currentElement.referenceOptions.startY - tCTM.f) / tCTM.d},
      tC2 = {x: (event.clientX - tCTM.e) / tCTM.a, y: (event.clientY - tCTM.f) / tCTM.d},
      tDX = (tC2.x-tC1.x),
      tDY = (tC2.y-tC1.y);

    //current matrix as default
    tNewMatrix.a = tCurrentMatrix.a;
    tNewMatrix.b = tCurrentMatrix.b;
    tNewMatrix.c = tCurrentMatrix.c;
    tNewMatrix.d = tCurrentMatrix.d;
    tNewMatrix.e = tCurrentMatrix.e;
    tNewMatrix.f = tCurrentMatrix.f;

    //adjust matrix around p(bottom-left) while keeping proportions
    tNewMatrix.a = Math.max(tOriginalMatrix.a/100*((tOriginalBBox.w+tDX)*100/tOriginalBBox.w), tOriginalMatrix.d/100*((tOriginalBBox.h-tDY)*100/tOriginalBBox.h));
    tNewMatrix.d = tNewMatrix.a;
    tNewMatrix.e = (tOriginalMatrix.a - tNewMatrix.a) * tOriginalBBox.x + tOriginalMatrix.e; //vertical position
    tNewMatrix.f = (tOriginalMatrix.d - tNewMatrix.d) * (tOriginalBBox.y + tOriginalBBox.height) + tOriginalMatrix.f; //horizontal position

    //set new matrix to reference-element
    _currentElement.referenceElement.transform.baseVal.getItem(0).setMatrix(tNewMatrix)

    //set new matrix to dragging-element
    _currentElement.transform.baseVal.getItem(0).setMatrix(tNewMatrix)
  }
};

//add mouseup for dragging
_Drawing.onmouseup = _Drawing.onmouseleave = function(event){
  _currentElement = null
};

//adding dragging boxes to each "rect" and "circle"
document.querySelectorAll('rect, circle').forEach(function(element){
  const tElement = element;

  //create a "g.Dragging" inside "g.VWS_DragAndDrop"
  const tG = _Parent.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'g'));
  tG.className = 'Dragging';
  tG.setAttributeNS(null, 'transform', 'matrix(1 0 0 1 0 0)')
  tG.referenceElement = tElement;

  //apply the element's matrix to the new "g.Dragging"
  tG.transform.baseVal.getItem(0).setMatrix(tElement.transform.baseVal.getItem(0).matrix)

  //create a "rect" to scale the reference-element while dragging that "rect"
  const
    tBBox = tElement.getBBox(),
    tLength = Math.floor(Math.min(tBBox.width, tBBox.height) / 10);

  const tRect = tG.appendChild(document.createElementNS('http://www.w3.org/2000/svg', 'rect'));
  tRect.setAttributeNS(null, 'x', tBBox.x + tBBox.width - tLength);
  tRect.setAttributeNS(null, 'y', tBBox.y - tLength*3);
  tRect.setAttributeNS(null, 'width', tLength * 4);
  tRect.setAttributeNS(null, 'height', tLength * 4)
})
<svg width="800" height="800" style="background-color: aqua">
  <style>
    .VWS_DragAndDrop rect{
      cursor: move;
      fill: grey;
      stroke: black
    }
  </style>

  <circle cx="150" cy="150" r="15" fill="red" transform="matrix(1 0 0 1 0 0)" />
  <rect x="300" y="300" width="40" height="40" fill="red" transform="matrix(1 0 0 1 5 5)" />
<rect x="380" y="320" width="40" height="40" fill="blue" transform="matrix(1 0 0 1 0 0) rotate(45 400 340)" />

  <g class="VWS_DragAndDrop" />
</svg>

© www.soinside.com 2019 - 2024. All rights reserved.