Преобразовать SVG-путь в абсолютные команды

Учитывая элемент пути SVG, как я могу преобразовать все команды пути в абсолютные координаты? Например, преобразуйте этот путь:

<path d="M17,42 l100,0 v100 h-100 z"/>

в этот эквивалентный путь:

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

Этот вопрос был мотивирован этим вопросом.

3 ответа

Решение

Вот код 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

Если у вас есть Raphaël, у вас есть оба Raphael.pathToRelative а также Raphael._pathToAbsolute,

Raphael._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. Если у вас очень сложные пути, браузер может зависнуть и для предотвращения этого веб-работники предоставляют решение в современных браузерах.

Вот библиотека для SVG путь (d attr) манипуляции: https://github.com/fontello/svgpath.

Другие вопросы по тегам