我有一个 SVG,其中包含一个绘制特定形状的
<path>
元素。该路径的坐标包含在路径的 'd'
属性值中。我需要这个形状水平翻转。当我尝试在 Adobe Illustrator 中完成此操作(例如使用 Reflect 工具)时,我在 'd'
属性值中获得双倍的数据大小,因此 SVG 文件的大小也增加了一倍,这实在是太痛苦了。我可以使用 transform
和 scale
函数来翻转形状,而不更改 'd'
中的坐标,但随后我会增加渲染时间和 CPU 使用率,因为我为浏览器或渲染 SVG 的软件添加了额外的工作。
合乎逻辑的做法是将 'd'
内的坐标本身更改为其“相反”,以实现形状的翻转。
我可以编写一个脚本来执行此操作,但可惜我不知道这些坐标的存储格式以及它们实际代表的内容。既有字母也有数字。
所以,我的问题是,如何更改
<path>
元素的 'd'
的坐标以实现整个形状的水平翻转?
这是我用于说明的示例 SVG:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px">
<path id="example" class="st0" d="M492 534h-96q-37 0 -53.5 -12.5t-30.5 -50.5q-20 -54 -44 -165q-17 -79 -17 -105q0 -49 38 -58q17 -3 51 -3h67q41 0 56 7.5t23 32.5h27l-24 -106q-10 -42 -27 -58q-18 -15 -50 -19t-139 -4q-89 0 -128 5.5t-63 21.5q-54 35 -54 122q0 53 25 177q31 146 62 218t76 101 t124 29h258l-18 -80q-7 -34 -19 -43.5t-44 -9.5z"/>
</svg>
这里有一个如何翻转路径的示例。我在两个不同的函数中执行此操作,因为
moveElm()
在其他情况下可能很有用。对于这两个函数来说,数学并不难,基本上只是理解 d 属性中不同命令的作用即可。实现并不完整,但它适用于问题中给出的示例。
这个问题没有说明您想使用哪种编程语言。我在示例中使用的唯一特定语言是getBBox()。在Python中,你可以在包svgpathtools中找到类似的东西,在那里你可以找到方法
path.bbox()
(参见这个答案:https://stackoverflow.com/a/76076555/322084)。找到路径的位置和大小对于移动(和缩放)路径是必要的。
let path = document.getElementById('example');
let path_bbox = path.getBBox();
let x = Math.round(path_bbox.width + 2 * path_bbox.x);
flipElm(path);
moveElm(path, x, 0);
function moveElm(path_elm, x, y){
let d = path_elm.getAttribute('d');
let regexp = /([a-zA-Z])([\s\d\-\.]*)/g;
let new_d = [...d.matchAll(regexp)].map(command => {
let arr = command[2].trim().split(/\s/).map(val => parseFloat(val));
let return_arr = arr;
switch(command[1]){
case 'M':
case 'L':
case 'H':
case 'V':
case 'Q':
case 'T':
return_arr = [arr[0] + x, arr[1] + y];
break;
case 'z':
case 'Z':
return_arr = [];
break;
}
return `${command[1]}${return_arr.join(' ')}`;
}).join(' ');
path_elm.setAttribute('d', new_d);
}
function flipElm(path_elm) {
let d = path_elm.getAttribute('d');
let regexp = /([a-zA-Z])([\s\d\-\.]*)/g;
let new_d = [...d.matchAll(regexp)].map(command => {
let arr = command[2].trim().split(/\s/).map(val => parseFloat(val));
let return_arr = [];
switch (command[1]) {
case 'A':
case 'a':
return_arr = arr.map((num, i) => "not implemented");
break;
case 'z':
case 'Z':
return_arr = [];
break;
default:
return_arr = arr.map((num, i) => (i % 2) ? num : num * -1);
break;
}
return `${command[1]}${return_arr.join(' ')}`;
}).join(' ');
path_elm.setAttribute('d', new_d);
}
svg {
border: solid thin black;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -10 600 680" height="200">
<path class="st0" d="M492 534h-96q-37 0 -53.5 -12.5t-30.5 -50.5q-20 -54 -44 -165q-17 -79 -17 -105q0 -49 38 -58q17 -3 51 -3h67q41 0 56 7.5t23 32.5h27l-24 -106q-10 -42 -27 -58q-18 -15 -50 -19t-139 -4q-89 0 -128 5.5t-63 21.5q-54 35 -54 122q0 53 25 177q31 146 62 218t76 101 t124 29h258l-18 -80q-7 -34 -19 -43.5t-44 -9.5z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -10 600 680" height="200">
<path id="example" class="st0" d="M492 534h-96q-37 0 -53.5 -12.5t-30.5 -50.5q-20 -54 -44 -165q-17 -79 -17 -105q0 -49 38 -58q17 -3 51 -3h67q41 0 56 7.5t23 32.5h27l-24 -106q-10 -42 -27 -58q-18 -15 -50 -19t-139 -4q-89 0 -128 5.5t-63 21.5q-54 35 -54 122q0 53 25 177q31 146 62 218t76 101 t124 29h258l-18 -80q-7 -34 -19 -43.5t-44 -9.5z"/>
</svg>