如何从 SVG 路径的交集找到新路径(形状)?
How to find a new path (shape) from intersection of SVG paths?
我需要相交 2 条 SVG 路径并获得与它们相交的路径。
它应该在浏览器或 Node.js 中工作(其中任何一个,而不是两者)。
我需要交集,clip-path
的用法不正确。
如果由于某种原因交集将使用 transform
没关系(我会自己删除它)。
我认为有一个库,但我只找到了
- svg-intersections - returns 点数组,但我需要
path
- path-intersection - 不能让它工作 - 由于某种原因它总是 returns 一个空数组
- snap.svg - 似乎提供了一些有用的东西,但我不明白如何使用它
就像 Difference and Intersection of paths in SVG using Snap.path.intersection
例如相交以下路径:
M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z
m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z
我想要这样的东西:
M 43.943359 11.123047 C 40.995759 11.900151 38.742188 12.572266 38.742188 12.572266 L 35.236328 14.996094 C 44.091432999999995 21.21816 55.052161 29.822765 57.455078 37.628906 L 66.939453 33.650391 63.632812 30.410156 C 58.77426 27.95814 52.364322 23.85552 52.214844 19.224609 L 43.943359 11.123047 z
这是示例中带有 path
的交互式代码段(忽略颜色 - 为清楚起见)- 需要从 #path1
获取 Intersection 和#path2
:
svg { width: 10em; width: 100vmin; outline: 1px dotted blue; display: none; }
input { display: none; }
label { width: 10em; float: left; clear: left; cursor: pointer; line-height: 2em; margin: 0 .5em .25em 0; padding: 0 .25em; border: 1px solid; }
:checked + * + * + label { background: antiquewhite; color: blue; }
:checked + * + * + * + * + * + svg { display: inline-block; }
<input type=radio name=svg id=in checked>
<input type=radio name=svg id=out>
<input type=radio name=svg id=cp>
<label for=in>Input</label>
<label for=out>Intersection</label>
<label for=cp>Clip</label>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="22 0 76 64">
<path id="path1"
style="fill:rgba(255,0,0,.5); stroke:red;stroke-width:0.26458332px;"
d="M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z"
/>
<path id="path2"
style="fill:rgba(0,255,0,.5);stroke:green;stroke-width:0.26458332px;"
d="m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z"
/>
</svg>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="22 0 76 64">
<path
style="fill:rgba(0,0,255,.5);stroke:blue;stroke-width:0.26458332px;"
d="M 43.943359 11.123047 C 40.995759 11.900151 38.742188 12.572266 38.742188 12.572266 L 35.236328 14.996094 C 44.091432999999995 21.21816 55.052161 29.822765 57.455078 37.628906 L 66.939453 33.650391 63.632812 30.410156 C 58.77426 27.95814 52.364322 23.85552 52.214844 19.224609 L 43.943359 11.123047 z"
/>
</svg>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="22 0 76 64">
<clipPath id="clip2">
<use xlink:href="#path2" />
</clipPath>
<use xlink:href="#path1" clip-path="url(#clip2)" />
</svg>
示例Snap.svg:
var p1 = "M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z"
var p2 = "m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z"
var intersection = Snap.path.intersection(p1, p2)
console.log(intersection)
.as-console-wrapper.as-console-wrapper { max-height: 100vh }
<script src=//cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js></script>
Snap.svg 似乎具有执行此操作所需的基本功能。他们中的一些人有点尴尬,但这可能是前进的方向。请将此视为伪代码,而不是按原样工作:
// returns something like [ [ "M", 1, 2 ], ["C", 3, 4, 5, 6, 7, 8 ], ... ]
// and has a custom .toString() method
segList1 = Snap.path.toCubic(p1)
segList2 = Snap.path.toCubic(p2)
intersections = Snap.path.intersection(p1, p2)
// handle the subpaths between neighbouring intersections
intersectionPaths = intersections.map((point, id) => {
from = point
to = intersections[id + 1 < intersections.length ? id + 1 : 0]
return pathBetweenIntersections(from, to)
})
// return the two paths between two neighbouring intersection points
function pathBetweenIntersections (from, to) {
// list the segments between the intersection points on first path
// TODO: handle cases when from.segment1 >= to.segment1
subSegList1 = segList1.slice(from.segment1 + 1, to.segment1)
// first segment has the intersection point
startSegments = [ from.bez1.slice(0, 2).unshift('M'), from.bez1.slice(2, 8).unshift('C') ]
startString1 = Snap.path.toCubic(startSegments).toString()
// construct a path element from the segment
startPath1 = Paper.path(startString1)
startPathLength1 = startPath1.getTotalLength()
// get the relevant subpath from intersection point to end
startPathString1 = startPath1.getSubpath(from.t1 * startPathLength1, startPathLength1)
subSegList1 = Snap.path.toCubic(startPathString1).concat(subSegList1)
// and the same for the last segment
endSegments = [ to.bez1.slice(0, 2).unshift('M'), to.bez1.slice(2, 8).unshift('C') ]
endString1 = Snap.path.toCubic(endSegments).toString()
endPath1 = Paper.path(endString1)
endPathLength1 = endPath1.getTotalLength()
endPathString1 = endPath1.getSubpath(0, to.t1 * endPathString1)
subSegList1.push(Snap.path.toCubic(endPathString1)[1])
subSegList2 = // do the same for segList2
return {
p1: Snap.path.toCubic(subSegList1).toString(),
p2: Snap.path.toCubic(subSegList2).toString()
}
}
我们可以使用 PaperJS 布尔运算来实现,它可以使用 SVG 路径进行运算。
PaperJS 有 5 种不同的布尔运算:exclude
、subtract
、unite
、intersect
、divide
我们将使用其中的一种姓名 intersect
。此操作也是具有相同名称的函数,它们 return item
对象具有函数 exportSVG()
。它 return 是正确的 SVG Path element
,它具有两条路径相交的新形状。
正确解的例子
paper.install(window);
window.onload = function()
{
paper.setup('canvas');
var p1 = 'M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z',
p2 = 'm 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z',
path1 = new Path(p1),
path2 = new Path(p2);
path1.fillColor = 'rgba(255,0,0,.5)';
path1.position = new Point(25, 25);
path2.fillColor = 'rgba(0,255,0,.5)';
path2.position = new Point(40, 25);
var result = path2.intersect(path1);
result.selected = true;
result.fillColor = '#77f';
//exportSVG() docu: http://paperjs.org/reference/item/#exportsvg
var svgPathElement = result.exportSVG(),
dPath = svgPathElement.getAttribute('d');
document.querySelector('path').setAttribute('d', dPath);
var output = document.querySelector('#output');
output.innerHTML = '<pre>' + dPath + '</pre>';
output.innerHTML += '<xmp>' + svgPathElement.outerHTML + '</xmp>';
};
table
{
margin-left:14px;
padding-left:14px;
border-left:1px solid gray;
display:inline-block
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.0/paper-full.min.js"></script>
<canvas id="canvas" width="75" height="75" resize></canvas>
<table><tr><td><b>Our new shape of both paths intersection in separate SVG:</b></td></tr>
<tr><td>
<svg width="75" height="75" viewBox="0 0 75 75">
<path fill="rgba(0,0,255,.5)" d=""/>
</svg>
</td></tr></table>
<div id="output"></div>
有用链接:
我需要相交 2 条 SVG 路径并获得与它们相交的路径。
它应该在浏览器或 Node.js 中工作(其中任何一个,而不是两者)。
我需要交集,clip-path
的用法不正确。
如果由于某种原因交集将使用 transform
没关系(我会自己删除它)。
我认为有一个库,但我只找到了
- svg-intersections - returns 点数组,但我需要
path
- path-intersection - 不能让它工作 - 由于某种原因它总是 returns 一个空数组
- snap.svg - 似乎提供了一些有用的东西,但我不明白如何使用它
就像 Difference and Intersection of paths in SVG using Snap.path.intersection
例如相交以下路径:
M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z
m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z
我想要这样的东西:
M 43.943359 11.123047 C 40.995759 11.900151 38.742188 12.572266 38.742188 12.572266 L 35.236328 14.996094 C 44.091432999999995 21.21816 55.052161 29.822765 57.455078 37.628906 L 66.939453 33.650391 63.632812 30.410156 C 58.77426 27.95814 52.364322 23.85552 52.214844 19.224609 L 43.943359 11.123047 z
这是示例中带有 path
的交互式代码段(忽略颜色 - 为清楚起见)- 需要从 #path1
获取 Intersection 和#path2
:
svg { width: 10em; width: 100vmin; outline: 1px dotted blue; display: none; }
input { display: none; }
label { width: 10em; float: left; clear: left; cursor: pointer; line-height: 2em; margin: 0 .5em .25em 0; padding: 0 .25em; border: 1px solid; }
:checked + * + * + label { background: antiquewhite; color: blue; }
:checked + * + * + * + * + * + svg { display: inline-block; }
<input type=radio name=svg id=in checked>
<input type=radio name=svg id=out>
<input type=radio name=svg id=cp>
<label for=in>Input</label>
<label for=out>Intersection</label>
<label for=cp>Clip</label>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="22 0 76 64">
<path id="path1"
style="fill:rgba(255,0,0,.5); stroke:red;stroke-width:0.26458332px;"
d="M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z"
/>
<path id="path2"
style="fill:rgba(0,255,0,.5);stroke:green;stroke-width:0.26458332px;"
d="m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z"
/>
</svg>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="22 0 76 64">
<path
style="fill:rgba(0,0,255,.5);stroke:blue;stroke-width:0.26458332px;"
d="M 43.943359 11.123047 C 40.995759 11.900151 38.742188 12.572266 38.742188 12.572266 L 35.236328 14.996094 C 44.091432999999995 21.21816 55.052161 29.822765 57.455078 37.628906 L 66.939453 33.650391 63.632812 30.410156 C 58.77426 27.95814 52.364322 23.85552 52.214844 19.224609 L 43.943359 11.123047 z"
/>
</svg>
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="22 0 76 64">
<clipPath id="clip2">
<use xlink:href="#path2" />
</clipPath>
<use xlink:href="#path1" clip-path="url(#clip2)" />
</svg>
示例Snap.svg:
var p1 = "M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z"
var p2 = "m 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z"
var intersection = Snap.path.intersection(p1, p2)
console.log(intersection)
.as-console-wrapper.as-console-wrapper { max-height: 100vh }
<script src=//cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js></script>
Snap.svg 似乎具有执行此操作所需的基本功能。他们中的一些人有点尴尬,但这可能是前进的方向。请将此视为伪代码,而不是按原样工作:
// returns something like [ [ "M", 1, 2 ], ["C", 3, 4, 5, 6, 7, 8 ], ... ]
// and has a custom .toString() method
segList1 = Snap.path.toCubic(p1)
segList2 = Snap.path.toCubic(p2)
intersections = Snap.path.intersection(p1, p2)
// handle the subpaths between neighbouring intersections
intersectionPaths = intersections.map((point, id) => {
from = point
to = intersections[id + 1 < intersections.length ? id + 1 : 0]
return pathBetweenIntersections(from, to)
})
// return the two paths between two neighbouring intersection points
function pathBetweenIntersections (from, to) {
// list the segments between the intersection points on first path
// TODO: handle cases when from.segment1 >= to.segment1
subSegList1 = segList1.slice(from.segment1 + 1, to.segment1)
// first segment has the intersection point
startSegments = [ from.bez1.slice(0, 2).unshift('M'), from.bez1.slice(2, 8).unshift('C') ]
startString1 = Snap.path.toCubic(startSegments).toString()
// construct a path element from the segment
startPath1 = Paper.path(startString1)
startPathLength1 = startPath1.getTotalLength()
// get the relevant subpath from intersection point to end
startPathString1 = startPath1.getSubpath(from.t1 * startPathLength1, startPathLength1)
subSegList1 = Snap.path.toCubic(startPathString1).concat(subSegList1)
// and the same for the last segment
endSegments = [ to.bez1.slice(0, 2).unshift('M'), to.bez1.slice(2, 8).unshift('C') ]
endString1 = Snap.path.toCubic(endSegments).toString()
endPath1 = Paper.path(endString1)
endPathLength1 = endPath1.getTotalLength()
endPathString1 = endPath1.getSubpath(0, to.t1 * endPathString1)
subSegList1.push(Snap.path.toCubic(endPathString1)[1])
subSegList2 = // do the same for segList2
return {
p1: Snap.path.toCubic(subSegList1).toString(),
p2: Snap.path.toCubic(subSegList2).toString()
}
}
我们可以使用 PaperJS 布尔运算来实现,它可以使用 SVG 路径进行运算。
PaperJS 有 5 种不同的布尔运算:exclude
、subtract
、unite
、intersect
、divide
我们将使用其中的一种姓名 intersect
。此操作也是具有相同名称的函数,它们 return item
对象具有函数 exportSVG()
。它 return 是正确的 SVG Path element
,它具有两条路径相交的新形状。
正确解的例子
paper.install(window);
window.onload = function()
{
paper.setup('canvas');
var p1 = 'M 24.379464,51.504463 23.434524,23.156249 38.742559,12.572916 c 0,0 29.860118,-9.0714281 17.00893,0.755953 -12.851191,9.82738 13.229166,19.465774 13.229166,19.465774 z',
p2 = 'm 32.883928,0.28869028 c 0,0 -15.686011,1.51190452 -8.504463,7.18154712 7.181546,5.6696426 50.270836,30.0491076 26.458332,42.3333336 -23.8125,12.284226 47.058036,14.174107 47.058036,14.174107 z',
path1 = new Path(p1),
path2 = new Path(p2);
path1.fillColor = 'rgba(255,0,0,.5)';
path1.position = new Point(25, 25);
path2.fillColor = 'rgba(0,255,0,.5)';
path2.position = new Point(40, 25);
var result = path2.intersect(path1);
result.selected = true;
result.fillColor = '#77f';
//exportSVG() docu: http://paperjs.org/reference/item/#exportsvg
var svgPathElement = result.exportSVG(),
dPath = svgPathElement.getAttribute('d');
document.querySelector('path').setAttribute('d', dPath);
var output = document.querySelector('#output');
output.innerHTML = '<pre>' + dPath + '</pre>';
output.innerHTML += '<xmp>' + svgPathElement.outerHTML + '</xmp>';
};
table
{
margin-left:14px;
padding-left:14px;
border-left:1px solid gray;
display:inline-block
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.0/paper-full.min.js"></script>
<canvas id="canvas" width="75" height="75" resize></canvas>
<table><tr><td><b>Our new shape of both paths intersection in separate SVG:</b></td></tr>
<tr><td>
<svg width="75" height="75" viewBox="0 0 75 75">
<path fill="rgba(0,0,255,.5)" d=""/>
</svg>
</td></tr></table>
<div id="output"></div>
有用链接: