Html5 Tutorial : Canvas

6. HTML5 Canvas

6.1. Overview Of Graphics In The Browser

  • Static images in a browser are supported since a long time using <img>
  • What about dynamic graphics? There is no native support for that.
  • Current solutions are:

    • Using plugins such as Flash, Silverlight, JavaFX
    • VML (Vector Markup Language) only works in IE
  • HTML5 comes with the <canvas> element which provides a JavaScript API for 2D drawing (from basics shapes to complex paths) in the browser
  • The Canvas element is part of the HTML5 specification but the Canvas 2D API itself has its own specification

6.1.1. Canvas Vs. SVG

  • There is one main alternative to Canvas that you often hear about: SVG (Scalable Vector Graphics)
  • Both Canvas and SVG allow graphic manipulation in the browser but it is two different techniques
  • Depending on the kind of graphics application you want to do, you want to consider the following characteristics:

    Table 1. Canvas VS. SVG

     

    Canvas

    SVG

    Advantages

    • High performance graphics
    • Pixel-level manipulation
    • Constant performance depending on the resolution used
    • Canvas drawing surface can be saved as an image file
    • Vector-based, scalable to any resolution
    • Good support for animations
    • DOM manipulated elements

    Drawbacks

    • No API for animation, You have to redraw every time
    • Pixel-manipulation: impossible for shape you create to respond to events
    • Not scalable
    • Not suited for user interfaces
    • Works with the DOM so with a lot of elements it gets slower
    • Not suited for gaming applications


6.1.2. Accessibility

  • When the canvas is not supported in the browser, the only fallback we have is what we can nest inside the <canvas> element:

    <canvas>
      Text which is displayed if your browser does not support the canvas element...
    </canvas>
  • We can insert text string and images in a canvas but:

    • There is no way to provide alternative text for images used in a canvas
    • Other thing is that a text in a canvas is just a collection of pixels there is no way to copy paste the text displayed or access it in the DOM.
  • Accessibility in canvas element so far just needs more work

6.2. Using A Canvas

  • To use the canvas element in a web page, we would do the following:

    <canvas width="533px" height="300px" id="mycanvas"></canvas>
    [Note]Note

    This is all you have to do in HTML to use a canvas. Now, if you want to access your canvas and draw on it, you have to use JavaScript.

6.3. Context And Coordinates

  • All drawing operations on a canvas happen in in the canvas’s context: its 2D context.
  • Use JavaScript to get the canvas’s context:

    var ctx = document.getElementById("mycanvas").getContext("2d");
  • Now that we obtained the context we will be able to perform drawing methods on it.
  • Before getting into drawing operations and transformations, let us see how coordinates works on a canvas:

    Figure 6. Coordinate system in a canvas

    images/canvas_and_svg-coordinates.png


    [Note]Note

    Coordinates on a canvas are expressed in pixels

6.4. Drawing Shapes

  • Canvas supports only one basic shape: Rectangle

    [Note]Note

    We are going to see how to draw more things than rectangles using paths later.

  • Filled rectangle:

    var ctx=document.getElementById("mycanvas").getContext('2d'); 1
    ctx.fillRect(50,50,100,200); 2

    1

    Since any drawing operations take place on the context object, we need to obtain the context to then work on it.

    2

    fillRect draws a filled rectangle of 100px width and 200px height with the top-left corner located at (50,50). Default color of the rectangle is black.
  • Stroked rectangle:

    var ctx=document.getElementById("mycanvas").getContext('2d');
    ctx.strokeRect(50,50,100,200); 1

    1

    strokeRect draws a stroked rectangle of 100px width and 200px height with the top-left corner located at (50,50). Default color of the rectangle is black.

6.5. Working With Paths

  • To draw shapes that are more complex than rectangles, we need to use paths.
  • Think of a path as a collection of pixels going from a starting point to an ending point.
  • A path can also be composed of subpaths.
  • Four methods are used when dealing with paths:

    • beginPath(): To start a new path
    • closePath(): To close the current path
    • stroke(): To stroke the path
    • fill(): To fill the path

      [Note]Note

      When we will draw on the canvas, the default color is black. We can change that by using CSS colors with the two following context properties: fillStyle and strokeStyle. For instance ctx.fillStyle='rgb(255,0,0)'; would define the filling color as red.

6.5.1. Drawing Straight Lines

  • Consider these two main methods to draw straight lines:

    • moveTo(x,y): Starting point of the line we want to draw. Think of this method as lifting the "pencil" to the specified coordinate.
    • lineTo(x,y): Think of this method as drawing the path from the previous specified point to this one.
  • Here is how you would draw a path using three lines:

    var ctx=document.getElementById("mycanvas").getContext('2d');
    ctx.beginPath();
    ctx.moveTo(0,0);
    ctx.lineTo(50,50);
    ctx.lineTo(100,0);
    ctx.lineTo(150,50);
    ctx.lineTo(200,0);
    ctx.lineTo(250,50);
    ctx.stroke();

    Figure 7. Example of a stroked path

    images/canvas_and_svg-stroked_path.png


