有没有一种用JavaScript合并两个路径元素(SVG)的方法?

17

我使用SVG绘制了两条路径线,并将这些元素保存到了我的javascript代码中的两个变量'Line1'和'Line2'中,我需要将这两条线合并成一个单独的路径元素。是否有一种方法可以实现这一点?


2
程序相关内容翻译:编程:如何通过程序合并两个SVG路径元素? - jantimon
2个回答

17

你定义的路径是相对路径(小写字母)还是绝对路径(大写字母)?如果是绝对路径,连接两个路径很简单,只需附加d属性的值。如果你有这样的两条路径:

<path id="Line1" d="M50,50
         A30,30 0 0,1 35,20
         L100,100"
      style="stroke:#660000; fill:none;"/>
<path id="Line2" d="M110,110
         L100,0"
      style="stroke:#660000; fill:none;"/>

然后这段JavaScript代码:

var Line1 = document.getElementById("Line1");
var Line2 = document.getElementById("Line2");
//Add paths together
Line1.setAttribute('d', Line1.getAttribute('d') + ' ' + Line2.getAttribute('d'));
//Remove unnecessary second path
Line2.parentNode.removeChild(Line2);
会导致你只有这样一个单一的路径:
<path id="Line1" d="M50,50
         A30,30 0 0,1 35,20
         L100,100 M110,110
         L100,0"
      style="stroke:#660000; fill:none;"/>

这是一个 jsFiddle,它在 Firefox 4 中可用(需要HTML5解析器才能使用内联SVG)。

如果你的路径是相对的,那么你需要在附加路径之间添加一些内容,以便第二个路径从正确的位置开始。


1
我猜这就是使用他们那里的命令的优势,特别是带有 M 的 "Moveto"。我可以想象用户必须标记明确的起点和终点的语法,这将使得这样的函数更加复杂。 - Nate Anderson

2

连接d属性

通常,您可以将几个<path>元素的路径数据d属性连接起来以获得组合路径。

不幸的是,您可能会遇到一些使用Mm作为可互换命令的不良实践。

Mm的常见误解:

  • M(moveto)可以是绝对的或相对的。
    z(closepath)命令(小写/大写-无关紧要)不同。
    相对的m命令可以用于复合路径,例如字母“o”的内部“孔”,指的是前一个命令的结束坐标。
  • 实际上,每个第一个mM命令使用绝对坐标-因为没有先前的点
  • 但是,第一个M命令可以是大写或小写-无所谓
    (归咎于规范)
  • 例外:小写m命令引入了一行隐式的相对l lineto命令。(但您可以/应该避免这种情况)

示例1:以(不必要的)相对m命令开头的路径

svg{
  border:1px solid #ccc;
  width:25%;
}

path{
  fill:#555;
}
<p>Seperate paths</p>
<svg viewBox="0 0 50 10">
  <path id="path1" d="m 20 0 l 10 0 l 0 10 l -10 0z" />
  <path id="path2" d="m 40 0 l 10 0 l 0 10 l -10 0z" />
  <path id="path3" d="m 0 0 l 10 0 l 0 10 l -10 0z" />
</svg>

<p>Merged paths</p>
<svg viewBox="0 0 50 10">
  <path id="pathConcat" 
  d="
 m 20 0 l 10 0 l 0 10 l -10 0z 
 m 40 0 l 10 0 l 0 10 l -10 0z
 m 0 0 l 10 0 l 0 10 l -10 0z
 " />
</svg>

<p>Merged paths - fixed</p>
<svg viewBox="0 0 50 10">
  <path id="pathConcat" 
  d="
 M 20 0 l 10 0 l 0 10 l -10 0z 
 M 40 0 l 10 0 l 0 10 l -10 0z
 M 0 0 l 10 0 l 0 10 l -10 0z
 " />
</svg>

修复方法:只需将每个起始的m替换为绝对M

例2:m命令用于相邻的linetos

