SVG旋转和缩放变换问题

时间:2014-03-24 09:03:32

标签: javascript html5 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);  

有什么想法可以解决这个问题吗?

1 个答案:

答案 0 :(得分:1)

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

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

<!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>