5

我试图找到一种方法来实现跨浏览器路径规范化器。这里描述了一种本机方式,功能示例在这里,但它仅适用于最新的 Opera(但不适用于 IE、FF、Safari、Chrome)。

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

我只找到了一个 javascript 代码jsfiddled 功能示例,但它仅适用于 IE 和 FF。Chrome 给出“未捕获的错误:INDEX_SIZE_ERR:DOM 异常 1”。如何解决这个问题,使其在 Opera、Safari 和 Chrome 中也能正常工作,或者有没有其他方法可以规范化 SVG 路径?

4

3 回答 3

3

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

终于让它在 Safari、Opera、IE9、Firefox 和 Chrome 中工作:http: //jsfiddle.net/timo2012/M6Bhh/41/

该函数对 SVG 路径的数据进行归一化处理,以便将所有路径段转换为 M、C、L 和 z(= 绝对坐标,这意味着所有相对坐标都转换为绝对坐标)。所有其他线段都很简单且 100% 准确,但圆弧 (A) 是一种特殊情况,您可以选择将圆弧转换为直线 (L)、二次曲线 (Q) 还是三次曲线 (C)。最准确的是线条,但随后我们失去了分辨率独立性。由于某种原因,二次曲线在某些弧线中失败,但三次曲线更准确。

如果我们有以下路径:

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

如果我们将两者布局在彼此之上,结果是这样的(红色是标准化的,黑色是原始的):

规范化路径和原始路径

其他可能性如下:

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 的脚本,我使它更跨浏览器。

于 2012-10-21T16:11:05.450 回答
1

我想我已经在 Opera、Safari 和 Chrome 中找到了 DOM 异常的原因。

SVG 文档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() 以确定原始路径是否被修改。

于 2012-10-20T21:49:34.960 回答
1

编辑:我修复了 Raphaël 错误,并对动画和非动画复杂路径进行了彻底测试,所以我认为使用 Raphaël 进行路径规范化是明智的。错误的解释及其修复在这里:https ://stackoverflow.com/a/13079377/1691517 。Raphaël 的 path2curve 函数可以轻松地将所有路径命令(也 A 即 Arc)转换为规范化形式(即三次曲线)。太好了,Cubics 可以代表所有路径命令!


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

在此处输入图像描述

功能示例在这里,代码如下:

<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 路径段(= 速度和向后兼容性)。我做了一个错误报告。希望它在将来的某个版本中得到修复。

于 2012-10-23T16:29:41.007 回答