Creating simple HTML Canvas animations with Fabric.js
By: Uday Hiwarale
Tags: Graphics, Animation, Web Development, Canvas, Gaming

In the “Introduction to Canvas” lesson, we learned about the Canvas Web API and how to draw basic shapes. In that lesson, we also saw how to animate those shapes on the canvas and how surreal it can be to achieve the simplest animations. The Canvas API is not at fault, this is how animations work.

Animation, in a nutshell, is a sequence of frames played quickly so that our eyes perceive smooth motion. This is how traditional film projectors work. It flashes each film 24 frames a second on the screen and due to persistence of vision, it looks like a natural motion to us.

A “frame” in a general sense is a state of an object at a particular moment in time. Each frame contains a slightly different state of the object such as its position, shape, or characteristics. If we have sufficient enough frames, we can display it on the screen one after the other after enough to create a smooth animation. So what we basically need to do is to change the state of the object, clear the screen (old state), and paint the current state.

In the previous article, we learned how to setup Fabric.js and render basic shapes on the canvas. We are going to follow the same setup so you should take a look at this article before continuing with this.

In this lesson, we are going to only play with rectangles to keep things simple and to the point. Therefore, let’s draw a basic rectangle on the canvas.

In the above example, we have created a rectangle of size 50x50 pixels and corner radius of 5px . We are drawing the rectangle on the canvas at ( 50,50 ) position with a purple fill color. Let’s see how it looks.

Now that our rectangle is sitting at one position doing nothing, let’s animate it by moving it elsewhere. For that, we need to know how to change its position. For that, we use the get and set methods available with each shape.

var value = object.get( propertyName );

object.set( propertyName, propertyValue );

The get() method returns the current value of a given property such as width , height , top , left , fill , etc. The set() method updates the property value of the object but it does not re-render the object. For that, we need to use the canvas.renderAll() method. The renderAll() method redraws all the shapes on the canvas.

In the above example, we are creating an interval that runs every 500ms (0.5sec) 6 times. In each iteration, we are getting the top and left values of the rect object using the rect.get() method and incrementing these values by 10px on every single run. Then the canvas.renderAll() will re-render the entire scene (canvas) which only contains one shape right now.

You can also update multiple properties at once by passing an object to the set() method which contains property names as keys and values as the new values of those properties.

// get top, left and angle values

const top = rect.get( 'top' );

const left = rect.get( 'left' );

const angle = rect.get( 'angle' ); // update top, left and angle values by 10px

rect.set( {

top: top + 10,

left: left + 10,

angle: angle + 15,

} );

Now let’s get a little serious. The animation we created above is not smooth because we are only rendering 6 frames with each frame having 10px increment in the width and height property. The final top and left value of the rectangle is 110px since we are moving the rectangle by 60px .

Let’s say we want to achieve this animation in 3 seconds. If we target 10 frames a second, we would get 30 frames. However, 10FPS is still not smooth enough. For human eyes to perceive smooth motion, we would need at least 24FPS. But we should ideally target the maximum frame available on the device such as 60FPS (or more) to get even smoother motion.

In the rAF lesson, we talked about the requestAnimationFrame function which executes the callback function when it about to perform the paint operation. In this lesson, we also talked about the progress based animation strategy which we are going to use here so make sure to read it first.

In the above example, we are asking the browser to execute the update() function before the next paint operation. As the requestAnimationFrame function passes the timestamp argument to the callback which is the current time in milliseconds at which the callback was invoked, we can track the progress we have made in the 3s animation. Based on this progress value, we can update the rect properties and call renderAll() to update the canvas.

Though it’s not clearly visible in the above GIF, this animation is as smooth as it can get since we are using the requestAnimationFrame API. If you have multiple objects involved in the animation, you would first update the properties of all the objects and then call the renderAll() method.

However, it seems like we are doing a lot of things here to achieve even the simplest animations. Well, Fabric.js gives us the animate method with every object to achieve the same results but with little code.

object.animate( property, value, {

onChange, from, duration, easing, onComplete

});

The animate() method animates the object from the current value of the property until it reaches the final value . We can set the duration of this animation in milliseconds. If the duration value is missing, the animation will run for 500ms by default.

You can also set the starting property value using the from property unless you want to start the animation from the current value of the object . The easing property is the animation function and they are listed here. By default, the animation function is fabric.util.ease.easeInSine .

The animate() function doesn’t re-render the canvas, that is our responsibility. For each frame, this function calls the onChange callback inside which we can call the renderAll() method to update the canvas. The onComplete callback, if provided, will be called when the animation ends.

Like set() , you can also provide multiple properties of the object that need to be animated. We will run the previous animation but this time using the animate function and with easeInOutElastic animation function.

In the above example, we have passed the canvas.renderAll.bind(canvas) expression as the callback function since it will return the canvas.renderAll function with canvas as its this value which is a requirement.

Unlike set() , you can pass relative property values such as '+=60' we have used above which indicates that the final top value would be 60 plus the current value of this property. You can also use the '-=x' notation.

Well, that is basically it for the animation in Fabric.js. Now you can let your imagination run wild and create beautiful animations from what we have learned.