6.5.2. Drawing Circles Or Arcs

  • To draw a circle or an arc, we are going to use the arc(x, y, radius, startAngle, endAngle) method:

    • x and y being the coordinates of the center of the circle/arc in pixels
    • radius being the radius of your circle/arc in pixel
    • startAngle being the starting angle of the circle/arc in radians
    • endAngle being the ending angle of the circle/arc in radians
  • An example of how to draw a filled circle would be:

    var ctx=document.getElementById("mycanvas").getContext('2d');
    ctx.beginPath();
    ctx.arc(265,150,75,0,Math.PI*2); 1
    ctx.fill(); 2

    1

    Here for instance, this will represent a full circle.

    2

    Again, we have the choice to either draw a filled circle or a stroked circle.

    Figure 8. Example of a filled circle

    images/canvas_and_svg-filled_circle.png


6.6. Drawing Text

  • We have seen how to draw shapes and lines made of paths.

    • Drawing text is still drawing a path
    • Like other shapes we have just discussed, we can have a filled text or stroked text
  • Let us see how it works:

    var ctx=document.getElementById("mycanvas").getContext('2d');
    ctx.beginPath();
    ctx.textAlign="center"; 1
    ctx.font="italic 50px verdana"; 2
    ctx.fillText("Hello World!", 265, 150); 3

    1

    textAlign is the position of the text regarding to a specified point.

    2

    font has the same syntax as the CSS font rule.

    3

    fillText has the following arguments: a text string to draw then the x coordinate and the y coordinate of the point where we want to draw the text.

    Figure 9. Example of a filled text

    images/canvas_and_svg-filled_text.png


6.7. Drawing Images

  • To draw an image on a canvas, we have three methods available:

    • drawImage(image, dx, dy): draws the specified image on the canvas at coordinates (dx,dy) without resizing the image
    • drawImage(image, dx, dy, dw, dh): draws the specified image on the canvas at coordinates (dx,dy) with the width dw and the height dh
    • drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh): Here we have the ability to select a rectangle region on the image. This selected region has its top-left corner located at (sx,sy), a width sw and a height sh. This selected region of the image will be drawn on the canvas at the top-left corner coordinate (dx,dy) with a width dw and a height dh
  • Let us see an example of that. I want to just select the head of this wonderful leopard (in reality: 346px by 400px):

    Figure 10. Original image

    images/canvas_and_svg-leopard.png


  • To do that, I could write the following code:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset=utf-8 />
        <title>HTML5 Example: Canvas</title>
        <script>
          function drawOnCanvas() {
            var ctx=document.getElementById("mycanvas").getContext('2d');
            var image=new Image();
            image.src="leopard.png";
            image.onload=function() {
              ctx.drawImage(image,140,0,206,183,0,0,206,183);
            }
          }
    
          window.addEventListener('load', drawOnCanvas, true);
        </script>
      </head>
      <body>
        <canvas width="346px" height="400px" id="mycanvas"></canvas>
      </body>
    </html>

    Figure 11. Drawing an image

    images/canvas_and_svg-leopard_head.png


6.8. Working With Pixels

  • It is possible to get the pixels data from a canvas
  • You can imagine the different kind of web application you can create:

    • Image filters
    • Facial recognition application
    • Etc…
  • context.getImageData(sx,sy,sw,sh) gets the image data of a selected region and returns a ImageData object

    • (sx,sy) being the top-left coordinate of the selected region
    • sw and sh being the width and the height of the selected region
  • The ImageData object contains the width, the height and the data properties.
  • The data property is a CanvasPixelArray containing pixels informations

    • Each pixel is composed of four channels: the red, blue, green and the alpha transparency channel

      [r1,g1,b1,a1,r2,g2,b2,...,a2,rN,gN,bN,aN]
      [Warning]Warning

      data.length will not give you the number of pixels since the array has four channels for each pixel! data.length/4 will give you the number of pixels

  • This is, for instance, how we would create a green color filter for our previous leopard:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset=utf-8 />
        <title>HTML5 Example: Canvas</title>
        <script>
          function drawOnCanvas() {
            var canvas=document.getElementById("mycanvas");
            var ctx=canvas.getContext('2d');
            var image=new Image();
            image.src="leopard.png";
            image.onload=function() {
              ctx.drawImage(image,0,0); 1
              var pixels=ctx.getImageData(0,0,canvas.width,canvas.height); 2
              for (var i = 0, n = pixels.data.length; i < n; i += 4){
                pixels.data[i+0] = 0; 3
                pixels.data[i+1] = 255 - pixels.data[i+1]; 4
                pixels.data[i+2] = 0; 5
              }
              ctx.putImageData(pixels,0,0); 6
            }
          }
    
          window.addEventListener('load', drawOnCanvas, true);
        </script>
        <style type="text/css">
          #mycanvas {
            border-style:solid;
          }
        </style>
      </head>
      <body>
        <canvas width="346px" height="400px" id="mycanvas"></canvas>
      </body>
    </html>

    1

    We have to draw the image on the canvas so we can get the image data later

    2

    Here we get the image data from the whole canvas area

    3

    Red channel

    4

    Green channel

    5

    Blue channel

    6

    We have to put the pixels back in the canvas if we want to see the result by using putImageData

    Figure 12. Working with the pixel data

    images/canvas_and_svg-green_leopard.png


