The requestAnimationFrame API

requestAnimationFrame() – 60 frames/second animation (best practice)!

 

INTRODUCTION: ADVANTAGES OVER PREVIOUS METHODS

The new requestAnimationFrame(animationLoop) is very similar to setTimeout:

    • It targets 60 frames/s: requestAnimationFrame asks the browser to schedule a call to the animationLoop function passed as parameter in 1/60th of a second (equivalent to 16.6ms). Keep in mind that most monitors cannot display more than 60 frames per second (FPS). Note that whether humans can tell the difference among high frame rates depends on the application, most games are acceptable above 30 FPS, and virtual reality might require 75 FPS to achieve a natural feel. Some gaming monitors go up to 144 FPS (pro players in e-sport train themselves at Counter Strike with a 150 frames/s rate).
    • It calls the function only ONCE, so if you want a continuous animation, like with setTimeout, you need to call again requestAnimationFrame at the end of the animationLoop function.

It has, however, several advantages over setInterval and setTimeout:

    • The scheduling is much more accurate: if the code inside the function can be executed in less than 16.6ms, then the average error between the scheduled time and the real time will be much smaller than with the old functions.
    • High resolution timer: even if this difference is small, the function that is called after 16.6ms has an extra parameter that is a high resolution time, very useful for writing games that do time-based animation. Time-based animation will be studied in detail in the HTML5 Part 2 course at W3Cx. It is a technique that comprises measuring the amount of time elapsed between two frames, then computing the distance in pixels to move objects on screen so that the visible speed for a human eye remains constant, even if the frame rate is not.
    • Multiple animations are merged: browsers can bundle animations happening at the same time into a single paint redraw (thus happening faster/with less CPU cycles), solving the problems that can occur with simultaneous setInterval calls.
    • CPU/GPU optimization, battery saved on mobiles:  if the JavaScript execution is occurring in a tab/window which is not visible, it doesn’t have to be drawn. However the animation loop is still executed (objects will be moved, not drawn). This is the same when a mobile phone or tablet screen is black or if the application is put in background.

TYPICAL USE

You will note that  requestAnimationFrame(function) is used like setTimeout(function, delay). A call to requestAnimationFrame just asks the browser to call the function passed as a parameter ONCE, and the target delay is fixed, and corresponds to a 60 frames/s frame rate (16.6ms). Notice that an id is used for stopping an animation with cancelAnimationFrame(id).

EXAMPLE: ANIMATE THE MONSTER WITH REQUESTANIMATIONFRAME

Online example at JsBin

 

IS THE 16.6MS DELAY REALLY ACCURATE? CAN WE TRUST IT?

This target may be hard to reach if:

    • The animation loop content is too complex,
    • The target device that runs the animation is a low end phone or an old computer,
    • The scheduler may be a bit late or a bit in advance (even if this kind of error is much smaller withrequestAnimationFrame than with setInterval or setTimeout).

Many HTML5 games perform what we call a “time-based animation”. For this, we need an accurate timer that will tell us the elapsed time between each animation frame.

Depending on this time, we can compute the distances that must be achieved by each object on the screen in order to move at a constant speed (for a human eye), independently of the CPU or GPU of the computer or mobile device that is running the game.

The timeStamp parameter of the animationLoop function (line 1 in the above code) is useful for exactly that: it gives a high resolution time. By measuring deltas between two consecutive calls of the animationLoop, we will know exactly, with a sub-millisecond accuracy, the elapsed time between two frames.

Using time-based animation, and more generally, using the canvas element for writing HTML5 games, will be a chapter of the HTML5 Part-2 course to come.

CURRENT SUPPORT

Current support (as of June 2016) is really good and all modern browsers support this API.

requestAnimationFrame support

 

 

Advertisements

Using the setTimeout() JavaScript function

Using setTimeout() instead of setInterval()

INTRODUCTION

One thing you should always remember about using setInterval: if we set number of milliseconds at – let’s say 20ms – it will call our game loop function EACH 20ms, even if the previous one is not yet finished. This may lead to many problems (incomplete rendering, etc.).

That’s where we can use another function:

    • Syntax: setTimeout(function, ms);

This function works like setInterval(…) with one difference: it calls your function ONCE and AFTER a given amount of time.

EXAMPLE OF THE MONSTER ANIMATED IN A CANVAS WITH SETTIMEOUT

Example of use: http://jsbin.com/timebu/3/edit (open the JavaScript, console and output tabs).

PROBLEMS WITH SETINTERVAL() AND SETTIMEOUT()

setTimeout does not “wait”  during the timeout period. It lets the rest of the JavaScript code run. It schedules a new call to the function passed as first parameter with a timer running in the background. This might cause it to take slightly longer than the expected timeout period to start executing.

This problem also occurs with setInterval, the timing is not “very” reliable. If you plan to run a function every 20ms, and if you measure precisely the real timing, sometimes you will discover big differences between what is scheduled and what is performed. This is because these methods were designed a long time ago, when high precision timers and 60 frames per second animation were not an option.

Here comes the requestAnimationFrame API, a very good companion to the canvas API!

Performing animation using the JavaScript setInterval(…) function

The setInterval(…) function is still popular on the Web, and even though this is not the recommended way to do 60 frames/second canvas animation, it is worth understanding how it works.

BASIC EXAMPLE THAT SHOWS HOW TO ANIMATE A DIV USING THE DOM API (THIS IS HOW PRE-HTML5 GAMES WERE WRITTEN)

Please try it online: http://jsbin.com/huluhe/2/edit (open the html, JavaScript and output tabs)

Animate the monster in a canvas, using setInterval

Online example: http://jsbin.com/lomopo/1/edit

PROBLEMS WITH SETINTERVAL

Running several animations simultaneously

The setInterval function may become hard to debug, particularly if you run several animations simultaneously. For example, if you have two intervals, one running every 100 milliseconds, the other every second, and if you want to debug the second one, the first one will constantly be run at regular intervals, making step by step debugging really difficult.

A single animation may be interrupted by itself to become two simultaneous animations

setInterval will execute the function passed as first parameter every n milliseconds regardless of when the function was last called or how long the function takes to execute. If the function takes longer than the interval, then setInterval might queue too many function executions back to back when the interval is too short, leading to unpredictable results.

Canvas context: colors

Canvas context: colors

COLORS AND TRANSPARENCY

You can use the same syntax for colors that is supported by CSS3. The next lines show possible values/syntaxes.

  1. ctx.strokeStyle = ‘red’;
  2. ctx.fillStyle = “#00ff00”;
  3. ctx.strokeStyle = “rgb(0, 0, 255)”;
  4. ctx.fillStyle = “rgba(0, 0, 255, 0.5)”;

Note that:

    • All values are strings,
    • Line 4 defines a “transparent color”, the “a” of “rgba” means “alpha channel”. Its value is between 0 and 1, where 0 means “completely transparent” and 1 means “opaque”.

Here is an example that shows how to draw different filled rectangles in blue, with different levels of transparency.

Try it online: http://jsbin.com/duwaxa/3/edit

transparent rgba color example

Linear gradients