例外情况是后面跟着坐标的m命令-用作后续l(相对linetos)的简写。 (也请参见w3c规格。

svg{
  border:1px solid #ccc;
  width:25%;
}

path{
  fill:#555;
}
<p>Seperate paths</p>
<svg viewBox="0 0 50 10">
  <path id="path1" d="m 20 0  10 0  0 10  -10 0z" />
  <path id="path2" d="m 40 0  10 0  0 10  -10 0z" />
  <path id="path3" d="m 0 0  10 0  0 10  -10 0z" />
</svg>

<p>Merged paths</p>
<svg viewBox="0 0 50 10">
  <path id="pathConcat" 
  d="
 m 20 0  10 0  0 10  -10 0z 
 m 40 0  10 0  0 10  -10 0z
 m 0 0  10 0  0 10  -10 0z
 " />
</svg>

<p>Merged paths - fixed</p>
<svg viewBox="0 0 50 10">
  <path id="pathConcat" 
  d="
 m 20 0  10 0  0 10  -10 0z 
 M 40 0  l 10 0  0 10  -10 0z
 M 0 0  l 10 0  0 10  -10 0z
 " />
</svg>

修复:插入 l 命令

<path d="m 20 0  10 0  0 10  -10 0z" />

equals

<path d="M 20 0 l 10 0 l 0 10 l -10 0z" />  

或者

<path d="M 20 0 l 10 0  0 10 -10 0z" />

示例三:通过getPathData()修复伪相对m命令

目前仍是草案,并未得到主要浏览器的本地支持。
但是您可以使用Jarek Foksa的polyfill

getPathData()将返回一个命令对象数组,并规范化重复的命令,如下所示:

[
  {type: 'm', values:[20, 0] },
  {type: 'l', values:[10, 0]},
  {type: 'l', values:[0, 10]},
  {type: 'l', values:[-10, 0]}
]

function concatSimple(){
  let d1= path1.getAttribute('d')
  let d2= path2.getAttribute('d')
  let d3= path3.getAttribute('d')
  pathConcat.setAttribute('d', d1+d2)
}

function concatPathData(){
  let pathData1= fixFirstM(path1.getPathData());
  let pathData2= fixFirstM(path2.getPathData());
  let pathData3= fixFirstM(path3.getPathData());
  
  let pathDataConcat = pathData1.concat(pathData2).concat(pathData3);
  pathConcat.setPathData(pathDataConcat);
  
}

// change first m to absolute M
function fixFirstM(pathData){
  pathData[0].type='M';
  return pathData;
}
svg{
  border:1px solid #ccc;
  width:25%;
}

path{
  fill:#555;
}
<p><button onclick="concatSimple()">concat d simple</button>
  <button onclick="concatPathData()">concat d pathData</button>
</p>


<svg viewBox="0 0 50 10">
  <path id="path1" d="m 20 0  10 0  0 10  -10 0z" />
  <path id="path2" d="m 40 0  10 0  0 10  -10 0z" />
  <path id="path3" d="m 0 0  10 0  0 10  -10 0z" />
</svg>

<svg viewBox="0 0 50 10">
  <path id="pathConcat" d="" />
</svg>



<script src="https://cdn.jsdelivr.net/npm/path-data-polyfill@1.0.4/path-data-polyfill.min.js"></script>

为了修复第一个相对的,我们可以通过更改第一个命令类型进行转换。
 pathData[0].type='M';

建议:只有在实际上是相对的情况下,才使用相对的m命令:

  • 如果你需要一个简写方式来跟随l命令(比如m 20 0 10 0 0 10 -10 0z
  • 对于复合路径中的相对(子路径)起点,比如字母"o"

实际合并形状:删除重叠的形状

如果您需要合并形状-paper.js拥有一些强大的路径操作,如联合、减去等。
在这里解释:"将两个基于贝塞尔曲线的形状合并成一个以创建新轮廓"


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