Given an SVG Path element, how can I convert all path commands into absolute coordinates? For example, convert this path:
<path d="M17,42 l100,0 v100 h-100 z"/>
into this equivalent path:
<path d="M17,42 L117,42 V142 H17 Z"/>
This question was motivated by this question.
The absolutePath function works by beginning at the starting folder and moving up one level for each "../" in the relative path. Then it concatenates the changed starting folder with the relative path to produce the equivalent absolute path.
It can draw anything! I've heard that under the hood all the other drawing elements ultimately use path anyway. The path element takes a single attribute to describe what it draws: the d attribute.
We can shorten the above path declaration a little bit by using the "Close Path" command, called with Z . This command draws a straight line from the current position back to the first point of the path. It is often placed at the end of a path node, although not always.
A path is defined in SVG using the 'path' element. The basic shapes are all described in terms of what their equivalent path is, which is what their shape is as a path. (The equivalent path of a 'path' element is simply the path itself.)
Here's the JavaScript code I came up with:
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;
}
}
Used like so with the path from the question:
var path = document.querySelector('path');
convertToAbsolute(path);
console.log(path.getAttribute('d'));
// M 17 42 L 117 42 V 142 H 17 Z
Edit: Here's a test page with a path that includes every command (absolute and relative) interleaved and shows that the conversion works in the now-current versions of IE, Chrome, FF, and Safari.
http://phrogz.net/svg/convert_path_to_absolute_commands.svg
If you have Raphaël, you have both Raphael.pathToRelative
and Raphael._pathToAbsolute
.
Raphael._pathToAbsolute
is not in documentation (like pathToRelative) and is used in many places in Raphael source internally. But it can be used also externally, if you add _
before function name this way: Raphael._pathToAbsolute
.
Online conversion: http://jsbin.com/mudusiseta
The usage is the same as relative one:
<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>
I don't know why pathToAbsolute is not in documentation. It should.
If you want this to work in Web Workers (to speedup code), it is easy to grab the desired function from Raphael (which should be allowed by the licence) and use it as a DOM free method. Raphael is a DOM manipulating library (as well as jQuery) and it cannot be used in Workers, because DOM is not supported in Workers. If you have very complex paths, the browser may hang and to prevent this Web Workers provide a solution in modern browsers.
Here is library for svg path (d
attr) manipulations: https://github.com/fontello/svgpath.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With