It is possible to define the stroke or the fill style as a “gradient”, a set of interpolated colors, like in this example below (try it online at: http://jsbin.com/ruguxi/2/edit)

linear gradient from blue to white to red

The concept of linear gradient is seen as an “invisible” rectangle in which a set of colors are interpolated along a line.

The gradient becomes visible when we draw shapes on top of the invisible gradient, and when the fillStyleor strokeStyle property has for value this gradient.

Here are the different steps needed:

Step 1: define a linear gradient

Syntax:

  1. ctx.createLinearGradient(x0,y0,x1,y1);

… where the (x0, y0) and (x1, y1) parameters define “the direction of the gradient” (as a vector with a starting and an ending point). This direction is an invisible line along which the colors that compose the gradient will be interpolated.

Let’s see an example:

  1. grdFrenchFlag = ctx.createLinearGradient(0, 0, 300, 0);

This line defines the direction of the gradient: a virtual, invisible line that goes from the top left corner of the canvas (0, 0) to the top right corner of the canvas (300, 0). The interpolated colors will propagate along this line.

If this gradient is going to be reused by different functions, it is good practice to create/initialize it in a function called when the page is loaded and to store it in a global variable.

Step 2: add a number of “color stops” to this gradient

We will add a set of “colors” and “stops” to this gradient. The stops go from 0 (beginning of the virtual line defined just above), to 1 (end of the virtual line). A color associated with a value of 0.5 will be right in the middle of the virtual line.

Here is an example that corresponds to an interpolated version of the French flag, going from blue to white, then to red, with proportional intervals. We define three colors, blue at position 0, white at position 0.5 and red at position 1:

  1. grdFrenchFlag.addColorStop(0, “blue”);
  2. grdFrenchFlag.addColorStop(0.5, “white”);
  3. grdFrenchFlag.addColorStop(1, “red”);

Step 3: draw some shapes

First, let’s set the fillStyle or strokeStyle of the context with this gradient, then let’s draw some shapes “on top of the gradient”.

In our example, the gradient corresponds to an invisible rectangle that fills the canvas. If we draw a rectangle of the canvas size, it should be filled with the entire gradient:

  1. ctx.fillStyle = grdFrenchFlag;
  2. ctx.fillRect(0, 0, 300, 200);

The result is shown below: a big rectangle that fills the whole canvas, with colors going from blue (left) to white (middle) to red (right).

linear gradient from blue to white to red, left to right

CHANGING THE DIRECTION OF THE GRADIENT

If you modify the source code that defines the direction of the gradient as follows…

  1. grdFrenchFlag = ctx.createLinearGradient(0, 0, 300, 200);

… then you will define a gradient that goes from the top left corner of the canvas to the bottom right of the canvas. Let’s see what it does (online version here: http://jsbin.com/ruguxi/4/edit):

diagonal gradient from top left to bottom right

DRAWING SHAPES THAT DO NOT COVER THE WHOLE GRADIENT

Instead of drawing a filled rectangle that covers the whole surface of the canvas, let’s draw several smaller rectangles.

Online example: http://jsbin.com/baluxa/3/edit

smaller rectangle on top of a gradient

Note that the canvas has its default background color where we did not draw anything. And where we have drawn rectangles, we can see “through” and the colors from the gradient are visible.

Here is the code that draws the checkboard:

  1. ctx.fillStyle = grdFrenchFlag;
  2. ctx.fillRect(0, 0, 50, 50);
  3. ctx.fillRect(100, 0, 50, 50);
  4. ctx.fillRect(200, 0, 50, 50);
  5. ctx.fillRect(50, 50, 50, 50);
  6. ctx.fillRect(150, 50, 50, 50);
  7. ctx.fillRect(250, 50, 50, 50);
  8. ctx.fillRect(0, 100, 50, 50);
  9. ctx.fillRect(100, 100, 50, 50);
  10. ctx.fillRect(200, 100, 50, 50);
  11. ctx.fillRect(50, 150, 50, 50);
  12. ctx.fillRect(150, 150, 50, 50);
  13. ctx.fillRect(250, 150, 50, 50);

This code is rather ugly isn’t it? It would have been better  to use a loop…

Here is function that draws a chessboard (online example at JsBin):

  1. // n = number of cells per row/column
  2. function drawCheckboard(n) {
  3.     ctx.fillStyle = grdFrenchFlag;
  4.     var l = canvas.width;
  5.     var h = canvas.height;
  6.  
  7.     var cellWidth = l / n;
  8.     var cellHeight = h / n;
  9.     for(i = 0; i < n; i++) {
  10.        for(j = i % 2; j < n; j++) {
  11.           ctx.fillRect(cellWidth*i, cellHeight*j, cellWidth, cellHeight);
  12.        }
  13.     }
  14. }

The two loops (lines 11-15) draw only one cell out of two (see the j = i % 2 at line 12). i is the column number and if the column is odd or even, either we draw or we do not draw a rectangle.

This code is much more complex than the previous one, taking 16 lines instead of 13, but is much more powerful. Try to call the function with a value of 10, 20, or 2…

DRAWING OUTLINED SHAPES WITH GRADIENTS

Just as we used fillStyle and fillRect for drawing rectangles filled with a gradient, we can also use strokeStyle and strokeRect in order to draw wireframed rectangles. In the next example, which is just a variation of the previous one, we have used the lineWidth property to set the outline of the rectangles at 5 pixels: try this example at JsBin.

stroke rectangles with gradient

Extract from source code:

  1. function drawCheckboard(n) {
  2.     ctx.strokeStyle = grdFrenchFlag;
  3.     ctx.lineWidth=10;
  4.     …
  5.     for(i = 0; i < n; i++) {
  6.         for(j = i % 2; j < n; j++) {
  7.             ctx.strokeRect(cellWidth*i, cellHeight*j, cellWidth, cellHeight);
  8.         }
  9.     }
  10. }

WHAT HAPPENS IF WE DEFINE A GRADIENT SMALLER THAN THE CANVAS?

Let’s go back to the very first example on this page – the one with the blue-white-red interpolated French flag. This time we will define a smaller gradient. Instead of going from (0, 0) to (300, 0), it will go from (100, 0) to (200, 0), while the canvas remains the same (width=300, height=200).

  1. grdFrenchFlag = ctx.createLinearGradient(100, 0, 200, 0);

Like in the first example we will draw a filled rectangle that is the same size as the canvas. Here is the online version: http://jsbin.com/ruvuta/1/edit, and here is a screenshot of the result:

gradient smaller thant the drawn shape

We notice that “before” the gradient starts, the first color of the gradient is repeated without any interpolation (columns 0-100 are all blue), then we “see through” and the gradient is drawn (columns 100-200), then the last color of the gradient is repeated without any interpolation (columns 200-300 are red).

WHAT HAPPENS IF WE DEFINE A GRADIENT BIGGER THAN THE CANVAS?

Nothing special; we will “see through the drawn shapes”, and the parts of the gradient that are located in the canvas area will be shown. You can try this example that defines a gradient twice the size of the canvas:

  1. grdFrenchFlag = ctx.createLinearGradient(0, 0, 600, 400);

And if we draw the same rectangle with the canvas size, here is the result:

gradient bigger than the canvas size

The red color is beyond the bottom right corner…. we see only the top left quarter of the gradient.

DRAW SHAPES THAT SHARE THE SAME GRADIENT AS A WHOLE

This time, we would like to draw the chessboard with the gradient in each cell. How can we do this with one single gradient?

We can’t! At least we can’t without recreating it for each cell!

It suffices to create a new gradient before drawing each filled rectangle, and set it with the starting and ending point of its direction/virtual line accordingly to the rectangle coordinates. Here is an online example and the resulting display:

chessboard with individual gradient for each cell

Extract from source code:

  1. function setGradient(x, y, width, height) {
  2.     grdFrenchFlag = ctx.createLinearGradient(x, y, width, height);
  3.     grdFrenchFlag.addColorStop(0, “blue”);
  4.     grdFrenchFlag.addColorStop(0.5, “white”);
  5.     grdFrenchFlag.addColorStop(1, “red”);
  6.     // set the new gradient to the current fillStyle
  7.     ctx.fillStyle = grdFrenchFlag;
  8. }
  9.  
  10. // n = number of cells per row/column
  11. function drawCheckboard(n) {
  12.    var l = canvas.width;
  13.    var h = canvas.height;
  14.  
  15.    var cellWidth = l / n;
  16.    var cellHeight = h / n;
  17.    for(i = 0; i < n; i+=2) {
  18.      for(j = 0; j < n; j++) {
  19.         var x = cellWidth*(i+j%2);
  20.         var y = cellHeight*j;
  21.         setGradient(x, y, x+cellWidth, y+cellHeight);
  22.         ctx.fillRect(x, y, cellWidth, cellHeight);
  23.      }
  24.    }
  25. }

We wrote a function setGradient(startX, startY, endX, endY) that creates a gradient and set the fillStyle context property so that any filled shape drawn will have this gradient.

In the drawCheckBoard(…) function we call it just before drawing rectangles. In this way, each rectangle is drawn using its own gradient.

THIS FLAG DOES NOT REALLY LOOK LIKE THE FRENCH FLAG, DOES IT?

Indeed the French flag in these images is more accurate:

french flag with linear gradient

french flags with linear gradient

We slightly modified the examples of this chapter so that the flag looks more like the French flag. Look at the modified versions below, and try to find out what has changed in the gradient definitions:

Canvas context: radial gradients

Radial gradients

BASIC PRINCIPLE / SYNTAX: DEFINE TWO CIRCLES AT GRADIENT CREATION

Radial gradients are for creating gradients that propagate/interpolate colors along circles instead of propagating/interpolating along a virtual line, like linear gradients.

Here is an example of a radial gradient that interpolates the color of the rainbow. Online version: http://jsbin.com/mijoni/2/edit

radial gradient example: circles with the rainbow colors

The gradient is defined as follows:

  1. var grd = context.createRadialGradient(150, 100, 30, 150, 100, 100);
  2. grd.addColorStop(0, “red”);
  3. grd.addColorStop(0.17, “orange”);
  4. grd.addColorStop(0.33, “yellow”);
  5. grd.addColorStop(0.5, “green”);
  6. grd.addColorStop(0.666, “blue”);
  7. grd.addColorStop(1, “violet”);
  8. context.fillStyle = grd;

The method from the context object createRadialGradient(cx1, cy1, radius1, cx2, cy2, radius2) takes as the first three parameters the “starting” circle of the gradient, and as the three last parameters, the “ending circle”.

In the above example, the gradients starts at a circle located at (150, 100), with a radius of 30, and propagates to a circle with the same center as the first (150, 100), but with a bigger radius of 100, as shown below:

//d37djvu3ytnwxt.cloudfront.net/assets/courseware/v1/b83edcaf723d02f063cc50082fae441e/asset-v1:W3Cx+HTML5.1x+3T2016+type@asset+block/radialgradient2.jpg

We added color stops using a method similar to that used for linear gradients.

WHAT HAPPENS IF THE CIRCLES ARE NOT LOCATED AT THE SAME PLACE?

You obtain some nice effects; here we set the second circle’s center 60 pixels to the right of the first circle’s center (cx = 210 instead of 150). Online example: http://jsbin.com/fufelef/1/edit?html

  1. grd = ctx.createRadialGradient(150, 100, 30, 210, 100, 100);

And here is the result:

radial gradient with circles non aligned

WHAT HAPPENS IF THE GRADIENT IS SMALLER OR LARGER THAN THE SHAPES WE DRAW?

A gradient is an invisible shape on the screen: the radial gradient is made of two circles: an inner and an outer circle. Between these two circles, colors are interpolated.

We call the “first color” the color defined for the inner circle, the “last color” the last color of the gradient, that corresponds to the outer circle:

    • The color inside the first circle will be the first color.  In our example above, the first color is red: and the small circle of the gradient in our example is filled in red!
    • The color outside the outer circle will be the last color of the gradient – which is not interpolated. The last color in our example is purple, and it fills the rest of the filled rectangle area “after” the external circle of the gradient.
    • The colors between the two circles will be interpolated.

Painting with patterns

PRINCIPLE

The principle of “pattern” drawing is based on repeating an image (if the image is smaller than the surface of the shape you are going to draw) for filling the surface of objects to be drawn (either filled or stroked).

To illustrate this principle, in the next examples, we are going to draw rectangles using this pattern:

an example of repeateable pattern

There are a few steps we have to take before doing this:

    1. Create a JavaScript image object
      1. var imageObj = new Image();
    2. Define a callback function that will be called once the image has been fully loaded in memory; we cannot draw before the image has been loaded.
      1. imageObj.onload = function(){
      2. }
    3. Set the source of this image to the URL of the pattern (in our example with url of the pattern),
    4. As soon as step 3 is executed, an HTTP request is sent in background by the browser, and when the image is loaded in memory, the callback defined at step 2 is called. We create a pattern object inside, from the loaded image:
      1. // callback called asynchronously, after the src attribute of imageObj is set
      2. imageObj.onload = function(){ 
      3.     // We enter here when the image is loaded, we create a pattern object.
      4.     // It is good practice to set this as a global variable, easier to share
      5.     pattern1 = ctx.createPattern(imageObj, “repeat”);
      6. };
    5. Inside the callback function (or inside a function called from inside the callback) we can draw.
      1. // callback called asynchronously, after the src attribute of imageObj is set
      2. imageObj.onload = function(){
      3.     pattern1 = ctx.createPattern(imageObj, “repeat”);
      4.     // Draw a textured rectangle
      5.     ctx.fillStyle = pattern1;
      6.     ctx.fillRect(10, 10, 500, 800);
      7. };

EXAMPLE 1: DRAW TWO RECTANGLES WITH A PATTERN (ONE FILLED, ONE STROKED)

Online example: http://jsbin.com/qezojo/1/edit

Here we have two rectangles drawn using a pattern (an image that can be repeated along the X and Y axis). The first is a filled rectangle while the second is “stroked” with a lineWidth of 10 pixels.

example of painting with patterns

HTML source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <body onload=init();>
  4.    <canvas id=“myCanvas” width=“500” height=“400”>
  5.        Your browser does not support the canvas tag. </canvas>
  6.    </body>
  7. </html>

JavaScript source code:

  1. var canvas, ctx, pattern1;
  2.  
  3. function init() {
  4.    canvas = document.querySelector(‘#myCanvas’);
  5.    ctx = canvas.getContext(‘2d’);
  6.    // We need 1) to create an empty image object, 2) to set a callback function
  7.    // that will be called when the image is fully loaded, 3) to create a
  8.    // pattern object, 4) to set the fillStyle or the strokeStyle property of
  9.    // the context with this pattern, 5) to draw something
  10.    // WE CANNOT DRAW UNTIL THE IMAGE IS FULLY LOADED -> draw from inside the
  11.    // onload callback only !
  12.    // 1 – Allocate an image
  13.    var imageObj = new Image();
  14.  
  15.    // 2 – callback called asynchronously, after the src attribute of imageObj
  16.    // is set
  17.    imageObj.onload = function(){
  18.       // We enter here only when the image has been loaded by the browser
  19.       // 4 – Pattern creation using the image object
  20.       // Instead of “repeat”, try different values : repeat-x, repeat-y,
  21.       // or no-repeat, You may draw larger shapes in order to see
  22.       // different results
  23.       // It is good practice to leave this as a global variable if it
  24.       // will be reused by other functions
  25.       pattern1 = ctx.createPattern(imageObj, “repeat”);
  26.       // 5 – Draw things. Here a textured rectangle
  27.       ctx.fillStyle = pattern1;
  28.       ctx.fillRect(10, 10, 200, 200);
  29.       // … And a wireframe one
  30.       ctx.lineWidth=20;
  31.       ctx.strokeStyle=pattern1;
  32.       ctx.strokeRect(230, 20, 150, 100);
  33.   };
  34.   // 3 – Send the request to load the image
  35.   // Setting the src attribute will tell the browser to send an asynchronous
  36.   // request.
  37.   // When the browser gets an answer, the callback above will be called
  38.   imageObj.src = http://www.dreamstime.com/colourful-flowers-repeatable-pattern-thumb18692760.jpg&#8221;;
  39. }

EXAMPLE 2: THE REPEATABILITY OF A PATTERN

To “better” see the repeatability of the pattern, here is the same example with a 1000×1000 pixel wide canvas.

Online version here: http://jsbin.com/befiti/3/edit, and here is the result:

same example with bigger canvas and bigger rectangles

You can change the way the pattern is repeated by modifying the second parameter of this method:

  1. pattern1 = ctx.createPattern(imageObj, repeat);

Multiple image loader (for advanced users, or just take it as it is: a utility function you can reuse)

Below are 4 rectangles drawn with 4 different patterns.

4 rectangles drawn with different patterns

DRAW WITH MULTIPLE PATTERNS? WE NEED TO LOAD ALL OF THEM BEFORE DRAWING!

We said earlier that we cannot draw before the image used by a pattern is loaded. This can become rapidly complicated if we need to draw using multiple patterns. We need a way to load all images and then, only when all images have been loaded, start drawing.

JavaScript is an asynchronous language. When you set the src attribute of an image, then an asynchronous request is sent by the browser, and then after a while, the onload callback is called… The difficult part to understand for those who are not familiar with JavaScript is that these requests are done in parallel and we do not know when, and in what order, the images will be loaded.

SOLUTION: A MULTIPLE IMAGE LOADER THAT COUNTS THE LOADED IMAGES AND CALLS A FUNCTION YOU PASS WHEN DONE!

The trick is to have an array of URLs that will be used by our multiple image loader, then in the onload callback, this will be called once per image loaded, so we can count the number of images effectively loaded.

When all images have been loaded, we call a callback function that has been passed to our loader.

A complete example code that produces the result shown at the beginning of this page is at: http://jsbin.com/fufiyu/6/edit

Define the list of images to be loaded

  1. // List of images to load, we used a JavaScript object instead of
  2. // an array, so that named indexes (aka properties)
  3. // can be used -> easier to manipulate
  4. var imagesToLoad = {
  5.      flowers:
  6.         ‘http://cdn2.digitalartsonline.co.uk/images/features/1675/intro.jpg&#8217;,
  7.      lion: http://farm3.static.flickr.com/2319/2486384418_8c031fec76_o.jpg&#8217;,
  8.      blackAndWhiteLys: http://pshero.com/assets/tutorials/0062/final.jpg&#8217;,
  9.      tiledFloor:
  10.       ‘http://4.bp.blogspot.com/_Rqs7w7m37B4/TETj5rD_QmI/AAAAAAAAADk/qRiwoTO-zKk/s1600/symmetry:assymetry+repeatable+pattern.jpg&#8217;
  11. };

Notice that instead of using a traditional array, we defined this list as a JavaScript object, with properties whose names will be easier to manipulate (flowers, lion, tiledFloor, etc.).

The image loader function

  1. function loadImages(imagesToBeLoaded, drawCallback) {
  2.      var imagesLoaded = {};
  3.      var loadedImages = 0;
  4.      var numberOfImagesToLoad = 0;
  5.      // get num of images to load
  6.      for(var name in imagesToBeLoaded) {
  7.          numberOfImagesToLoad++;
  8.      }
  9.      for(var name in imagesToBeLoaded) {
  10.          imagesLoaded[name] = new Image();
  11.          imagesLoaded[name].onload = function() {
  12.              if(++loadedImages >= numberOfImagesToLoad) {
  13.                  drawCallback(imagesLoaded);
  14.              } // if
  15.          }; // function
  16.          imagesLoaded[name].src = imagesToBeLoaded[name];
  17.       } // for
  18. } // function

This function takes as a parameter the list of images to be loaded, and a drawCallback function that will be called only once all images have been loaded. This callback takes as a parameter a new object that is the list of images that have been loaded (see line 16).

We first count the number of images to load (lines 7-9), then for each image to be loaded we create a new JavaScript image object (line 12) and set its src attribute (line 19) – this will start to load the image.

When an image comes in, the onload callback is called (line 14) and inside, we increment the number of images loaded (line 15) and test if this number is >=  the total number of images that should be loaded. If this is the case, the callback function is called (line 16).

Example of use of this loader

  1. loadImages(imagesToLoad, function(imagesLoaded) {
  2.     patternFlowers = ctx.createPattern(imagesLoaded.flowers, ‘repeat’);
  3.     patternLion    = ctx.createPattern(imagesLoaded.lion, ‘repeat’);
  4.     patternBW = ctx.createPattern(imagesLoaded.blackAndWhiteLys, ‘repeat’);
  5.     patternFloor   = ctx.createPattern(imagesLoaded.tiledFloor, ‘repeat’);
  6.     drawRectanglesWithPatterns();
  7. });
  • Line 1 is the call to the image loader, the first parameter is the list of images to be loaded, while the second parameter is the callback function that will be called once all images have been loaded.
  • Lines 2-5: in this callback we create patterns from the loaded images (note the use of the property names imagesLoaded.flowers, etc. that makes the code easier to read).
  • Line 7: then we call a function that will draw the rectangles.

Here is the function:

  1. function drawRectanglesWithPatterns() {
  2.     ctx.fillStyle=patternFloor;
  3.     ctx.fillRect(0,0,200,200);
  4.     ctx.fillStyle=patternLion;
  5.     ctx.fillRect(200,0,200,200);
  6.     ctx.fillStyle=patternFlowers;
  7.     ctx.fillRect(0,200,200,200);
  8.  
  9.     ctx.fillStyle=patternBW;
  10.     ctx.fillRect(200,200,200,200);
  11. }

Drawing shadows

CONTEXT PROPERTIES TO DRAW WITH SHADOWS

a shadowed rectangle

There are 4 properties of the canvas context that are useful for indicating that we want to draw shapes with shadows:

    1. shadowColor: color to use for shadows,
    2. shadowBlur: blur level for shadows,
    3. shadowOffsetX: horizontal distance of the shadow from the shape,
    4. shadowOffsetY: vertical distance of the shadow from the shape

EXAMPLE 1: SIMPLE

Online example: http://jsbin.com/wivubi/3/edit

two shadowed rectangles

HTML source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <body onload = init();>
  4.     <canvas id=“myCanvas” width=“400” height =800>
  5.         Your browser does not support the canvas tag.
  6. </canvas>
  7. </body>
  8. </html>

JavaScript source code:

  1. var canvas, ctx;
  2. function init() {
  3.     canvas = document.getElementById(‘myCanvas’);
  4.     ctx = canvas.getContext(‘2d’);
  5.     // call to a function that will set the 4 context properties for shadows
  6.     setShadow();
  7.     // all drawings that will occur will cast shadows
  8.     // first green filled rectangle
  9.     ctx.fillStyle = “#22FFDD”; 
  10.     ctx.fillRect(20, 20, 200, 100);
  11.     // second stroked rectangle
  12.     ctx.strokeStyle = “purple”; 
  13.     ctx.lineWidth=10;
  14.     ctx.strokeRect(20, 150, 200, 100);
  15. }
  16.  
  17. // We define the 4 properties in a dedicated function, for clarity
  18. function setShadow() {
  19.     ctx.shadowColor = “Grey”;    // color
  20.     ctx.shadowBlur = 20;         // blur level
  21.     ctx.shadowOffsetX = 15;      // horizontal offset
  22.     ctx.shadowOffsetY = 15;      // vertical offset
  23. }
  • Lines 21-27: we set the 4 properties that define shadows in a dedicated function, for better clarity.
  • Line 8: we called this function once before drawing the rectangles.
  • Lines 11-18: we draw a filled and a stroked rectangle. Both rectangles cast shadows.

EXAMPLE 2: UNWANTED SHADOWS!

Let’s take a previous example: the one that draws a filled circle with an outline: http://jsbin.com/gazuba/2/edit

filled circle with outline

Now, let’s add a shadow to it, online example: http://jsbin.com/gokemu/1/edit

Here is an extract from the code:

  1. ctx.beginPath();
  2. // Add to the path a full circle (from 0 to 2PI)
  3. ctx.arc(centerX, centerY, radius, 0, 2*Math.PI, false);
  4. // With path drawing you can change the context
  5. // properties until a call to stroke() or fill() is performed
  6. ctx.fillStyle = “lightBlue”;
  7. // add shadows before drawing the filled circle
  8. addShadows();
  9. // Draws the filled circle in light blue
  10. ctx.fill();
  11. // Prepare for the outline
  12. ctx.lineWidth = 5;
  13. ctx.strokeStyle = “black”;
  14. // draws the path AGAIN (the circle), this
  15. // time in wireframe
  16. ctx.stroke();
  17. // Notice we only once called context.arc()! And drew it twice
  18. // with different styles
  19.  
  20. function addShadows(){
  21.     ctx.shadowColor = “Grey”; // color
  22.     ctx.shadowBlur = 20;      // blur level
  23.     ctx.shadowOffsetX = 15;   // horizontal offset
  24.     ctx.shadowOffsetY = 15;   // vertical offset
  25. }

And here is the result:

unwanted shadow casted by the outline

Ah, indeed, the call to ctx.fill() casts a shadow, but the call to ctx.stroke(), that paints the whole path again, casts a shadow too, and this time the outline produces an unwanted shadow… How can we avoid this effect, while using the same technique for drawing the path?

The trick is to save the context before setting the shadow properties, then draw the filled circle, then restore the context (to its previous state: without shadows), then draw the outlined circle by calling ctx.stroke().

Correct version of the code: http://jsbin.com/kedobi/2/edit

  1. // save the context before setting shadows and drawing the filled circle
  2.  ctx.save();
  3. // With path drawing you can change the context
  4. // properties until a call to stroke() or fill() is performed
  5.  ctx.fillStyle = “lightBlue”;
  6. // add shadows before drawing the filled circle
  7.  addShadows();
  8. // Draws the filled circle in light blue
  9.  ctx.fill();
  10. // restore the context to its previous saved state
  11.  ctx.restore();

And here is the final result:

unwanted shadow disappeared

Styling lines

Several context properties can be used to set the thickness of the shape outlines, the way line end caps are drawn, etc.

They apply to all shapes that are drawn in path mode (lines, curves, arcs) and some also apply to rectangles.

LINE STYLE: CHANGE THE LINE THICKNESS

We have seen this before. This is done by changing the value (in pixels) of the lineWidth property of the context:

  1. ctx.lineWidth = 10; // set the thickness of every shape drawn in stroke/wireframe mode to 10 pixels

Here is a complete example where we draw with a lineWidth of 20 pixels. You can play with the complete interactive example here: http://jsbin.com/dacuco/2/edit

line width changed

Source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>A simple example of lineWidth property use</title>
  5. </head>
  6. <body>
  7.    <canvas id=“myCanvas” width=“500”>
  8.        Your browser does not support the canvas tag.
  9.    </canvas>
  10. var canvas = document.getElementById(‘myCanvas’);
  11. var ctx = canvas.getContext(‘2d’);
  12.  
  13. // first path
  14. ctx.moveTo(20, 20);
  15. ctx.lineTo(100, 100);
  16. ctx.lineTo(100, 0);
  17.  
  18.  
  19. // second part of the path
  20. ctx.moveTo(120, 20);
  21. ctx.lineTo(200, 100);
  22. ctx.lineTo(200, 0);
  23.  
  24. // indicate stroke color + draw first part of the path
  25. ctx.strokeStyle = “#0000FF”;
  26. // Current line thickness is 20 pixels
  27. ctx.lineWidth = 20;
  28. ctx.stroke();        // draws the whole path at once
  29.  
  30. // Draws a rectangle in immediate mode
  31. ctx.strokeRect(230, 10, 100, 100);
  32.  
  33. </body>
  34. </html>

LINE STYLE: CHANGING THE END CAPS FOR A LINE

The lineCap property of the context indicates the way line end caps are rendered. Possible values are butt(default), round, square (from top to bottom in the next illustration). Note that a value of “round” or “square” makes the lines slightly longer than the default value “butt”.

line cap values

Try the next example interactively:  http://jsbin.com/yaliya/2/edit

line cap values table

Source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.    <title>A simple example of lineCap property use</title>
  5. </head>
  6. <body>
  7.  
  8. <canvas id=“myCanvas” width=“500”>
  9.     Your browser does not support the canvas tag.</canvas>
  10. var canvas = document.getElementById(‘myCanvas’);
  11. var ctx = canvas.getContext(‘2d’);
  12.  
  13. // first path
  14. ctx.moveTo(20, 20);
  15. ctx.lineTo(100, 100);
  16. ctx.lineTo(100, 30);
  17.  
  18. // second part of the path
  19. ctx.moveTo(120, 20);
  20. ctx.lineTo(200, 100);
  21. ctx.lineTo(200, 30);
  22.  
  23. // indicate stroke color + draw first part of the path
  24. ctx.strokeStyle = “#0000FF”;
  25. // Current line thickness is 20 pixels
  26. ctx.lineWidth = 20;
  27.  
  28. // Try different values: butt, square, round
  29. ctx.lineCap = “round”;
  30.  
  31. ctx.stroke();
  32. // Draws a rectangle
  33. ctx.strokeRect(230, 10, 100, 100);
  34.  
  35. </body>
  36. </html>

Note that in this example, the rectangle is not affected. It has no line ends visible – all its sides meet. However, the next property we’re going to look at will have an effect on rectangles!

LINE STYLE: SETTING THE TYPE OF CORNER WHEN TWO LINES MEET

The lineJoin property of the context indicates the way corners are rendered, when two lines meet. Possible values are miter (the default) for creating sharp corners, round, or bevel for “cut corners”.

Try the next example interactively: http://jsbin.com/dozida/2/edit

lineJoin value table

Source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>A simple example of lineJoin property use</title>
  5. </head>
  6. <body>
  7.  
  8. <canvas id=“myCanvas” width=“500”>Your browser does not support the canvas tag.</canvas>
  9.  
  10. var canvas = document.getElementById(‘myCanvas’);
  11. var ctx = canvas.getContext(‘2d’);
  12. // first path
  13. ctx.moveTo(20, 20);
  14. ctx.lineTo(100, 100);
  15. ctx.lineTo(100, 30);
  16.  
  17. // second part of the path
  18. ctx.moveTo(120, 20);
  19. ctx.lineTo(200, 100);
  20. ctx.lineTo(200, 30);
  21.  
  22. // indicate stroke color + draw first part of the path
  23. ctx.strokeStyle = “#0000FF”;
  24. // Current line thickness is 20 pixels
  25. ctx.lineWidth = 20;
  26. // Try different values : miter(default), bevel, round
  27. ctx.lineJoin = “round”;
  28. ctx.stroke();
  29. // Draws a rectangle
  30. ctx.strokeRect(230, 10, 100, 100);
  31.  
  32. </body>
  33. </html>

LINE STYLE: SPECIFIC CASE OF LINEJOIN=”MITER”, THE MITERLIMIT PROPERTY, A WAY TO AVOID LOOOOOOONG CORNERS!

The miterLimit property value corresponds to the maximum miter length: the distance between the inner corner and the outer corner where two lines meet. When the angle of a corner between two lines gets smaller, the miter length grows and can become too long.

In order to avoid this situation, we can set the miterLimit property of the context to a threshold value. If the miter length exceeds the miterLimit value, then the corner will be rendered as if the lineJoin property had been set to “bevel” and the corner will be “cut”.

miterLimit property shown with 3 different angles, we see that the part tha goes out of the angle can become very long

You can try an interactive example here: http://jsbin.com/bokusa/3/edit

In the example, try different values for the miterLimit property. You’ll see that the way the corners are rendered changes at values around 2 and 3.

Source code:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>A simple example of miterLimit property use</title>
  5. </head>
  6. <body>
  7.  
  8. <canvas id=“myCanvas” width=“500”>Your browser does not support the canvas tag.</canvas>
  9.  
  10. var canvas = document.getElementById(‘myCanvas’);
  11. var ctx = canvas.getContext(‘2d’);
  12. // first path
  13. ctx.moveTo(20, 20);
  14. ctx.lineTo(100, 100);
  15. ctx.lineTo(100, 30);
  16.  
  17. // second part of the path
  18. ctx.moveTo(120, 20);
  19. ctx.lineTo(200, 100);
  20. ctx.lineTo(200, 30);
  21.  
  22. // indicate stroke color + draw first part of the path
  23. ctx.strokeStyle = “#0000FF”;
  24. // Current line thickness is 20 pixels
  25. ctx.lineWidth = 20;
  26. // Try different values : miter(default), bevel, round
  27. ctx.lineJoin = “miter”;
  28. // try to change this value, try 2, 3, 4, 5 et…
  29. ctx.miterLimit = 1;
  30. ctx.stroke();
  31.  
  32. // Draws a rectangle
  33. ctx.strokeRect(230, 10, 100, 100);
  34. </body>
  35. </html>

 

Bezier curves

INTRODUCTION

Bezier curves are interesting – used mostly for drawing “S” shapes or asymmetric curves.

bezier curve control points

(Picture from : http://www.html5canvastutorials.com/tutorials/html5-canvas-bezier-curves/)

Bezier curves are defined by a context point, like quadratic curves, two control points that define two tangents, and an ending point.

The first part of the curve is tangential to the imaginary line defined by the context point and the first control point. The second part of the curve is tangential to the imaginary line defined by the second control point and the ending point.

The best way to understand how they work is to check out one of these interactive applications:

TYPICAL USAGE OF BEZIER CURVES

Source code:

  1. ctx.moveTo(contextX, contextY);
  2. context.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY);
  3. // Optional : set lineWidth and stroke color
  4. context.lineWidth = 5;
  5. context.strokeStyle = “#0000ff”;
  6. // Draw!
  7. ctx.stroke();

EXAMPLE 1

Try this interactive example: http://jsbin.com/hodawa/2/edit

bezier curve example

  1. context.beginPath();
  2. context.moveTo(100, 20);
  3. context.bezierCurveTo(290, 40, 200, 200, 400, 100);
  4. context.lineWidth = 5;
  5. context.strokeStyle = “#0000ff”;
  6. context.stroke();

EXAMPLE 2 WITH LINES, QUADRATIC, AND BEZIER CURVES IN A PATH

Try this example online: http://jsbin.com/wifowa/1/edit

path with bezier curve, quadratic curve and line in the same, closed path

Extract from source code:

  1. context.beginPath();
  2. context.moveTo(100, 20);
  3. context.lineTo(200, 160);
  4. context.quadraticCurveTo(230, 200, 250, 120);
  5. context.bezierCurveTo(290, 40, 300, 200, 400, 150);
  6. context.lineTo(500, 90);
  7. // TRY COMMENTING THIS LINE OUT
  8. context.closePath();
  9. context.lineWidth = 5;
  10. context.strokeStyle = “#0000ff”;
  11. context.stroke();

In this example we use the closePath() method to draw a line between the last path point and the first path point (line 11), so that the drawing looks like a pair of goggles.

Note how the different parts are linked together and make a “path”:

path composition explained

INTERESTING, INTERACTIVE TOOL FOR GENERATING CODE THAT DRAWS BEZIER CURVES

This tool is available online: http://www.victoriakirst.com/beziertool/, try it!

Screenshot:

bezier curves code generator

Practical example: curved arrows

Practical example: use quadratics for drawing curved arrows

 

We propose a useful function for drawing curved arrows. The code is a modified version of one that has been proposed by several contributors to this thread at StackOverflow.

Source code of the function that draws a curved arrow:

  1. function drawCurvedArrow(startPointX, startPointY,
  2.                          endPointX, endPointY,
  3.                          quadPointX, quadPointY,
  4.                          lineWidth,
  5.                          arrowWidth,
  6.                          color) {
  7.     // BEST PRACTICE: the function changes color and lineWidth -> save context!
  8.     ctx.save();
  9.     ctx.strokeStyle = color;
  10.     ctx.lineWidth = lineWidth;
  11.  
  12.     // angle of the end tangeant, useful for drawing the arrow head
  13.     var arrowAngle = Math.atan2(quadPointX endPointX, quadPointY endPointY) + Math.PI;
  14.  
  15.     // start a new path
  16.     ctx.beginPath();
  17.     // Body of the arrow
  18.     ctx.moveTo(startPointX, startPointY);
  19.     ctx.quadraticCurveTo(quadPointX, quadPointY, endPointX, endPointY);
  20.     // Head of the arrow
  21.     ctx.moveTo(endPointX (arrowWidth * Math.sin(arrowAngle Math.PI / 6)),
  22.                endPointY (arrowWidth * Math.cos(arrowAngle Math.PI / 6)));
  23.  
  24.     ctx.lineTo(endPointX, endPointY);
  25.  
  26.     ctx.lineTo(endPointX (arrowWidth * Math.sin(arrowAngle + Math.PI / 6)),
  27.                endPointY (arrowWidth * Math.cos(arrowAngle + Math.PI / 6)));
  28.  
  29.     ctx.stroke();
  30.     ctx.closePath();
  31.     // BEST PRACTICE -> restore the context as we saved it at the beginning
  32.     // of the function
  33.     ctx.restore();
  34. }

 

Quadratic curves

INTRODUCTION

Quadratic curves are defined by a starting point (called a “context point”), a control point, and an ending point. The curve fits the tangents between the context and control points and between the control and ending points.

 

TYPICAL USE

  1. context.moveTo(contextX, contextY);
  2. context.quadraticCurveTo(controlX, controlY, endX, endY);
  3. // Optional : set lineWidth and stroke color
  4. context.lineWidth = 5;
  5. context.strokeStyle = “#0000ff”;
  6. // Draw!
  7. context.stroke();

EXAMPLE 1: QUADRATIC CURVE

Try this interactive example: http://jsbin.com/vefivu/1/edit

quadratic curve example 1

Source code:

  1. var canvas=document.querySelector(‘#myCanvas1’);
  2. var context=canvas.getContext(‘2d’);
  3.  
  4. context.beginPath();
  5.  
  6. context.moveTo(100, 20);
  7. context.quadraticCurveTo(230, 200, 250, 20);
  8.  
  9. context.lineWidth = 5;
  10. context.strokeStyle = “#0000ff”;
  11. context.stroke();

We set a starting point in line 6: moveTo(…), then set the control and ending points with a call to quadraticCurve(…), at line 7, then set some properties for color, thickness, and finally we call the stroke() method for drawing the curve.

EXAMPLE 2: LINES CONNECTED WITH A QUADRATIC CURVE

Try this interactive example: http://jsbin.com/sibuse/1/edit

a line followed by a curve followed by a curve

Source code:

  1. context.beginPath();
  2. context.moveTo(100, 20);
  3. context.lineTo(200, 80);
  4. context.quadraticCurveTo(230, 200, 250, 20);
  5. context.lineTo(500, 90);
  6. context.lineWidth = 5;
  7. context.strokeStyle = “#0000ff”;
  8. context.stroke();

Drawing rounded rectangles

Drawing rounded rectangles: the arcTo(x1, y1, x2, y2, radius) method

 

There is another method called ctx.arcTo(x1, y1, x2, y2, radius), which is a bit complex to use, but very practical for drawing rounded rectangles.

In fact, the arcTo(…) method draws an arc of a circle depending on some tangents. Let’s look at these pictures for a better understanding (original picture from http://www.dbp-consulting.com/tutorials/canvas/CanvasArcTo.html):

  1. ctx.moveTo(x0, y0);
  2. ctx.arcTo(x1, y1, x2, y2, radius);

EXAMPLE 1: SIMPLE USE

 

  1. context.beginPath();
  2. context.moveTo(0, 20);
  3. context.arcTo(100, 100, 200, 20, 50);
  4. context.lineWidth = 5;
  5. context.strokeStyle = “#0000ff”;
  6. context.stroke();

EXAMPLE 2: DRAW A ROUNDED RECTANGLE

Try this interactive example: http://jsbin.com/kuqalu/1/edit

Source code:

  1. var roundedRect=function(ctx,x,y,width,height,radius,fill,stroke) {
  2.     ctx.beginPath();
  3.    // draw top and top right corner
  4.    ctx.moveTo(x+radius,y);
  5.    ctx.arcTo(x+width,y,x+width,y+radius,radius);
  6.    // draw right side and bottom right corner
  7.    ctx.arcTo(x+width,y+height,x+widthradius,y+height,radius);
  8.    // draw bottom and bottom left corner
  9.    ctx.arcTo(x,y+height,x,y+heightradius,radius);
  10.    // draw left and top left corner
  11.    ctx.arcTo(x,y,x+radius,y,radius);
  12.    if(fill) {
  13.       ctx.fill();
  14.    }
  15.    if(stroke){
  16.       ctx.stroke();
  17.    }
  18. }
  19. var canvas document.getElementById(‘myCanvas’);
  20. var ctx    canvas.getContext(‘2d’);
  21. ctx.strokeStyle ‘rgb(150,0,0)’;
  22. ctx.fillStyle   ‘rgb(0,150,0)’;
  23. ctx.lineWidth   7;
  24. roundedRect(ctx151516012020truetrue);

EXAMPLE 3 COMPARISON BETWEEN LINETO AND ARCTO

This example at JS Bin is the same as the previous one, except that we added at the end of the roundedRect function the same lines of code that draw the rounded rectangle, but using lineTo instead of arcTo. Just take a look!

JS Bin example

lineTo vs arcTo

Example 4: use the unrounded vertices in arcTo

For drawing a rounded square, this code also works:

  1. ctx.moveTo(x+radiusy);
  2. ctx.arcTo(x+widthy,x+widthy+heightradius);
  3. ctx.arcTo(x+widthy+heightxy+heightradius); 
  4. ctx.arcTo(xy+heightxy,radius);
  5. ctx.arcTo(xyx+widthy,radius);

which might be easier than trying to figure out where the arc will end like this:

  1. ctx.moveTo(x+radiusy);
  2. ctx.arcTo(x+widthyx+widthy+radiusradius);
  3. ctx.arcTo(x+widthy+heightx+widthradiusy+height,radius); 
  4. ctx.arcTo(xy+heightxy+heightradiusradius);
  5. ctx.arcTo(xyx+radiusy,radius);

 

Drawing circles and arcs

The ctx.arc(cx, cy, radius, startAngle, endAngle, drawInverse) method is useful for drawing arcs of circles. It takes the center of the circle/arc, its radius, the starting angle of the arc (turning clockwise), the ending angle of the arc, and an optional parameter we will talk about later.

TYPICAL USAGE

Typical usage for drawing an arc/circle/ellipse is:

  1. ctx.arc(centerX, centerY, radius, startAngle, endAngle); // clockwise drawing
  2. ctx.arc(centerX, centerY, radius, startAngle, endAngle, false);

EXAMPLE 1: DRAWING AN ARC WITH RADIUS = 50, STARTING ANGLE = 0, END ANGLE = PI/2

Try this example online: http://jsbin.com/tikite/1/edit

  1. ctx.beginPath();
  2. // we ommited the last parameter
  3. ctx.arc(10075500Math.PI/2);
  4.  
  5. ctx.lineWidth 10;
  6. ctx.stroke();

If we change the last parameter (we omitted it, so it took a value of false by default):

  1. ctx.beginPath();
  2. // we omitted the last parameter
  3. ctx.arc(10075500Math.PI/2, true);
  4.  
  5. ctx.lineWidth 10;
  6. ctx.stroke();

EXAMPLE 2: DRAWING A FULL CIRCLE (FILLED + OUTLINED)

Try this example: http://jsbin.com/gazuba/2/edit

  1. var canvas = document.getElementById(“myCanvas”);
  2. var ctx = canvas.getContext(“2d”);
  3. var centerX = canvas.width / 2;
  4. var centerY = canvas.height / 2;
  5. var radius = 70;
  6. ctx.beginPath();
  7. // Add to the path a full circle (from 0 to 2PI)
  8. ctx.arc(centerX, centerY, radius, 0, 2*Math.PI, false);
  9. // With path drawing you can change the context
  10. // properties until a call to stroke() or fill() is performed
  11. ctx.fillStyle = “lightBlue”;
  12. // Draws the filled circle in light blue
  13. ctx.fill();
  14. // Prepare for the outline
  15. ctx.lineWidth = 5;
  16. ctx.strokeStyle = “black”;
  17. // draws the path (the circle) AGAIN, this
  18. // time in wireframe
  19. ctx.stroke();
  20. // Notice we called ctx.arc() only once ! And drew it twice
  21. // with different styles

 

Drawing lines

  1. var canvas=document.getElementById(‘myCanvas’);
  2. var ctx=canvas.getContext(‘2d’);
  3. // Vertical lines
  4. for (var x = 0.5; x < 500; x += 10) {
  5.     ctx.moveTo(x, 0);
  6.     ctx.lineTo(x, 375);
  7. }
  8. // Horizontal lines
  9. for (var y = 0.5; y < 375; y += 10) {
  10.     ctx.moveTo(0, y);
  11.     ctx.lineTo(500, y);
  12. }
  13.  
  14. // Draw in blue
  15. ctx.strokeStyle = “#0000FF”;
  16.  
  17. // Until the execution of the next line, nothing has been drawn!
  18. ctx.stroke();

In this example, the entire grid is drawn during the execution of the last line of code, with the single call to ctx.stroke().

ANOTHER EXAMPLE MIXING FILLED AND WIREFRAME SHAPES (AND IMMEDIATE AND PATH MODES)

Try this interactive example here: http://jsbin.com/zetupi/4/edit

two consecutive lines and a filled rectangle in immediate mode

  1. var canvas=document.getElementById(‘myCanvas’);
  2. var ctx=canvas.getContext(‘2d’);
  3. // a filled rectangle in immediate mode
  4. ctx.fillStyle=‘#FF0000’;
  5. ctx.fillRect(0,0,80,100);
  6. // two consecutive lines in path mode
  7. ctx.moveTo(0,0);
  8. ctx.lineTo(100, 100);
  9. ctx.lineTo(100,0);
  10. // draws only the two lines in wireframe mode
  11. ctx.strokeStyle = “#0000FF”;
  12. ctx.stroke();

DRAWING A SINGLE PATH MADE WITH DISCONNECTED LINES / PARTS

Try this interactive example here: http://jsbin.com/lefoze/2/edit

One path made of two disconnected lines

  1. var canvas=document.getElementById(‘myCanvas’);
  2. var ctx=canvas.getContext(‘2d’);
  3. // first part of the path
  4. ctx.moveTo(20,20);
  5. ctx.lineTo(100, 100);
  6. ctx.lineTo(100,0);
  7. // second part of the path, moveTo(…) is used to “jump” to another place
  8. ctx.moveTo(120,20);
  9. ctx.lineTo(200, 100);
  10. ctx.lineTo(200,0);
  11. // indicate stroke color + draw the path
  12. ctx.strokeStyle = “#0000FF”;
  13. ctx.stroke();

Drawing lines with different styles

COMMON MISTAKE: DRAWING THE SAME PATH TWICE

Let’s look at the drawing from the last example of the previous section:

DRAWING TWO PATHS WITH DIFFERENT STYLES: THE WRONG AND THE RIGHT WAY!

First, the wrong way!

In this example, we will draw the two parts of the path with different styles: the first part in wireframe mode, and the second part in filled mode.

What we will try first is to call stroke() after the first half of the path, then call fill() after the second half of the path (check this interactive example):

twice drawn path

Here is the code:

  1. var canvas=document.getElementById(‘myCanvas’);
  2. var ctx=canvas.getContext(‘2d’);
  3. // first part of the path
  4. ctx.moveTo(20,20);
  5. ctx.lineTo(100, 100);
  6. ctx.lineTo(100,0);
  7. // indicate stroke color + draw first part of the path
  8. ctx.strokeStyle = “#0000FF”;
  9. ctx.stroke();
  10. // second part of the path
  11. ctx.moveTo(120,20);
  12. ctx.lineTo(200, 100);
  13. ctx.lineTo(200,0);
  14. // indicate stroke color + draw the path
  15. ctx.fillStyle = “pink”;
  16. ctx.fill();

Now, the right way!

Online example: http://jsbin.com/niceqo/2/edit

two different paths

Source code:

  1. var canvas=document.getElementById(‘myCanvas’);
  2. var ctx=canvas.getContext(‘2d’);
  3. // first part of the path
  4. ctx.moveTo(20,20);
  5. ctx.lineTo(100, 100);
  6. ctx.lineTo(100,0);
  7. // indicate stroke color + draw first part of the path
  8. ctx.strokeStyle = “#0000FF”;
  9. ctx.stroke();
  10. // start a new path, empty the current buffer
  11. ctx.beginPath();
  12. // second part of the path
  13. ctx.moveTo(120,20);
  14. ctx.lineTo(200, 100);
  15. ctx.lineTo(200,0);
  16. // indicate stroke color + draw the path
  17. ctx.fillStyle = “pink”;
  18. ctx.fill();

This time, in order to draw the two shapes differently, we defined two separate paths. The way to do this is just to call ctx.beginPath() to start a new path. In this example, the first path has been drawn in wireframe mode, then a new path has been started that is drawn in filled mode.

Drawing lines in immediate mode

Sometimes it might be useful to draw just one line without being in another path.

It’s interesting to see how we can write a single “draw line” function that takes the start and end coordinates, the color, the line width, etc.

Here is the code:

  1. function drawLine(x1, y1, x2, y2, color, width) {
  2.     ctx.save();
  3.     // set color and lineWidth, if these parameters
  4.     // are not defined, do nothing (default values)
  5.     if(color)
  6.         ctx.strokeStyle = color;
  7.     if(width)
  8.         ctx.lineWidth = width;
  9.     // start a new path
  10.     ctx.beginPath();
  11.     ctx.moveTo(x1, y1);
  12.     ctx.lineTo(x2, y2);
  13.     ctx.stroke();
  14.     ctx.restore();
  15. }

Here is an example (see online example):

  1. drawLine(0, 0, 100, 100);
  2. drawLine(0, 50, 150, 200, ‘red’);
  3. drawLine(10, 100, 100, 10, ‘green’, 10);

Practical example: drawing arrows

  1. // Adapted from : http://stackoverflow.com/questions/808826/draw-arrow-on-canvas-tag
  2. function drawArrow(ctx, fromx, fromy, tox, toy, arrowWidth, color){
  3.     //variables to be used when creating the arrow
  4.     var headlen = 10;
  5.     var angle = Math.atan2(toyfromy,toxfromx);
  6.  
  7.     ctx.save();
  8.     ctx.strokeStyle = color;
  9.  
  10.     //starting path of the arrow from the start square to the end square
  11.     //and drawing the stroke
  12.     ctx.beginPath();
  13.     ctx.moveTo(fromx, fromy);
  14.     ctx.lineTo(tox, toy);
  15.     ctx.lineWidth = arrowWidth;
  16.     ctx.stroke();
  17.  
  18.     //starting a new path from the head of the arrow to one of the sides of
  19.     //the point
  20.     ctx.beginPath();
  21.     ctx.moveTo(tox, toy);
  22.     ctx.lineTo(toxheadlen*Math.cos(angleMath.PI/7),
  23.                toyheadlen*Math.sin(angleMath.PI/7));
  24.  
  25.     //path from the side point of the arrow, to the other side point
  26.     ctx.lineTo(toxheadlen*Math.cos(angle+Math.PI/7),
  27.                toyheadlen*Math.sin(angle+Math.PI/7));
  28.  
  29.     //path from the side point back to the tip of the arrow, and then
  30.     //again to the opposite side point
  31.     ctx.lineTo(tox, toy);
  32.     ctx.lineTo(toxheadlen*Math.cos(angleMath.PI/7),
  33.                toyheadlen*Math.sin(angleMath.PI/7));
  34.  
  35.     //draws the paths created above
  36.     ctx.stroke();
  37.     ctx.restore();
  38. }

Online example that uses the above code: http://jsbin.com/qekuqotumu/1/edit

  1. drawArrow(ctx, 10, 10, 100, 100, 10, ‘red’);
  2. drawArrow(ctx, 100, 10, 140, 140, 3, ‘black’);

DRAW NICER ARROWS?

On the Web, you will find many different ways to draw arrows.

This Web site is worth reading: http://www.dbp-consulting.com/tutorials/canvas/CanvasArrow.html. It details how to draw arrows with curved heads and different styles for the head. Note, however, that you will need to modify some parts if you want it to support different line widths, etc.

Closing a path

The ctx.closePath() method indicates that we would like a closed path: draw from the last point to the first.

  1. var canvas=document.getElementById(‘myCanvas’);
  2. var ctx=canvas.getContext(‘2d’);
  3. // Path made of three points (defines two lines)
  4. ctx.moveTo(20,20);
  5. ctx.lineTo(100, 100);
  6. ctx.lineTo(100,0);
  7. // Close the path, try commenting this line
  8. ctx.closePath();
  9. // indicate stroke color + draw first part of the path
  10. ctx.strokeStyle = “blue”;
  11. ctx.stroke();