Basic animations

Since we're using JavaScript to control <canvas> elements, it's also very easy to make (interactive) animations. Doing more complex animations can take a little extra work; we hope to introduce a new article to help with that soon.

Probably the biggest limitation is that once a shape gets drawn it stays that way. If we need to move it we have to redraw it and everything that was drawn before it. It takes a lot of time to redraw complex frames and the performance depends highly on the speed of the computer it's running on.

Basic animation steps

These are the steps you need to take to draw a frame:

  1. Clear the canvas
    Unless the shapes you'll be drawing fill the complete canvas (for instance a backdrop image), you need to clear any shapes that have been drawn previously. The easiest way to do this is using the clearRect() method.
  2. Save the canvas state
    If you're changing any setting (such as styles, transformations, etc) which affect the canvas state and you want to make sure the original state is used each time a frame is drawn, you need to save that original state.
  3. Draw animated shapes
    The step where you do the actual frame rendering.
  4. Restore the canvas state
    If you've saved the state, restore it before drawing a new frame.

Controlling an animation

Shapes are drawn to the canvas by using the canvas methods directly or by calling custom functions. In normal circumstances, we only see these results appear on the canvas when the script finishes executing. For instance, it isn't possible to do an animation from within a for loop.

That means we need a way to execute our drawing functions over a period of time. There are two ways to control an animation like this.

Scheduled updates

First there's the window.setInterval() and window.setTimeout() functions, which can be used to call a specific function over a set period of time.

Note: The window.requestAnimationFrame() method is now the recommended way to schedule animations. We will update this tutorial to cover it in detail soon.

setInterval(function, delay)
Starts repeatedly executing the function specified by function every delay milliseconds.
setTimeout(function, delay)
Executes the function specified by function in delay milliseconds.

If you don't want any user interaction it's best to use the setInterval() function which repeatedly executes the supplied code.

Updating on user interaction

The second method we can use to control an animation is user input. If we wanted to make a game, we could use keyboard or mouse events to control the animation. By setting EventListeners, we catch any user interaction and execute our animation functions.

If you want the user interaction, you could use the minor or the major version of our framework for animations:

var myAnimation = new MiniDaemon(null, animateShape, 500, Infinity);

or

var myAnimation = new Daemon(null, animateShape, 500, Infinity);

In the examples below, however, we'll use the window.setInterval() method to control the animation. At the bottom of this page are some links to examples which use window.setTimeout().

An animated solar system

This example animates a small model of our solar system.

var sun = new Image();
var moon = new Image();
var earth = new Image();
function init(){
  sun.src = 'https://mdn.mozillademos.org/files/1456/Canvas_sun.png';
  moon.src = 'https://mdn.mozillademos.org/files/1443/Canvas_moon.png';
  earth.src = 'https://mdn.mozillademos.org/files/1429/Canvas_earth.png';
  setInterval(draw,100);
}

