Here's some code that generalises the best of the above answers and gives Raphael paths a simple .attr({pathXY: [newXPos, newYPos]})
attribute similar to .attr({x: newXPosition})
and .animate({x: newXPosition})
for shapes.
This lets you move your path to a fixed, absolute position or move it by a relative amount in a standard way without hardcoding path strings or custom calculations.
Edit: Code below works in IE7 and IE8. An earlier version of this failed in IE8 / VML mode due to a Raphael bug that returns arrays to .attr('path') in SVG mode but strings to .attr('path') in VML mode.
Code
Add this code (Raphael customAttribute, and helper function) after defining paper
, use as below.
paper.customAttributes.pathXY = function( x,y ) {
// use with .attr({pathXY: [x,y]});
// call element.pathXY() before animating with .animate({pathXY: [x,y]})
var pathArray = Raphael.parsePathString(this.attr('path'));
var transformArray = ['T', x - this.pathXY('x'), y - this.pathXY('y') ];
return {
path: Raphael.transformPath( pathArray, transformArray)
};
};
Raphael.st.pathXY = function(xy) {
// pass 'x' or 'y' to get average x or y pos of set
// pass nothing to initiate set for pathXY animation
// recursive to work for sets, sets of sets, etc
var sum = 0, counter = 0;
this.forEach( function( element ){
var position = ( element.pathXY(xy) );
if(position){
sum += parseFloat(position);
counter++;
}
});
return (sum / counter);
};
Raphael.el.pathXY = function(xy) {
// pass 'x' or 'y' to get x or y pos of element
// pass nothing to initiate element for pathXY animation
// can use in same way for elements and sets alike
if(xy == 'x' || xy == 'y'){ // to get x or y of path
xy = (xy == 'x') ? 1 : 2;
var pathPos = Raphael.parsePathString(this.attr('path'))[0][xy];
return pathPos;
} else { // to initialise a path's pathXY, for animation
this.attr({pathXY: [this.pathXY('x'),this.pathXY('y')]});
}
};
Usage
For absolute translation (move to fixed X,Y position) - Live JSBIN demo
Works with any path or set of paths including sets of sets (demo). Note that since Raphael sets are arrays not groups, it moves each item in the set to the defined position - not the centre of the set.
// moves to x=200, y=300 regardless of previous transformations
path.attr({pathXY: [200,300]});
// moves x only, keeps current y position
path.attr({pathXY: [200,path.pathXY('y')]});
// moves y only, keeps current x position
path.attr({pathXY: [path.pathXY('x'),300]});
Raphael needs to handle both x and y co-ordinates together in the same customAttribute so they can animate together and so they stay in sync with each other.
For relative translation (move by +/- X,Y) - Live JSBIN demo
// moves down, right by 10
path.attr({pathXY: [ path.pathXY('x')+10, path.pathXY('y')+10 ]},500);
This also works with sets, but again don't forget that Raphael's sets aren't like groups - each object moves to one position relative to the average position of the set, so results may not be what are expected (example demo).
For animation (move a path to relative or absolute positions)
Before animating the first time, you need to set the pathXY values, due to a bug/missing feature up to Raphael 2.1.0 where all customAttributes need to be given a numeric value before they are animated (otherwise, they will turn every number into NaN and do nothing, failing silently with no errors, or not animating and jumping straight to the final position).
Before using .animate({pathXY: [newX,newY]});
, run this helper function:
somePath.pathXY();