使用矩阵将SVG路径绕中心旋转

7

我有许多不同大小的SVG形状,我需要以不同的角度动态旋转它们。我想使用矩阵将它们居中旋转,但我在计算矩阵中的e和f时遇到了问题。如何计算矩阵(a、b、c、d、e、f)中的e和f?我有一个角度,但不知道如何计算新位置,所以它看起来像是居中旋转了。这是一个我已经旋转了45度的示例形状: enter image description here

     <rect x="0" y="0" width="50" height="50" style="stroke: #3333cc;fill:none;" transform="matrix(1,0,0,1,100,100)"></rect>

   <rect x="0" y="0" width="50" height="50" style="fill: #3333cc" transform="matrix(0.707,-0.707,0.707,0.707,100,100)"></rect>

你使用 matrix() 而不是 rotate(angle, cx,cy) 有什么原因吗? - Paul LeBeau
иҜҖзӘҚжҳҜз»ҳеҲ¶д»ҘеҺҹзӮ№(0,0)дёәдёӯеҝғзҡ„<rect>пјҢ然еҗҺдҪҝз”Ёrotate(degree)е’Ңtranslate(x,y)гҖӮиҝҷз§Қж–№жі•е§Ӣз»Ҳжңүж•ҲпјҢиҖҢдё”жҜ”matrix(....)жӣҙжҳ“дәҺйҳ…иҜ»гҖӮ - Alvin K.
1
@PaulLeBeau 我想学习矩阵旋转的工作机制。 - Atta Ch
@AlvinK。我仍然需要使用矩阵来解决它。 - Atta Ch
rotate(degree offset-x offset-y) 其中 offset-x = 宽度/2 & offset-y = 高度/2。请查看文档matrix(...) 相当于 rotate()translate() 的组合。换句话说,无论您使用哪种方法,都会得到相同的旋转效果。 - Alvin K.
2个回答

10
一个关于变换和矩阵乘法的介绍超出了Stack Overflow答案的范围。有许多在线资源可供参考。
您可以在SVG规范中阅读有关SVG变换的内容:

http://www.w3.org/TR/SVG/coords.html

基本上,要围绕特定点(cx,cy)旋转角度(a),您需要通过矩阵乘法组合以下三个操作:
  1. 将中心点平移回原点(-cx,-cy)
  2. 执行旋转(a)
  3. 平移回原始位置
[1 0 cx] [cos(a) -sin(a) 0] [1 0 -cx]
[0 1 cy] [sin(a)  cos(a) 0] [0 1 -cy]
[0 0 1 ] [  0       0    1] [0 0  1 ]

当你将这三个矩阵相乘时,你会得到一个形如下面的矩阵:

[cos(a) -sin(a) (-cos(a) * x + sin(a) * y + x)]
[sin(a)  cos(a) (-sin(a) * x - cos(a) * y + y)]
[0         0               1          ]

或者在SVG格式中:

matrix( ca, sa, -sa, ca, (-ca * x + sa * y + x), (-sa * x - ca * y + y) )

在哪里:

ca = cos(angle)
sa = sin(angle)

这是一个演示。绿色矩形使用transform="rotate(a,x,y)",红色矩形使用我们计算出的等效方法。

var matrix = getMatrixForRotation(45, 250, 250);

document.getElementById("myrect").setAttribute("transform", matrix);



function getMatrixForRotation(a, cx, cy)
{
  var ca = Math.cos(a * Math.PI / 180);
  var sa = Math.sin(a * Math.PI / 180);

  var a = ca.toFixed(4);
  var b = sa.toFixed(4);
  var c = (-sa).toFixed(4);
  var d = ca.toFixed(4);
  var e = (-ca * cx + sa * cy + cx).toFixed(4);
  var f = (-sa * cx - ca * cy + cy).toFixed(4);

  return "matrix(" + [a,b,c,d,e,f].join(' ') + ")";
}
<svg width="500" height="500">
  
  <rect x="100" y="150" width="300" height="200" fill="#eee"/>
  
  <rect x="100" y="150" width="300" height="200" fill="none"
        stroke="green" stroke-width="10"
        transform="rotate(45 250 250)"/>
  
  <rect x="100" y="150" width="300" height="200" fill="none"
        stroke="red" stroke-width="4"
        id="myrect"/>
  
</svg>


为什么你要使用.toFixed?这样做会不必要地丢失精度! - wizzwizz4
两个原因:(1)JS数字是双精度,而SVG数字是单精度。因此我们不能直接将JS值输出为字符串。(2) 更整洁的输出。大多数情况下,四位小数就足够了。 - Paul LeBeau
两个反驳观点:(1)是的,你可以:看看这个jsfiddle。(2)大多数时候不等于全部时间;为什么要提供会失去准确性的代码,而不是提供不会失去准确性的代码呢? - wizzwizz4

0
基本上你需要做的是将你的X位置加上并将你的Y位置取反。例如,如果你有这段代码。

    <rect x="0" y="0" width="50" height="50" style="stroke: #3333cc;          fill:none;" transform="matrix(1,0,0,1,100,100)"></rect>

    <rect x="25" y="-25" width="50" height="50" style="fill: #3333cc" transform="matrix(0.5,0.5,-0.5,0.5,100,100)"></rect>

</svg>

第一个矩形是普通的直角矩形,没有旋转。要在第一个矩形的中心旋转它,你需要先

Divide the width by your x transform (0.5) : 50 / 0.5 = 25
Divide the height by your y transform (0.5) : 50 / 0.5 = 25

然后在您的第二个矩形中使用这些值,新的X位置将为25,新的Y位置将为-25。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接