开发者

Is there a way to merge two path elements (svg) using Javascript?

I have drawn two path lines using SVG and I've saved these elements into two variables in my javascript code:开发者_StackOverflow中文版 'Line1', and 'Line2', and I need to merge the two lines into one single path element. Is there a way to do so?


Are your paths defined relatively (small letters) or absolutely (capitals)? If absolute, joining two paths is trivial, just append the values of the d attribute. If you have two paths like this:

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

Then this JavaScript code:

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);

Will lead to you having a single path like this:

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

Here's a jsFiddle, it works in Firefox 4 (needs an HTML5 parser so you can have inline SVG).

If your paths are relative then you're going to have to add something between the appended paths so that the second one starts in the correct place.


Concatenate d attributes

Usually, you can just concatenate the pathdata d attribute of several <path> elements to get a combined path.

Unfortunately, you might encounter some »bad practices« using M or m as interchangeable commands.

Common misconceptions about M or m:

  • M (moveto) can be absolute or relative.
    Unlike the z (closepath) command (lowercase/uppercase – doesn't matter).
    Relative m commands can be used used for compound paths like e.g the inner "hole" of the letter "o" referring to the previous command's end coordinate.
  • In fact, every first m or M command uses absolute coordinates – since there are no preceding points.
  • However, the first M command can be uppercase or lowercase – doesn't matter
    (blame the specs)
  • Exception: the lowercase m command introduces a row of implicit relative l lineto commands. (But you can/should also avoid this)

Example 1: paths starting with (unnecessary) relative m command

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>

Fix: just replace each starting m with an absolute M

Example 2: m command for adjacent linetos

The exception are m commands followed by coordinates – used as a shorthand for succeeding l (relative linetos). (See also w3c specs.)

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>

Fix: insert l commands

<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" />  

or

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

Example 3: fix pseudo relative m commands via getPathData()

Currently still a draft and not natively supported by major browser.
However you can use Jarek Foksa's polyfill..

getPathData() will return an array of command objects and normalize repeated commands like this:

[
  {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>

To fix the first relative m we can convert by changing the first command type

 pathData[0].type='M';

Recommendation: only use relative m commands if they are actually relative:

  • if you need a shorthand for following l commands (like m 20 0 10 0 0 10 -10 0z)
  • for relative (subpath) starting points in compound paths – like the letter "o"

Actually merging shapes: removing overlapping shapes

If you need to merge shapes - paper.js has some powerful path operations like unite, subtract etc.
Explained here: "Merging two bezier-based shapes into one to create a new outline"

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