获取svg元素的全局变换矩阵

时间:2016-02-12 23:20:29

标签: javascript svg

我想使用浏览器svg + JavaScript从用户准备的svg模板生成动态图像(例如,在Inkscape中创建),其中有矩形占位符,用于标记应放置动态图形的位置。

理想情况下,用户 - 模板创建者 - 应该被允许以任何他们想要的方式移动,缩放,旋转,倾斜矩形(倾斜后不是那么多矩形)。他们唯一必须做的就是为那些占位符矩形设置正确的id值。

我正在寻找一种方法来获取/计算那些占位符的变换矩阵。由于它们可以嵌套在组中,因此仅仅读取元素的属性是不够的。

1 个答案:

答案 0 :(得分:3)

SVG命令getCTM和/或getScreenCTM可能是您正在寻找的内容。 (CTM代表'当前转换矩阵',我相信。)您可以使用它们,例如,通过使用jQuery检索元素,剥离封闭的jQuery对象并调用命令,例如: $("#mySvgCircleId")[0].getScreenCTM()。它们都返回SVG矩阵对象。两者都将为您提供矩阵信息,该信息考虑了他们自己的直接变换以及已应用于形状嵌套在其中的任何父级svg元素的任何变换。这听起来像你在寻找。

但请注意,这两个命令之间存在重要差异。我在下面的代码片段中演示了一些差异。在那里,我展示了两个svg元素,一个有两个红色矩形,一个有两个蓝色矩形。所有矩形具有相同的宽度和高度,但每种颜色中的一种未转化,而另一种颜色嵌套在三个不同转换的组中。输出显示每个矩形使用getCTMgetScreenCTM的矩阵结果。请注意以下事项:

  • getCTMgetScreenCTM都会考虑任何封闭的祖先元素的变换,例如任何g组元素,直至封闭的svg元素。例如返回的矩阵对于未转换的Rect1'而言是不同的。和' transformedRect1'对于这两个命令。
  • getCTM结果不受其父级元素中封闭式svg元素位置的影响,getScreenCTM结果为getCTM。例如未转化的结果&#39;和&#39; untransformedRect2&#39;使用getScreenCTM时相同但使用var infoType = "CTM"; show("Matrix Results from getCTM()"); show(getInfo(infoType, "untransformedRect1")); show(getInfo(infoType, "transformedRect1")); show(getInfo(infoType, "untransformedRect2")); show(getInfo(infoType, "transformedRect2")); show("<br />"); var infoType = "ScreenCTM"; show("Matrix Results from getScreenCTM()"); show(getInfo(infoType, "untransformedRect1")); show(getInfo(infoType, "transformedRect1")); show(getInfo(infoType, "untransformedRect2")); show(getInfo(infoType, "transformedRect2")); function getInfo(mtrx, id) { var mtrx; if (infoType === "CTM") { var mtrx = $("#" + id)[0].getCTM(); } else if (infoType === "ScreenCTM") { var mtrx = $("#" + id)[0].getScreenCTM(); } var str = r(mtrx.a) + ", " + r(mtrx.b) + ", " + r(mtrx.c) + ", " + r(mtrx.d) + ", " + r(mtrx.e) + ", " + r(mtrx.f); return id + ": matrix: " + str; } function r(num) { return Math.round(num * 1000) / 1000; } function show(msg) { document.write(msg + "<br />"); }时不同。

例如,如果您处理iframe,将svg元素嵌套在其他svg元素中等等,可能还有其他复杂问题,我在这里没有提及。

