如何从一个SVG元素中的点绘制到另一个SVG元素?

时间:2017-03-13 03:59:20

标签: javascript css svg

我的情况是HTML文档中有多个SVG元素。元素是使用HTML规则,盒子模型,flexbox布局的,现在我告诉网格将很快开始使用。这个jsfiddle是一个例子,显示了一对填充的抛物线,使用二次路径操作绘制了公共端点和稍微不同的控制点。其他情况可能是各种厚度的简单水平或对角线。

在小提琴的第一个SVG中,两个端点在同一个SVG块中,并且尺寸(以SVG为单位)是众所周知的,并且绘制抛物线很简单。 (控制点也在这里显示)

然而,接下来的两个SVG证明了这个问题。端点位于单独的SVG中,由未知数量的“东西”分隔,此处由一些文本表示。很明显,javascript将需要重写端点和控制点的坐标,以便抛物线(或其他)可以连接两者。

如何获得第二个SVG中端点相对于第三个SVG坐标系的SVG单位坐标,以便将抛物线连接到其左端点?

可以做出一个可能简化的假设:SVG单位到像素的坐标比率对于任何绘图都是一致的,尽管为了增加复杂性,比率可能会不时变化(触发需要重新计算和重新绘制交叉 - SVG项目。)

一个可能复杂的问题是大多数端点将嵌套在两层嵌套的SVG元素中:外部SVG的SVG单元与CSS像素的大小相同,但内部SVG将具有这个不同的单元大小可能会不时变化。

可以根据需要向解决方案添加ID或CLASS。

/**CSS*/

svg { overflow: visible; }
<!--HTML -->

<svg width="12cm" height="6cm" viewBox="0 0 1200 600"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>Example quad01 - quadratic Bézier commands in path data</title>
  <desc>Picture showing a "Q" a "T" command,
        along with annotations showing the control points
        and end points</desc>
  <rect x="1" y="1" width="1198" height="598"
        fill="none" stroke="blue" stroke-width="1" />

  <path d="M500,300 Q300,50 100,300 Q300,75 500,300"
        fill="green" stroke="green" stroke-width="1" />
  <!-- End points -->
  <g fill="black" >
    <circle cx="100" cy="300" r="3"/>
    <circle cx="500" cy="300" r="3"/>
  </g>
  <!-- Control points and lines from end points to control points -->
  <g fill="#888888" >
    <circle cx="300" cy="50" r="3"/>
    <circle cx="300" cy="75" r="3"/>
  </g>
  <path d="M100,300 L300,50 L500,300 
           L300,75 L100,300"
        fill="none" stroke="#888888" stroke-width="1" />
</svg>
<br>

<svg width="2cm" height="6cm" viewBox="0 0 200 600"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>Example quad01 - quadratic Bézier commands in path data</title>
  <desc>SVG left endpoint</desc>
  <rect x="1" y="1" width="198" height="598"
        fill="none" stroke="blue" stroke-width="1" />

  <!-- End points -->
  <g fill="black" >
    <circle cx="100" cy="300" r="3"/>
  </g>
</svg>
some stuff here
<svg width="2cm" height="6cm" viewBox="0 0 200 600"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>Example quad01 - quadratic Bézier commands in path data</title>
  <desc>SVG right endpoint</desc>
  <rect x="1" y="1" width="198" height="598"
        fill="none" stroke="blue" stroke-width="1" />

  <path d="M100,300 Q-100,50 -300,300 Q-100,75 100,300"
        fill="green" stroke="green" stroke-width="1" />
  <!-- End points -->
  <g fill="black" >
    <circle cx="100" cy="300" r="3"/>
  </g>
</svg>

(一般来说,坐标不会有相同的Y位置,这个例子很简单:但我只需要一种确定端点在适当坐标系中的位置的方法。)

1 个答案:

答案 0 :(得分:0)

这对我来说很难弄明白,但是一位同事帮助了我们,我们终于对此感到困惑。解决方案结果相当简单。

我们困惑的方式,似乎有一个坐标转换矩阵,可用于从SVG用户坐标转换为浏览器像素......并通过其逆转回SVG用户坐标。这是通过以下方式为每个起始和结束元素获得的:

start = document.getElementById('startSVGElement');
spt = start.createSVGPoint();
sCTM = start.getScreenCTM();

end = document.getElementById('endSVGElement');
ept = end.createSVGPoint();
eCTM = end.getScreenCTM();

然后,要从起始元素中的点(100,400)绘制到结束元素中的点(50,60),请填写以下点:

spt.x = 100;
spt.y = 400;
ept.x = 50;
ept.y = 60;

现在转换魔术:根据CTM将结束点从SVG空间转换为浏览器像素空间,然后使用起始元素CTM逆转换回SVG空间:

ept_in_start = ept.matrixTransform( eCTM ).matrixTransform( sCTM.inverse())

现在spt和ept_in_start具有相对于同一元素(起始元素)和相同坐标系(起始元素的SVG空间)的坐标,您可以使用这些坐标绘制任何您想要的东西,基于坐标。

当然,如果页面重排,则必须重新计算并重绘。并且您必须确保为每个端点的(x,y)坐标获得正确的CTM ...如果您有嵌套的SVG和转换,则有许多重叠的坐标系。