将SVG路径转换为绝对命令

14
给定一个SVG Path元素,如何将所有路径命令转换为绝对坐标?例如,将此路径转换为:
<path d="M17,42 l100,0 v100 h-100 z"/>

转换成等效路径:

<path d="M17,42 L117,42 V142 H17 Z"/>

这个问题的动机来自这个问题

3个回答

15

这是我想出的 JavaScript 代码:

function convertToAbsolute(path){
  var x0,y0,x1,y1,x2,y2,segs = path.pathSegList;
  for (var x=0,y=0,i=0,len=segs.numberOfItems;i<len;++i){
    var seg = segs.getItem(i), c=seg.pathSegTypeAsLetter;
    if (/[MLHVCSQTA]/.test(c)){
      if ('x' in seg) x=seg.x;
      if ('y' in seg) y=seg.y;
    }else{
      if ('x1' in seg) x1=x+seg.x1;
      if ('x2' in seg) x2=x+seg.x2;
      if ('y1' in seg) y1=y+seg.y1;
      if ('y2' in seg) y2=y+seg.y2;
      if ('x'  in seg) x+=seg.x;
      if ('y'  in seg) y+=seg.y;
      switch(c){
        case 'm': segs.replaceItem(path.createSVGPathSegMovetoAbs(x,y),i);                   break;
        case 'l': segs.replaceItem(path.createSVGPathSegLinetoAbs(x,y),i);                   break;
        case 'h': segs.replaceItem(path.createSVGPathSegLinetoHorizontalAbs(x),i);           break;
        case 'v': segs.replaceItem(path.createSVGPathSegLinetoVerticalAbs(y),i);             break;
        case 'c': segs.replaceItem(path.createSVGPathSegCurvetoCubicAbs(x,y,x1,y1,x2,y2),i); break;
        case 's': segs.replaceItem(path.createSVGPathSegCurvetoCubicSmoothAbs(x,y,x2,y2),i); break;
        case 'q': segs.replaceItem(path.createSVGPathSegCurvetoQuadraticAbs(x,y,x1,y1),i);   break;
        case 't': segs.replaceItem(path.createSVGPathSegCurvetoQuadraticSmoothAbs(x,y),i);   break;
        case 'a': segs.replaceItem(path.createSVGPathSegArcAbs(x,y,seg.r1,seg.r2,seg.angle,seg.largeArcFlag,seg.sweepFlag),i);   break;
        case 'z': case 'Z': x=x0; y=y0; break;
      }
    }
    // Record the start of a subpath
    if (c=='M' || c=='m') x0=x, y0=y;
  }
}

使用问题中给出的路径,可以像这样使用:

var path = document.querySelector('path');
convertToAbsolute(path);
console.log(path.getAttribute('d'));
// M 17 42 L 117 42 V 142 H 17 Z

编辑: 这是一个测试页面,包含绝对路径和相对路径命令的间隔,同时显示转换结果在现行版本的IE、Chrome、FF和Safari中均可以正常工作。
http://phrogz.net/svg/convert_path_to_absolute_commands.svg


seg.pathSegTypeAsLetter总是返回大写字母,即使路径中有小写字母命令。在switch语句背后还有其他想法吗? - Rajkamal Subramanian
如果您不需要保留所有类型的片段,那么使用SVG中的内置功能pathElm.normalizedPathSegList也是一种选择,它提供了绝对moveto、lineto、curveto和close。 - Erik Dahlström
@ErikDahlström 我想使用它,但是在 WebKit 上不存在 normalizedPathSegList(始终返回 undefined)。 - Phrogz
1
谢谢!我把它变成了一个方便的phantomjs svg2abs 工具。 - ecmanaut
4
这在Chrome 40+中已不再适用,因为pathSegList已被废弃并将被替换为甚至还没有测试版或实现的内容。 - thednp
显示剩余3条评论

9
如果您使用Raphaël,您将同时拥有Raphael.pathToRelativeRaphael._pathToAbsoluteRaphael._pathToAbsolute没有在文档(如pathToRelative)中提到,并且在Raphael源代码中的许多地方内部使用。但是如果您在函数名称前面添加_,也可以在外部使用它,例如:Raphael._pathToAbsolute在线转换: http://jsbin.com/mudusiseta 使用方式与相对路径一样:
<script src="raphael.js"></script>
<script>
  // ...

  var paper = Raphael(10, 50, 320, 200);
  var path_string = "M10 10 L 20 20 L 100 10"; // Original coordinates
  var path = paper.path(path_string);

  // To relative coordinates
  var path_string_rel = Raphael.pathToRelative(path_string);
  console.log(path_string_rel);

  // To absolute coordinates
  var path_string_abs = Raphael._pathToAbsolute(path_string_rel);
  console.log(path_string_abs);

  // ...    
</script>

我不知道为什么pathToAbsolute没有在文档中。它应该被包含。
如果你想让这个工作在Web Workers中(以加快代码),很容易从Raphael中获取所需的函数(这应该是允许的许可证),并将其用作无DOM方法。 Raphael是一个DOM操作库(以及jQuery),不能在Workers中使用,因为DOM在Workers中不受支持。如果你有非常复杂的路径,浏览器可能会挂起,为了防止这种情况,在现代浏览器中Web Workers提供了解决方案。

2

我们如何在HTML页面中使用这个库,而不是在Node中使用? - Kpym

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接