New to raphael.js, I'm looking for an explanation on how to do something like move planets around a sun in an orbit. I'm stuck trying to create a path and animate the movement of the circle around it.
Thanks for any pointers in the right direction!
New to raphael.js, I'm looking for an explanation on how to do something like move planets around a sun in an orbit. I'm stuck trying to create a path and animate the movement of the circle around it.
Thanks for any pointers in the right direction!
My friend @Kevin Nielsen is right, you'll want "getPointAtLength." There's a nice little Raphael function here that adds an .animateAlong() function, though it needs a little modification to work on circular objects. I stripped it to the necessities for you.
Assuming you recognize post-1609 astronomy, you'll want elliptical orbits. (Though the difference in the short and long radii are quite small in reality, which is why Copernicus was a bit off the mark.) But you can't use the .ellipse() function, since you need the ellipse as a path in order to animate along it. See the SVG specifications for the elliptical arc, or just try a bunch of combinations until it looks right, like I did:
var paper = Raphael("canvas", 500, 500);
var center = {x: 200, y: 100 };
var a = 100;
var b = 80;
//see http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
var ellipse = "M" + (center.x - a) + "," + center.y + " a " + a + "," + b + " 0 1,1 0,0.1";
var orbit = paper.path(ellipse);
Now you want to draw the earth at one of the foci of the ellipse, and the moon along the path. We'll start it at the perigee.
var focus = Math.pow(a*a - b*b, 0.5);
var palebluedot = paper.circle(center.x - focus, center.y, 25)
.attr({
stroke: 0,
fill: "blue"
});
var moon = paper.circle(center.x - a, center.y, 10)
.attr({
stroke: 0,
fill: "#CCC"
});
And here's your modified "animateAlong" function:
//Adapted from https://github.com/brianblakely/raphael-animate-along/blob/master/raphael-animate-along.js
Raphael.el.animateAlong = function(path, duration, easing, callback) {
var element = this;
element.path = path;
element.pathLen = element.path.getTotalLength();
duration = (typeof duration === "undefined") ? 5000 : duration;
easing = (typeof easing === "undefined") ? "linear" : duration;
//create an "along" function to take a variable from zero to 1 and return coordinates. Note we're using cx and cy specifically for a circle
paper.customAttributes.along = function(v) {
var point = this.path.getPointAtLength(v * this.pathLen),
attrs = {
cx: point.x,
cy: point.y
};
this.rotateWith && (attrs.transform = 'r'+point.alpha);
return attrs;
};
element.attr({along: 0 }).animate({along: 1}, duration, easing, function() {
callback && callback.call(element);
});
};
and here it is:
moon.animateAlong(orbit, 2000);
@Chris Wilson's answer is right on the money.
One slight modification I needed was to have the animation repeat indefinitely. @boom doesn't ask for it specifically, but as I can imagine this could be a common requirement for orbit animations, here's my modification to Chris's version of .animateAlong()
:
Raphael.el.animateAlong = function(path, duration, repetitions) {
var element = this;
element.path = path;
element.pathLen = element.path.getTotalLength();
duration = (typeof duration === "undefined") ? 5000 : duration;
repetitions = (typeof repetitions === "undefined") ? 1 : repetitions;
paper.customAttributes.along = function(v) {
var point = this.path.getPointAtLength(v * this.pathLen),
attrs = { cx: point.x, cy: point.y };
this.rotateWith && (attrs.transform = 'r'+point.alpha);
return attrs;
};
element.attr({along:0});
var anim = Raphael.animation({along: 1}, duration);
element.animate(anim.repeat(repetitions));
};
Note that I've dropped the easing and callback parameters (as I didn't need them) and added the repetitions
parameter, which specifies the number of repetitions to perform.
An example call (starting an endlessly looping orbit animation) is:
moon.animateAlong(orbit, 2000, Infinity);