Animations in SVG are pretty straight forward. We can simply move objects or groups in any x and y position, and rotate it in degrees. There are two ways to animate objects, either with object animation data or with pure javascript transform animation. Both of them are useful, object animation data could be used form simple background animations like clouds or stars, really easy. With javascript transform animation, everything can be animated in a common way.
object animation data
Every SVG element can have its own animation data. This seems not to be very useful, and they are very hard to control, but in some cases this technique might be handy. In example, for some background objects or objects with a constant animation. These animations are timeline based, which means the object moves to the end point in a certain amount of time. Its also possible to stack multiple animation an play them together in one large timeline.
We create a simple square with a pivot point, and add a rotation animation to the object. You can move the pivot point to any position.
We add the Square with the same technique used in the Draw with SVG post. We just add some animation data to the object via Javascript.
var ani = document.createElementNS("http://www.w3.org/2000/svg","animateTransform");//CREATE SVG ANIMATION ELEMENT ani.setAttributeNS(null,"id","Ani"); //ID OF THE ANIMATION, FOR LATER ACCESS ani.setAttribute("attributeName", "transform"); //ANIMATION ATTRIBUTE ani.setAttribute("attributeType", "xml"); //ATTRIBUTE TYPE ani.setAttribute("type", "rotate" ); //ANIMATE ROTATION ani.setAttribute("from", "0"+" "+(pivot.cx.baseVal.value)+" "+(pivot.cy.baseVal.value));//FROM 0 DEG, WITH THE PIVOT POSITION X & Y ani.setAttribute("to", "360"+" "+(pivot.cx.baseVal.value)+" "+(pivot.cy.baseVal.value));//TO 360 DEG, WITH THE PIVOT POSITION X & Y ani.setAttribute("begin", "0s");//START OF THE ANIMATION ani.setAttribute("dur", "2s"); //DURATION OF THE ANIMATION ani.setAttribute("repeatCount", "indefinite"); //REPEAT INFINITY square.appendChild(ani); //APPEND THE ANIMATION TO AN OBJECT
To manipulate the animation while it runs, we change the animation attributes.
//GET THE ANIMATION ID AND CHANGE THE PIVOT OF THE ANIMATION TO THE POSITION OF THE PIVOT POINT document.getElementById("Ani").setAttribute("from", "0"+" "+(pivot.cx.baseVal.value)+" "+(pivot.cy.baseVal.value)); document.getElementById("Ani").setAttribute("to", "360"+" "+(pivot.cx.baseVal.value)+" "+(pivot.cy.baseVal.value));
This is a really simple example of the SVG object animation data. But it is possible to animate every timeline based animation.
Unfortunately there are not many software which handles SVG animation and export them. Synfig Studio http://www.synfig.org, a free and open-source 2D animation software, seams to be a good solution.
dynamic animation data
We create the same animation with pure Javascript. To animate something in Javascript we need to call a function each frame, the render tick. This is done with the requestAnimationFrame method, see “requestAnimationFrame for Smart Animating” by Paul Irish for more information. This is currently the common way to animate objects in Javascript. Changing the transform property of the object each frame moves the object.
We put the requestAnimationFrame function at the top of the script file. Once the animation function is called the animation starts running. The object starts rotating, by transforming it each frame 2 deg around the pivot.
if ( !window.requestAnimationFrame ) { window.requestAnimationFrame = ( function() { return window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) { window.setTimeout( callback, 1000 / 60 ); }; } )(); } var square; var pivot; var rot = 0; function init() { var svg = document.getElementById("SVG_scene"); //GET MAIN SVG ELEMENT var svgNS = "http://www.w3.org/2000/svg"; //DEFINE THE namespaceURI var w = window.innerWidth; //WINDOW WIDTH var h = window.innerHeight; //WINDOW HEIGHT square = document.createElementNS(svgNS,"rect");//CREATE SVG RECT square.setAttributeNS(null,"id","Square"); //ID OF THE CIRCLE, OBJECT CAN BE MODIFIED VIA CSS square.setAttributeNS(null,"x",w/2-50);//CENTER RECT X square.setAttributeNS(null,"y",h/2-50);//CENTER RECT Y square.setAttributeNS(null,"width",100);//RECT WIDTH square.setAttributeNS(null,"height",100);//RECT HEIGHT square.setAttributeNS(null,"fill","#F05011");//SET RANDOM COLOR square.setAttributeNS(null,"stroke","none");//NO STROKE svg.appendChild(square); //ADD CIRCLE TO THE MAIN SVG ELEMENT pivot = document.createElementNS(svgNS,"circle"); //CREATE BASIC SVG ELEMENT(RECT, CIRCLE, ELLIPSE, LINE, POLYLINE, POLYGON) pivot.setAttributeNS(null,"id","Circle"); //ID OF THE CIRCLE, OBJECT CAN BE MODIFIED VIA CSS pivot.setAttributeNS(null,"class","circle"); //CLASS OF THE CIRCLE, OBJECT CAN BE MODIFIED VIA CSS pivot.setAttributeNS(null,"cx",w/2); //CENTER OF THE CIRCLE X pivot.setAttributeNS(null,"cy",h/2); //CENTER OF THE CIRCLE Y pivot.setAttributeNS(null,"r",10); //RADIUS OF THE CIRCLE pivot.setAttributeNS(null,"fill","black"); //FILLCOLOR pivot.setAttributeNS(null,"stroke","none"); //OUTLINE COLOR pivot.onmousedown = movePivot; //ON CLICK EVENT svg.appendChild(pivot); //ADD CIRCLE TO THE MAIN SVG ELEMENT animate() } function movePivot() { //WHILE OBJECT IS CLICKED AND MOVED document.onmousemove = function(e) { pivot.cx.baseVal.value = e.pageX; //MOVE PIVOT TO MOUSE POSITION X pivot.cy.baseVal.value = e.pageY; //MOVE PIVOT TO MOUSE POSITION Y } //IF MOUSE IS RELEASED STOP MOVE EVENT document.onmouseup = function() { document.onmousemove = null } } //THIS FUNCTION IS EXECUTED EACH FRAME function animate() { rot +=2; //ADD EACH FRAME TO THE ROTATION square.setAttribute("transform", "rotate(" + rot + " "+pivot.cx.baseVal.value+" "+ pivot.cy.baseVal.value+")"); //SET CURRENT ROTATION AND PIVOT requestAnimationFrame( animate );//KEEP ANIMATING }
path animation
With path animation we can animate objects along a path with constant speed, or we can animate the path on its own. This is a SVG built in function “getPointAtLength”, it returns exactly what it is called, the x and y coordinate of a certain point on a path. With the point before and the point after a position, we can calculate the current angle of the path segment.
Just add a simple path to the base example. Keep the length of the path in a variable for later access.
var length; path = document.createElementNS(svgNS,"path"); //CREATE NEW PATH path.setAttributeNS(null,"id","Path"); //ID OF THE OBJECT, OBJECT CAN BE MODIFIED VIA CSS path.setAttributeNS(null,"d","M-10,300 Q200,50 400, 300 T800,300 T1200,300 T1600,300 T2000,300 L2000,"+h+" L-10,"+h+" Z"); //PATH path.setAttributeNS(null,"fill","#F05011"); //FILLCOLOR path.setAttributeNS(null,"stroke","black"); //OUTLINE COLOR path.setAttributeNS(null,"stroke-width","3"); //OUTLINE COLOR svg.appendChild(path); //ADD CIRCLE TO THE MAIN SVG ELEMENT length = path.getTotalLength(); //GET TOTAL LENGTH OF THE PATH
We get the position in the render tick, calculate the angle and change the position of the animated object. In this example the object would have 20 second to travel the full path, but the object will be reseted anyway at the end of the screen.
//THIS FUNCTION IS EXECUTED EACH FRAME function animate() { //TIMER TO GET POSITION BASED ON TIME var now = new Date().getTime(), //GET CURRENT TIME dt = now - (time || now); //FRAME TIME elapsed += dt/1000; //ELAPSED TIME SINCE ANIMATION START var t = elapsed / duration; //RUNNING TIME OF THE FULL ANIMATION var duration = 20; var percent = t * 100; time = now; // angle calculations var p0 = path.getPointAtLength( length * percent/100-1 ) //GET POINT BEFORE var p1 = path.getPointAtLength( length * percent/100+1 ) //GET POINT AFTER var angle = Math.atan2(p1.y-p0.y,p1.x-p0.x)*180 / Math.PI; //GET ANGLE //FIND POSITION var pos = path.getPointAtLength( length * percent/100 ); //GET POSITION A CURRENT PERCENTAGE square.x.baseVal.value = pos.x -10; //OFFSET X TO CENTER THE CUBE square.y.baseVal.value = pos.y-10; //OFFSET Y TO CENTER THE CUBE square.setAttribute("transform", "rotate(" + angle + " "+ pos.x+" "+ pos.y+")"); //SET CURRENT ROTATION AND POSITION if(pos.x > window.innerWidth) //RESET POSITION IF END OF SCREEN IS REACHED { elapsed = 0; }; if( percent > 100 || percent < 0 ) //RESET POSITION IF END OF PATH IS REACHED { elapsed = 0; }; requestAnimationFrame( animate );//KEEP ANIMATING }
conclusion
Moving objects with a custom pivot point are really necessary to animate something properly. The path animation is really handy and fast. There is definitely no better way to animate something along a path in html5.
With these three simple animation methods, there are plenty of thing possible to create.