javascript - SVG 旋转和缩放转换问题

标签 javascript html matrix svg

我正在尝试使用变换来调整、旋转和拖动 SVG 元素。如果我旋转一个对象,然后重新调整大小并再次旋转,在最后一次旋转时元素会移位。

用于调整大小的代码

var matrix = SVG.createSVGMatrix()
                .translate(x,y)
                .scaleNonUniform(sx,sy)
                .translate(-x,-y);
var newMatrix =  SVG.createSVGTransformFromMatrix(ctm.multiply(matrix)); 
element.transform.baseVal.initialize(newMatrix);  

用于旋转的代码

var matrix = SVG.createSVGMatrix()
                .translate(cx,cy)
                .rotate(angle)
                .translate(-cx,-cy);
var newMatrix = SVG.createSVGTransformFromMatrix(ctm.multiply(matrix));
element.transform.baseVal.initialize(newMatrix);  

有解决这个问题的办法吗?

最佳答案

无论应用哪种变换,我都对元素使用 getBBox 的组合来识别其中心。此外,为了跟踪元素的转换中心,我将它封装在一个 svg 包装器中,并获取它的边界框。我在每个变换上使用矩阵变换和 consolidate()。

以下是如何使用它来将元素移动到所需点的示例:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>Native Center Transforms with Wrapper</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body style='padding:10px;font-family:arial'>
<center>
<h4>Native Center Transforms with Wrapper</h4>
<div style='width:90%;background-color:gainsboro;text-align:justify;padding:10px;border-radius:6px;'>
The <b>Native Center</b> is the center of an element's bounding box. This remains constant no matter which transforms has been applied to an element.
Therefore this center point there can be used as the <b>reference point</b> for stable rotation, scale, and skew transforms. By use of an svg <b>wrapper</b>, the element can be translated so its
center point moves to a target point.
</div>
<table><tr>
<td>
<div style="padding:10px;width:400px;text-align:justify">
<b>Scenerio:</b><br />
The rect element has been translated from its original location (dashed rect).
Further scale, rotate, and skew transforms can use the rect's
original bounding box center (black circle): <b>NativeCenterX, NativeCenterY</b>.<br /><br />
Also, folowing scale. rotate, skew..<br />
The center of the element can be <b>moved to</b> a specific target point (maroon circle) by using the center point of it's svg <b>wrapper</b>
<br /><br />
<i>Click the buttons one or more times to transform the rect.</i>
</div>
</td>
<td>
<div id="svgDiv" style='background-color:lightgreen;width:400px;height:400px;'>
<svg id="mySVG" width="400" height="400">
<rect id="myRect" fill=red stroke="none" x="10" y="60" width=150 height=80 transform="translate(110,50)" />
<rect id="bbRect" fill=none stroke="black" stroke-width="1" stroke-dasharray="10 10"  />
<circle id=nativeCenter r=5 fill="black" stroke="none" />
<circle id=moveToTarget r=5 fill="maroon" cx=240 cy=330 stroke="none" />
</svg>
</div>
<center>
<button onClick=rotateRect()>rotate</button>
<button onClick=scaleRect()>scale</button>
<button onClick=skewXRect()>skewX</button>
<button onClick=skewYRect()>skewY</button>
then...<button onClick=moveToRect()>Move To</button>
</center>
</td>
</tr></table>
  <br />SVG Source:<br />
<textarea id=svgSourceValue style='font-size:110%;font-family:lucida console;width:90%;height:200px'></textarea>
  <br />Javascript:<br />