6.9. Understanding Transforms

  • Before we dive into tranformations, we are going to discuss two important methods: save() and restore()
  • These two methods are used to save and restore the context’s state

    • The state of the canvas contains the current style and transformations applied
  • The canvas maintain a stack of states:

    • Calling save() pushes the current state on the top of the stack
    • Calling restore() takes out the top state of the stack to use it
    [Note]Note

    Using save() and restore() will save you a lot of headaches and lines of code.

  • There are a lot of transformations you can apply on your canvas: translation, rotation, scaling, etc…

6.9.1. Translation

  • The translate(x,y) method moves the canvas and its origin to a specified point in the canvas. This point will become the new origin (0,0)
  • Below is an example showing that, we will first draw an horizontal line and then draw it again using a translation:

    <script>
      function drawOnCanvas() {
        var canvas=document.getElementById("mycanvas");
        var ctx=canvas.getContext('2d');
        ctx.save(); 1
        ctx.strokeStyle='rgb(51,153,255)';
        ctx.translate(0,100); 2
        drawLines(ctx); 3
        ctx.restore(); 4
        drawLines(ctx); 5
      }
    
      function drawLines(context) {
        context.beginPath();
        context.moveTo(0,0);
        context.lineTo(50,50);
        context.lineTo(100,0);
        context.lineTo(150,50);
        context.lineTo(200,0);
        context.lineTo(250,50);
        context.stroke();
      }
    
      window.addEventListener('load', drawOnCanvas, true);
    </script>

    1

    This saves the current context

    2

    Calling the translate method moves the canvas and its origin to the new specified point

    3 5

    This function draws the same path (The blue stroked path is the one draw with the translation. The black stroked path is the one drawn after calling restore, there is no translation for this one)

    4

    This restores the previous saved state

    Figure 13. Applying a translation

    images/canvas_and_svg-translation.png


6.9.2. Rotation

  • The rotate(angle) method performs a clockwise rotation of the canvas around its origin (0,0) with an angle in radians

    [Note]Note

    To move the center of the rotation, you would perform a translation of the origin before

  • We can easily use the previous example and apply a rotation instead of a translation this time:

    <script>
      function drawOnCanvas() {
        var canvas=document.getElementById("mycanvas");
        var ctx=canvas.getContext('2d');
        ctx.save();
        ctx.strokeStyle='rgb(51,153,255)';
        ctx.rotate(Math.PI/4); 1
        drawLines(ctx);
        ctx.restore();
        drawLines(ctx);
      }
    
      function drawLines(context) {
        context.beginPath();
        context.moveTo(0,0);
        context.lineTo(50,50);
        context.lineTo(100,0);
        context.lineTo(150,50);
        context.lineTo(200,0);
        context.lineTo(250,50);
        context.stroke();
      }
    
      window.addEventListener('load', drawOnCanvas, true);
    </script>

    1

    This will perform a clockwise rotation of 45 degrees

    Figure 14. Applying a rotation

    images/canvas_and_svg-rotation.png


6.9.3. Scaling

  • The scale(x,y) method modify the units of our canvas

    • For instance calling scale(1,2) means that 1 pixel on the y axis would correspond to 2 pixel on the canvas
  • Let us modify the scaling and then draw a circle and see how oval the circle will be:

    var ctx=document.getElementById("mycanvas").getContext('2d');
    ctx.save();
    ctx.scale(1,0.5); 1
    ctx.beginPath();
    ctx.arc(200,100,50,0,Math.PI*2);
    ctx.stroke();

    1

    This means that 1px on the x axis will still be 1px but 1px on the y axis will be represented as 0.5px on the canvas.

    Figure 15. Applying scaling

    images/canvas_and_svg-scaling.png


6.10. Browser Support

  • Firefox 3.5+, Safari 4.0+, Chrome 6.0+ and Opera 10.5+ support Canvas

    [Note]Note

    Canvas is also supported in Firefox 3.0 except for drawing text.

    [Note]Note

    Canvas can be supported in Internet Explorer using a third-party JavaScript library called explorercanvas. You can then use <!--[if IE]>...<![endif]--> to load the library when needed.

6.11. Quiz

  1. Cite 2 advantages and 2 drawbacks of using canvas
  2. Cite 2 examples of canvas’s lack of accessibility
  3. The following code will draw a black rectangle on the screen:

    var canvas=document.getElementById("mycanvas");
    canvas.fillRect(50,50,100,200);
    • True
    • False
  4. This gives you the number of pixels in your canvas:

    var pixels=ctx.getImageData(0,0,canvas.width,canvas.height);
    var pixels_nbr=pixels.data.length;
    • True
    • False