2016-02-12 91 views
1

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

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

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

回答

3

SVG命令getCTM和/或getScreenCTM可能是您要查找的内容。 (我相信CTM代表'Current Transform Matrix',我相信)。例如,您可以使用它们,方法是使用jQuery检索元素,去除封闭的jQuery对象并调用该命令。 $("#mySvgCircleId")[0].getScreenCTM()。它们都返回SVG矩阵对象。两者都会为您提供矩阵信息,这些信息会考虑到它们自己的直接转换以及已应用于嵌套形状的任何父级svg元素的任何转换。这听起来像你在找什么。

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

  • 两者getCTMgetScreenCTM考虑到任何封闭祖先元素的帐户变换,例如任何g组元素,直到包含svg元素。例如两个命令的“untransformedRect1”和“transformedRect1”返回的矩阵是不同的。
  • getCTM结果不受其父代元素中封闭的svg元素的位置影响,而getScreenCTM结果为。例如当使用getCTM时,'untransformedRect1'和'untransformedRect2'的结果相同,但在使用getScreenCTM时不同。

可能会有进一步的并发症,如果,例如,你在处理内部框架,其他SVG元素等等,这些我在这里不是解决之内嵌套的SVG元素。

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 />"); 
 
}
<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 />

UPDATE事实上,在调查这我挖得更深一些,到SVG规范,发现了一个非常酷的其他功能,可能是如虎添翼你想要的东西:getTransformToElement。基本上,您可以通过一个命令(target.getTransformToElement(enclosing))从任何元素(我将其称为target)中检索累积变换矩阵至其任何封闭元素(我将称其为enclosing)。

我已经在下面提供了另一个代码片段,它演示了它的行为,用id命名,希望能够清楚地说明它如何与您的情况相关。片段显示target.getCTM()实质上提供了与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; 
 
}
<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>

+3

不幸的是,浏览器不支持此功能凉了:https://www.chromestatus.com/feature/5736166087196672 – Passiday

+0

这是严重的太糟糕了,因为我是要找到很兴奋这个。这将使操作在一个严重分组的svg图像更容易。好吧。非常感谢这些信息。 –

+2

您可以对不支持该功能的浏览器使用polyfill(stupid chrome ...)SVGElement.prototype.getTransformToElement = SVGElement.prototype.getTransformToElement ||函数(toElement){0}返回到Element.getScreenCTM()。inverse()。multiply(this.getScreenCTM()); }; –