将复杂的 SVG 文件与 paper.js 合并
Unite complex SVG files with paper.js
我正在尝试将相对复杂的 SVG 结合起来,例如 this。我想将制作“HELLO”字母的路径与后面的大“A”字母结合起来。在 Inkscape 上,我可以通过 selecting 所有路径并转到 Path->Union 来做到这一点,所以路径是从这个转换而来的:
为此:
注意中间的“HELLO”字母的路径现在是如何与它们后面的大“A”结合在一起的。我想通过使用 paper.js
来实现相同的目的,因为这是我能找到的最好的解决方案,可以在不对 SVG 文件进行布尔操作的情况下对它们进行布尔运算,而且因为我无法在没有 GUI 的情况下使用 Inkscape CLI 执行此操作。
我在 paper.js
中创建 this sketch 以加载 SVG 文件并在所有 CompoundPaths
和 Paths
之间建立联合,希望能达到相同的效果,但是,显然不是那么简单,草图的结果路径是:
<path xmlns="http://www.w3.org/2000/svg" d="M-177.63334,-177.64819h755.85598v755.85598h-755.85598z" id="path32" fill="#000000" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"/>
这几乎是一条空路!有蚂蚁知道实现这一目标的方法吗?我正在考虑像 Inkscape 那样检测形状的任何方法,我的意思是,在 Inkscape 上你可以单独 select 并将每个字母作为一个单一的形状来操作,所以如果我能识别每个形状(或闭合路径?)我可以进行并集运算
我对如何实现这一点有点迷茫,所以任何算法、技巧、书籍、博客、代码或几乎任何东西都会受到极大的赞赏!
终于解决了!我必须确保不使用 unite
用作 clipMask
的路径,并确保使用 closePath()
方法关闭所有路径。最终代码如下所示:
var canvas = document.getElementById('canvas')
var scope = paper.setup(new paper.Size(200, 200))
var svg = `{put your svg string here}`;
paper.project.importSVG(svg, function(pathItem) {
// Get Path and CompoundPath which are children from this item
let paths = pathItem.getItems({
className: 'Path'
});
let compoundPaths = pathItem.getItems({
className: 'CompoundPath'
});
// Filter paths that are inside CompoundPaths
paths = paths
.filter((p) => !compoundPaths.some((cp) => cp.children.includes(p)))
// Filter paths that are used as clipping paths
.filter((p) => !p.clipMask);
compoundPaths = compoundPaths.filter((c) => !c.clipMask);
// Close all paths to ensure a correct union
for (const path of compoundPaths.filter((c) => !c.closed)) {
path.closePath();
}
for (const path of paths.filter((c) => !c.closed)) {
path.closePath();
}
// If not paths or compound paths are available, return the same input SVG
if (!paths.length && !compoundPaths.length) {
debugger;
}
else {
// Merge all the paths to build a single path
let unitedItem = undefined;
let compoundPathsStartIndex = 0;
if (paths.length) {
unitedItem = paths[0];
for (let n = 1; n < paths.length; ++n) {
const path = paths[n];
unitedItem = unitedItem.unite(path);
}
} else {
unitedItem = compoundPaths[0];
compoundPathsStartIndex = 1;
}
for (let n = compoundPathsStartIndex; n < compoundPaths.length; ++n) {
const path = compoundPaths[n];
unitedItem = unitedItem.unite(path);
}
// Set fill color otherwise paths exported in the server (which uses node 8) end up without
// a filling color
unitedItem.fillColor = new paper.Color(0, 0, 0);
// Generate the merged SVG string and save it
const outputPathString = unitedItem.exportSVG({
asString: true,
bounds: new paper.Rectangle(0, 0, pathItem.getBounds().width, pathItem.getBounds().height)
});
// let outputSvg = outputPathString;
let outputSvg = `<?xml version="1.0" encoding="utf-8"?>\n<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="${pathItem.getBounds().width}" height="${pathItem.getBounds().height}">`;
outputSvg += outputPathString;
outputSvg += '</svg>';
console.log(outputSvg);
debugger;
}
});
包含 SVG 字符串的完整代码是 here,因为我将其包含在答案中超出了最大正文长度。
我正在尝试将相对复杂的 SVG 结合起来,例如 this。我想将制作“HELLO”字母的路径与后面的大“A”字母结合起来。在 Inkscape 上,我可以通过 selecting 所有路径并转到 Path->Union 来做到这一点,所以路径是从这个转换而来的:
为此:
注意中间的“HELLO”字母的路径现在是如何与它们后面的大“A”结合在一起的。我想通过使用 paper.js
来实现相同的目的,因为这是我能找到的最好的解决方案,可以在不对 SVG 文件进行布尔操作的情况下对它们进行布尔运算,而且因为我无法在没有 GUI 的情况下使用 Inkscape CLI 执行此操作。
我在 paper.js
中创建 this sketch 以加载 SVG 文件并在所有 CompoundPaths
和 Paths
之间建立联合,希望能达到相同的效果,但是,显然不是那么简单,草图的结果路径是:
<path xmlns="http://www.w3.org/2000/svg" d="M-177.63334,-177.64819h755.85598v755.85598h-755.85598z" id="path32" fill="#000000" fill-rule="nonzero" stroke="none" stroke-width="1" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10" stroke-dasharray="" stroke-dashoffset="0" font-family="none" font-weight="none" font-size="none" text-anchor="none" style="mix-blend-mode: normal"/>
这几乎是一条空路!有蚂蚁知道实现这一目标的方法吗?我正在考虑像 Inkscape 那样检测形状的任何方法,我的意思是,在 Inkscape 上你可以单独 select 并将每个字母作为一个单一的形状来操作,所以如果我能识别每个形状(或闭合路径?)我可以进行并集运算
我对如何实现这一点有点迷茫,所以任何算法、技巧、书籍、博客、代码或几乎任何东西都会受到极大的赞赏!
终于解决了!我必须确保不使用 unite
用作 clipMask
的路径,并确保使用 closePath()
方法关闭所有路径。最终代码如下所示:
var canvas = document.getElementById('canvas')
var scope = paper.setup(new paper.Size(200, 200))
var svg = `{put your svg string here}`;
paper.project.importSVG(svg, function(pathItem) {
// Get Path and CompoundPath which are children from this item
let paths = pathItem.getItems({
className: 'Path'
});
let compoundPaths = pathItem.getItems({
className: 'CompoundPath'
});
// Filter paths that are inside CompoundPaths
paths = paths
.filter((p) => !compoundPaths.some((cp) => cp.children.includes(p)))
// Filter paths that are used as clipping paths
.filter((p) => !p.clipMask);
compoundPaths = compoundPaths.filter((c) => !c.clipMask);
// Close all paths to ensure a correct union
for (const path of compoundPaths.filter((c) => !c.closed)) {
path.closePath();
}
for (const path of paths.filter((c) => !c.closed)) {
path.closePath();
}
// If not paths or compound paths are available, return the same input SVG
if (!paths.length && !compoundPaths.length) {
debugger;
}
else {
// Merge all the paths to build a single path
let unitedItem = undefined;
let compoundPathsStartIndex = 0;
if (paths.length) {
unitedItem = paths[0];
for (let n = 1; n < paths.length; ++n) {
const path = paths[n];
unitedItem = unitedItem.unite(path);
}
} else {
unitedItem = compoundPaths[0];
compoundPathsStartIndex = 1;
}
for (let n = compoundPathsStartIndex; n < compoundPaths.length; ++n) {
const path = compoundPaths[n];
unitedItem = unitedItem.unite(path);
}
// Set fill color otherwise paths exported in the server (which uses node 8) end up without
// a filling color
unitedItem.fillColor = new paper.Color(0, 0, 0);
// Generate the merged SVG string and save it
const outputPathString = unitedItem.exportSVG({
asString: true,
bounds: new paper.Rectangle(0, 0, pathItem.getBounds().width, pathItem.getBounds().height)
});
// let outputSvg = outputPathString;
let outputSvg = `<?xml version="1.0" encoding="utf-8"?>\n<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" width="${pathItem.getBounds().width}" height="${pathItem.getBounds().height}">`;
outputSvg += outputPathString;
outputSvg += '</svg>';
console.log(outputSvg);
debugger;
}
});
包含 SVG 字符串的完整代码是 here,因为我将其包含在答案中超出了最大正文长度。