如何规范化SVG路径数据(跨浏览器)?

时间:2012-10-19 15:24:07

标签: javascript path svg

我试图找到一种实现跨浏览器路径规范化器的方法。有一种原生方式被描述为here,功能范例是here,但它仅适用于最新的Opera(但不适用于IE,FF,Safari,Chrome)。

本机方式使用pathElm.normalizedPathSegList并将所有相对坐标转换为绝对坐标,并将所有路径段类型表示为以下类型子集:M,L,C,z。

我发现只有one javascript codejsfiddled functional example of it,但它仅适用于IE和FF。 Chrome提供“未捕获错误:INDEX_SIZE_ERR:DOM例外1”。如何在Opera,Safari和Chrome中修复此功能,还是有其他方法可以规范化SVG路径吗?

3 个答案:

答案 0 :(得分:3)

编辑:不要用这个!我对它进行了更多的测试并且意识到A-> C转换在所有情况下都不可靠,并且一些其他路径命令组合也失败了。请改用this

最后让它在Safari,Opera,IE9,Firefox和Chrome中运行: http://jsfiddle.net/timo2012/M6Bhh/41/

该函数规范化SVG路径的数据,以便将所有路径段转换为M,C,L和z(=绝对坐标,这意味着所有相对坐标都转换为绝对坐标)。所有其他段都是微不足道且100%准确,但是弧(A)是一种特殊情况,您可以选择是否将弧转换为线(L),二次曲线(Q)或三次曲线(C)。最精确的是线条,但是我们失去了分辨率独立性。由于某些原因,Quadratics在某些弧线中失败,但是立方体更准确。

如果我们有以下路径:

<svg width="400" height="400">
    <path stroke="red" stroke-width="3" d="M30 30 S40 23 23 42 L23,42 C113.333,113.333 136.667,113.333 150,80 t40,50 T230,240 q20 20 54 20 s40 23 23 42 t20,30 a20,30 0,0,1 -50,-50"/>
</svg>

并使用以下标准化它:

var path = document.querySelector('path');
path.normalizePath(3, 0.1); // 3 = C = cubic curves. Best alternative, rather good accuracy and path data remains reasonable sized

标准化版本是这样的:

<svg width="400" height="400">
    <path stroke="red" stroke-width="3" d="M 30 30 C 30 30 40 23 23 42 L 23 42 C 113.333 113.333 136.667 113.333 150 80 C 150 80 163.333 96.6667 190 130 C 216.667 163.333 230 200 230 240 C 243.333 253.333 261.333 260 284 260 C 284 260 324 283 307 302 C 307 302 313.667 312 327 332 C 324.811 336.924 321.997 341.154 318.719 344.448 C 315.441 347.741 311.762 350.033 307.893 351.194 C 304.024 352.355 300.04 352.361 296.169 351.213 C 292.298 350.064 288.616 347.783 285.333 344.5 C 282.05 341.217 279.23 336.996 277.035 332.078 C 274.839 327.161 273.311 321.642 272.537 315.839 C 271.763 310.035 271.759 304.06 272.525 298.254 C 273.291 292.448 274.811 286.924 277 282"/>
</svg>

如果我们将两者相互叠加,结果就是这样(红色是标准化的,黑色是原始的):

Normalized path and original

其他可能性如下:

path.normalizePath(1,0.5); // A->L, Many lines, high accuracy. Very good accuracy, but not so resolution independent, because when scaled, the corners become visible

path.normalizePath(1,40); // A->L, Few lines, less accuracy

path.normalizePath(2,0.5); // A->Q, quadratic curves. I tested this, but not good. Fails in some cases.

这有什么好处?

规范化路径数据的本地方式尚未在所有浏览器中实现,因此到目前为止我们都是自己的。当实现本机方式时,我们不确定所有浏览器是否都采用相同的方式。 SVG文档谈到将弧线转换为线条,但这不是一个好方法,因为SVG的主要优势 - 分辨率独立性 - 将会丢失。我们应该完全控制弧的规范化,并且这个脚本提供了一种方法。