function draw() {
  var ctx = document.getElementById('canvas').getContext('2d');

  ctx.globalCompositeOperation = 'destination-over';
  ctx.clearRect(0,0,300,300); // clear canvas

  ctx.fillStyle = 'rgba(0,0,0,0.4)';
  ctx.strokeStyle = 'rgba(0,153,255,0.4)';
  ctx.save();
  ctx.translate(150,150);

  // Earth
  var time = new Date();
  ctx.rotate( ((2*Math.PI)/60)*time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds() );
  ctx.translate(105,0);
  ctx.fillRect(0,-12,50,24); // Shadow
  ctx.drawImage(earth,-12,-12);

  // Moon
  ctx.save();
  ctx.rotate( ((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds() );
  ctx.translate(0,28.5);
  ctx.drawImage(moon,-3.5,-3.5);
  ctx.restore();

  ctx.restore();
  
  ctx.beginPath();
  ctx.arc(150,150,105,0,Math.PI*2,false); // Earth orbit
  ctx.stroke();
 
  ctx.drawImage(sun,0,0,300,300);
}

ScreenshotLive sample

An animated clock

This example draws an animated clock, showing your current time.

function init(){
  clock();
  setInterval(clock,1000);
}

function clock(){
  var now = new Date();
  var ctx = document.getElementById('canvas').getContext('2d');
  ctx.save();
  ctx.clearRect(0,0,150,150);
  ctx.translate(75,75);
  ctx.scale(0.4,0.4);
  ctx.rotate(-Math.PI/2);
  ctx.strokeStyle = "black";
  ctx.fillStyle = "white";
  ctx.lineWidth = 8;
  ctx.lineCap = "round";

  // Hour marks
  ctx.save();
  for (var i=0;i<12;i++){
    ctx.beginPath();
    ctx.rotate(Math.PI/6);
    ctx.moveTo(100,0);
    ctx.lineTo(120,0);
    ctx.stroke();
  }
  ctx.restore();

  // Minute marks
  ctx.save();
  ctx.lineWidth = 5;
  for (i=0;i<60;i++){
    if (i%5!=0) {
      ctx.beginPath();
      ctx.moveTo(117,0);
      ctx.lineTo(120,0);
      ctx.stroke();
    }
    ctx.rotate(Math.PI/30);
  }
  ctx.restore();
  
  var sec = now.getSeconds();
  var min = now.getMinutes();
  var hr  = now.getHours();
  hr = hr>=12 ? hr-12 : hr;

  ctx.fillStyle = "black";

  // write Hours
  ctx.save();
  ctx.rotate( hr*(Math.PI/6) + (Math.PI/360)*min + (Math.PI/21600)*sec )
  ctx.lineWidth = 14;
  ctx.beginPath();
  ctx.moveTo(-20,0);
  ctx.lineTo(80,0);
  ctx.stroke();
  ctx.restore();

  // write Minutes
  ctx.save();
  ctx.rotate( (Math.PI/30)*min + (Math.PI/1800)*sec )
  ctx.lineWidth = 10;
  ctx.beginPath();
  ctx.moveTo(-28,0);
  ctx.lineTo(112,0);
  ctx.stroke();
  ctx.restore();
  
  // Write seconds
  ctx.save();
  ctx.rotate(sec * Math.PI/30);
  ctx.strokeStyle = "#D40000";
  ctx.fillStyle = "#D40000";
  ctx.lineWidth = 6;
  ctx.beginPath();
  ctx.moveTo(-30,0);
  ctx.lineTo(83,0);
  ctx.stroke();
  ctx.beginPath();
  ctx.arc(0,0,10,0,Math.PI*2,true);
  ctx.fill();
  ctx.beginPath();
  ctx.arc(95,0,10,0,Math.PI*2,true);
  ctx.stroke();
  ctx.fillStyle = "rgba(0,0,0,0)";
  ctx.arc(0,0,3,0,Math.PI*2,true);
  ctx.fill();
  ctx.restore();

  ctx.beginPath();
  ctx.lineWidth = 14;
  ctx.strokeStyle = '#325FA2';
  ctx.arc(0,0,142,0,Math.PI*2,true);
  ctx.stroke();

  ctx.restore();
}

ScreenshotLive sample

A looping panorama

In this example, a panorama is scrolled left-to-right. We're using an image of Yosemite National Park we took from Wikipedia, but you could use any image that's larger than the canvas.

var img = new Image();

// User Variables - customize these to change the image being scrolled, its
// direction, and the speed.

img.src = 'https://mdn.mozillademos.org/files/4553/Capitan_Meadows,_Yosemite_National_Park.jpg';
var CanvasXSize = 800;
var CanvasYSize = 200;
var speed = 30; //lower is faster
var scale = 1.05;
var y = -4.5; //vertical offset

// Main program

var dx = 0.75;
var imgW;
var imgH;
var x = 0;
var clearX;
var clearY;
var ctx;

img.onload = function() {
    imgW = img.width*scale;
    imgH = img.height*scale;
    if (imgW > CanvasXSize) { x = CanvasXSize-imgW; } // image larger than canvas
    if (imgW > CanvasXSize) { clearX = imgW; } // image larger than canvas
    else { clearX = CanvasXSize; }
    if (imgH > CanvasYSize) { clearY = imgH; } // image larger than canvas
    else { clearY = CanvasYSize; }
    //Get Canvas Element
    ctx = document.getElementById('canvas').getContext('2d');
    //Set Refresh Rate
    return setInterval(draw, speed);
}

function draw() {
    //Clear Canvas
    ctx.clearRect(0,0,clearX,clearY);
    //If image is <= Canvas Size
    if (imgW <= CanvasXSize) {
        //reset, start from beginning
        if (x > (CanvasXSize)) { x = 0; }
        //draw aditional image
        if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-CanvasXSize+1,y,imgW,imgH); }
    }
    //If image is > Canvas Size
    else {
        //reset, start from beginning
        if (x > (CanvasXSize)) { x = CanvasXSize-imgW; }
        //draw aditional image
        if (x > (CanvasXSize-imgW)) { ctx.drawImage(img,x-imgW+1,y,imgW,imgH); }
    }
    //draw image
    ctx.drawImage(img,x,y,imgW,imgH);
    //amount to move
    x += dx;
}

Below is the <canvas> in which the image is scrolled. Note that the width and height specified here must match the values of the CanvasXZSize and CanvasYSize variables in the JavaScript code.

<canvas id="canvas" width="800" height="200"></canvas>

Live sample

Other examples

Gartic
Multi-player drawing game.
Canvascape
A 3D adventure game (first-person shooter).
A basic ray-caster
A good example of how to do animations using keyboard controls.
canvas adventure
Another nice example that uses keyboard controls.
An interactive Blob
Have fun with the blob.
Flying through a starfield
Fly through stars, circles, or squares.
iGrapher
An example that charts stock market data.

See also

Attachments

File Size Date Attached by
Canvas_animation1.png
20326 bytes 2005-11-13 20:47:38 KamielMartinet
Canvas_animation2.png
9993 bytes 2005-11-13 20:48:18 KamielMartinet
Canvas_earth.png
1612 bytes 2006-04-25 11:30:31 Taken
Canvas_moon.png
278 bytes 2006-04-25 11:30:49 Taken
Canvas_sun.png
7015 bytes 2006-04-25 11:29:53 Taken
Capitan Meadows, Yosemite National Park
http://commons.wikimedia.org/wiki/File:Capitan_Meadows,_Yosemite_National_Park.jpg
178305 bytes 2012-12-27 16:49:17 trevorh

Document Tags and Contributors

Contributors to this page: Sheppy, onedir, Jeremie, trevorh, mcdavis
Last updated by: Jeremie,