&#13;
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Depending on how you are viewing this, you may need to scroll down to see the matrix values.</p>
<div id="containerForSvgs">
  <svg id="svg1" width="150" height="100">
    <rect id="background1" width="300" height="200" fill="#eee" transform="translate(0,0)"></rect>
    <rect id="untransformedRect1" width="20" height="10" fill="red"></rect>
    <g id="group1A" transform="scale(2)">
      <g id="group1B" transform="translate(40,20)">
        <g id="group1C" transform="rotate(-15)">
          <rect id="transformedRect1" width="20" height="10" fill="red"></rect>
        </g>
      </g>
    </g>
  </svg>
  <br />
  shift ===>
  <svg id="svg2" width="150" height="100">
    <rect id="background2" width="300" height="200" fill="#eee" transform="translate(0,0)"></rect>
    <rect id="untransformedRect2" width="20" height="10" fill="blue"></rect>
    <g id="group2A" transform="scale(2)">
      <g id="group2B" transform="translate(40,20)">
        <g id="group2C" transform="rotate(-15)">
          <rect id="transformedRect2" width="20" height="10" fill="blue"></rect>
        </g>
      </g>
    </g>
  </svg>
</div>
<br />
&#13;
getTransformToElement
&#13;
&#13;
&#13;

更新实际上,在调查这个问题时,我更深入地研究了SVG规范,发现了一个非常酷的其他功能,它可能对你想要的功能更加强大:target。基本上,您可以在一个命令中从任何元素(我将调用enclosing)到其任何封闭元素(我称之为target.getTransformToElement(enclosing))中检索累积变换矩阵: target.getCTM()

我在下面提供了另一个代码片段,用于演示其行为,其中id已被命名,希望能够清楚地表明它与您的情况有何关联。该代码段显示target.getTransformToElement(enclosingSvgElement)基本上提供与target.getTransformToElement(enclosing)相同的输出。然而,此外,它还表明,通过能够显示从子子嵌套元素到其任何祖先封闭元素的任何任意距离的变换,它更灵活。此外,你可以向任何一个方向看,例如, enclosing.getTransformToElement(target)var svg = $("svg" )[0]; var grp1 = $("#grp1_formatting_of_entire_app" )[0]; var grp2 = $("#grp2_menus_and_buttons_and_stuff" )[0]; var grp3 = $("#grp3_main_drawing_canvas" )[0]; var grp4 = $("#grp4_some_intervening_group" )[0]; var shp1 = $("#shp1_the_shape_I_currently_care_about" )[0]; var grp5 = $("#grp5_a_lower_group_I_dont_currently_care_about")[0]; var shp1_CTM = shp1.getCTM(); var shp1_to_svg = shp1.getTransformToElement(svg); var shp1_to_grp3 = shp1.getTransformToElement(grp3); var grp3_to_shp1 = grp3.getTransformToElement(shp1); document.write("<table>"); show("getCTM for shp1" , shp1_CTM ); show("getTransformToElement from shp1 to enclosing svg", shp1_to_svg ); show("getTransformToElement from shp1 to grp3" , shp1_to_grp3); show("getTransformToElement from grp3 to shp1" , grp3_to_shp1); document.write("</table>"); function show(msg, mtrx) { document.write("<tr><td>" + msg + "</td><td>" + mtrxStr(mtrx) + "</td></tr>"); } function mtrxStr(mtrx) { return "( " + rnd(mtrx.a) + ", " + rnd(mtrx.b) + ", " + rnd(mtrx.c) + ", " + rnd(mtrx.d) + ", " + rnd(mtrx.e) + ", " + rnd(mtrx.f) + " )"; } function rnd(n) { return Math.round(n*10)/10; },其中一个是另一个的矩阵倒数(如果我在这里得到我的数学术语正确的话)。

&#13;
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<svg width="200" height="60">
  <g            id="grp1_formatting_of_entire_app"                  transform="translate(30.0, 0)">
    <g          id="grp2_menus_and_buttons_and_stuff"               transform="translate(10.0, 0)">
      <g        id="grp3_main_drawing_canvas"                       transform="translate( 3.0, 0)">
        <g      id="grp4_some_intervening_group"                    transform="translate( 1.0, 0)">
          <rect id="shp1_the_shape_I_currently_care_about"          transform="translate( 0.3, 0)"
                 x="0" y="0" width="100" height="40" fill="red"></rect>
          <g    id="grp5_a_lower_group_I_dont_currently_care_about" transform="translate( 0.1, 0)">
          </g>
        </g>
      </g>
    </g>
  </g>
</svg>
<p>Results</p>
&#13;
{{1}}
&#13;
&#13;
&#13;