当数据被标准化时,它可以与位图图像中的坐标完全相同地进行更改。如果我们想以Illustrator方式扭曲(Arc,Arch,Bulge,Shell,Flag,Wave,Fish,Rise,Fisheye,Inflate,Squeeze,Twist)路径或扭曲路径以实现透视错觉,可以修改规范化路径数据可靠。

该代码基于YannickBochatay的script,我使其更加跨浏览器。

答案 1 :(得分:1)

我想我已经在Opera,Safari和Chrome中找到了DOM例外的原因。

The SVG doc表示getItem() 从列表中返回指定的项目。返回的项目是项目本身而不是副本。对该项目所做的任何更改都会立即反映在列表中。

同样appendItem() 在列表末尾插入一个新项目。如果newItem已经在列表中,则在将插入此列表之前,将从之前的列表中删除。插入的项目是项目本身而不是副本。

所以这引用了原始项目:

seg = path1.pathSegList.getItem(i);

当此项目使用

附加到其他路径的段列表时
newpath.pathSegList.appendItem(seg);

每个浏览器都有自己的意见如何处理原始seg。 IE9和FF保留原始path1的段列表完整(或至少保留索引(上例中的i)),Safari,Chrome和Opera从path1的段列表中删除seg。 SVG文档明确说明项目( 已从其上一个列表 中删除),因此IE9和FF似乎有错误的实现。因此,我(还)非常确定该项目是否已从之前的列表中删除,或者仅保留了索引(稍后我会检查)。

编辑:选中此项,并确认IE9和FF保留原始路径(path1)段列表的完整性,当它的段被附加到其他路径的段列表时,索引也会保留。 Safari,Chrome和Opera中的行为是不同的:当附加到其他列表时,该项目将从原始列表中删除,到目前为止,索引也会更新(旧的索引在追加后不再有效)。制作jsfiddle确认了差异。 IE9和FF返回seg列表长度1,1,10,10。 Opera,Safari,Chrome返回1,0,10,8。

再一次,我们必须以某种方式考虑浏览器的差异,例如。在第一个项目之前添加虚拟seg或查询path1.numberOfItems()以确定是否修改了原始路径。

答案 2 :(得分:1)

编辑:我修复了Raphaël错误并使用动画和非动画复杂路径进行了全面测试,所以我认为使用Raphaël进行路径规范化是明智的。 bug及其解决方法的解释如下:https://stackoverflow.com/a/13079377/1691517。 Raphaël的path2curve函数可以轻松地将所有路径命令(也就是A,即Arc)转换为标准化形式(即Cubic曲线)。很好,Cubics可以代表所有路径命令!


另一种方法是使用新的Raphaël,这是一个有趣的函数Raphael.path2curve(),它将所有路径命令转换为Cubic曲线,但它有一些bug。下图显示了错误:

enter image description here

功能示例为here,代码如下:

<style>path {fill:none}</style>
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<div id="res" style="width:800px"></div>
<div id="raphael"></div>

<script>
window.onload = function () {
var paper = Raphael("raphael", 400, 400);
var original_path = "M30 30 S40 23 23 42 L23,42 C113.333,113.333 136.667,113.333 150,80 t40,50 T230,240 q20 20 54 20 s40 23 23 42 t20,30 a20,30 0,0,1 -50,-50";
var arr=Raphael.path2curve(original_path);
var normalized_path = arr.toString();

var path1 = paper.path(normalized_path).attr({stroke: "red", "stroke-width":6});
var path2 = paper.path(original_path).attr({stroke: "black", "stroke-width":2});

document.getElementById("res").innerHTML="ORIGINAL PATH (black):<br>"+original_path+"<br><br>NORMALIZED PATH (red):<br>"+normalized_path;
}
</script>​

在Raphaël中进行路径规范化会非常好,因为它支持大量浏览器并使用数组而不是DOM路径段(=速度和向后兼容性)。我做了a bug report。希望它在未来的版本中得到修复。