<textarea id=jsValue style='border-radius:26px;font-size:110%;font-weight:bold;color:midnightblue;padding:16px;background-color:beige;border-width:0px;font-size:100%;font-family:lucida console;width:90%;height:400px'></textarea>
</center>
<div id='browserDiv' style='padding:5px;position:absolute;top:5px;left:5px;background-color:gainsboro;'>OK in:IE11/CH32/FF23<br /></div>
<script id=myScript>
var NativeCenterX
var NativeCenterY
var TransformRequestObj
var TransformList
//---translate target---
var TargetX
var TargetY
//---onload---
function initBBox()
{
    var bb=myRect.getBBox()
    var bbx=bb.x
    var bby=bb.y
    var bbw=bb.width
    var bbh=bb.height
    NativeCenterX=bbx+.5*bbw
    NativeCenterY=bby+.5*bbh
    bbRect.x.baseVal.value=bbx
    bbRect.y.baseVal.value=bby
    bbRect.width.baseVal.value=bbw
    bbRect.height.baseVal.value=bbh
    nativeCenter.cx.baseVal.value=NativeCenterX
    nativeCenter.cy.baseVal.value=NativeCenterY

    TargetX=moveToTarget.cx.baseVal.value
    TargetY=moveToTarget.cy.baseVal.value

    //--- transform myRect Objs---
    TransformRequestObj=mySVG.createSVGTransform()
    var animTransformList=myRect.transform
    TransformList=animTransformList.baseVal
}
//---button---
function rotateRect() //---15 degrees
{
    TransformRequestObj.setRotate(15,NativeCenterX,NativeCenterY)
    TransformList.appendItem(TransformRequestObj)
    TransformList.consolidate()
    svgSourceValue.value=svgDiv.innerHTML
}
function scaleRect() //---.8---
{
    TransformRequestObj.setTranslate(NativeCenterX,NativeCenterY  )
    TransformList.appendItem(TransformRequestObj)
    TransformList.consolidate()

    TransformRequestObj.setScale(.8,.8)
    TransformList.appendItem(TransformRequestObj)
    TransformList.consolidate()
    TransformRequestObj.setTranslate(-NativeCenterX,-NativeCenterY  )
    TransformList.appendItem(TransformRequestObj)
    TransformList.consolidate()

    svgSourceValue.value=svgDiv.innerHTML
}
function skewXRect()
{
    TransformRequestObj.setTranslate(NativeCenterX,NativeCenterY  )
    TransformList.appendItem(TransformRequestObj)
    TransformList.consolidate()

    TransformRequestObj.setSkewX(10)
    TransformList.appendItem(TransformRequestObj)
    TransformList.consolidate()

    TransformRequestObj.setTranslate(-NativeCenterX,-NativeCenterY  )
    TransformList.appendItem(TransformRequestObj)
    TransformList.consolidate()

    svgSourceValue.value=svgDiv.innerHTML
}
function skewYRect()
{
    TransformRequestObj.setTranslate(NativeCenterX,NativeCenterY  )
    TransformList.appendItem(TransformRequestObj)
    TransformList.consolidate()

    TransformRequestObj.setSkewY(10)
    TransformList.appendItem(TransformRequestObj)
    TransformList.consolidate()

    TransformRequestObj.setTranslate(-NativeCenterX,-NativeCenterY  )
    TransformList.appendItem(TransformRequestObj)
    TransformList.consolidate()

    svgSourceValue.value=svgDiv.innerHTML
}
var NS="http://www.w3.org/2000/svg"
//---move transformed rect to a specific point(TargetX,TargetY)--
function moveToRect()
{
    //---build temp wrapper---
    var wrapper=document.createElementNS(NS,"svg")
    mySVG.appendChild(wrapper)
    //---temp place rect in wrapper---
    wrapper.appendChild(myRect)
    var bb=wrapper.getBBox()
    //---return myRect to previous location---
    mySVG.insertBefore(myRect,bbRect)
    //---remove temp wrapper---
    mySVG.removeChild(wrapper)

    var bbx=bb.x
    var bby=bb.y
    var bbw=bb.width
    var bbh=bb.height
    //---center of svg srapper---
    var Wcx=bbx+.5*bbw
    var Wcy=bby+.5*bbh

    //---bind target point to current matrix---
    var pnt = myRect.nearestViewportElement.createSVGPoint();
    pnt.x = TargetX;
    pnt.y = TargetY;
    var sCTM = myRect.getCTM();
    PNT1 = pnt.matrixTransform(sCTM.inverse());
    //---bind wrapper center to current matrix--
    var pnt = myRect.nearestViewportElement.createSVGPoint();
    pnt.x = Wcx;
    pnt.y = Wcy ;
    var sCTM = myRect.getCTM();
    PNT2 = pnt.matrixTransform(sCTM.inverse());
    //---translate rect's center to target---
    var transX=PNT1.x-PNT2.x
    var transY=PNT1.y-PNT2.y

    TransformRequestObj.setTranslate(transX,transY)
    TransformList.appendItem(TransformRequestObj)
    TransformList.consolidate()

    svgSourceValue.value=svgDiv.innerHTML
}

</script>
<script>
document.addEventListener("onload",init(),false)
function init()
{
    initBBox()
    svgSourceValue.value=svgDiv.innerHTML
    jsValue.value=myScript.text
}
</script>

</body>

</html>

关于javascript - SVG 旋转和缩放转换问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22604912/

相关文章:

javascript - 使 div 上的颜色根据您单击的位置而变化

algorithm - 在允许交换列的情况下找到最大的 1 矩形

python - 在 Numpy 中生成对称矩阵

R中更快的矩阵分配的原因

javascript - 如何在 javascript 中正确循环表单元素

javascript - 如何让 fireEvent 方法在 IE8 中工作?

javascript - 将类添加到包含 <strong> 标记的 <li> 元素

javascript - 为什么操作地址加2?

html - 图标未显示

javascript - 如何从数据库中的图像创建 jQuery slider ?