Animate with SVG


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.