Canvas Rendering Context 2D: Overview

This is the third article of an ongoing series about game development for the Web. In the previous article, we saw a general overview of the graphics pipeline and briefly discussed he rendering context.It is necessary to understand these notions moving forward in this series. From this point on, content will be discussed under that assumption.

In this delivery, we will take a deeper look into the HTML Canvas Element and the HTML Canvas 2D Context.


What is the Canvas Element again?

In the first article of the series, we defined the Canvas Element as a drawing region to display programmable graphics in a Web Page.It is necessary to emphasize that the Canvas Element is just a medium and not an API, the rendering context will define the API with which the content will be drawn.

The Canvas Element is not the only medium to draw graphics on the Web; other options exist such as SVG, and direct DOM Animations, each of which differ from the other and have their strengths and weaknesses. For example, the Canvas Element is resolution-dependent, this means that the output will be displayed only at certain predetermined resolutions, in contrast to SVG, that is resolution-independent, which means that will output content at the highest resolution possible. In addition, SVG uses vector graphics instead of pixels like the Canvas Element. One of the most notable advantages of using SVG is that vector-based graphics can be scaled by any amount without degrading the quality.

You might ask yourself why we are not using SVG to develop games in the browser. The answer to this question is simple, performance & flexibility. SVG is known as a retained mode graphics model, which means that it retains a complete model of the objects to be rendered. The Canvas Element in the other hand is known as an immediate mode graphics model, which means that it must re-issue all drawing commands required to describe the objects to be rendered per frame. The later provides the maximum amount of control and flexibility to the application program, which makes it ideal for video game development.Learn about rendering modes here.

Moreover, if you feel like exploring in more depth the differences between SVG and the Canvas Element I would recommend reading this article.


HTML Canvas 2D Context

The 2D Context is an API that provides objects, methods, and properties to draw and manipulate graphics on a canvas drawing surface in a two dimensional space. Each canvas has its own context, so if a page contains multiple canvas elements a reference to each context must exist. The context provides access to the 2D properties and methods that allow us to draw and manipulate images on a canvas element.

In the 2D Context, two-dimensional Cartesian coordinates represent the drawing surface space. That is, all points are a combination of an X value and a Y value. The X value represents the horizontal, or left to right, direction while the Y value represents the vertical, or top to bottom, direction. The origin is in the upper left hand corner. That means that as a point moves rightward, its X value increases. Likewise, as an object moves downward, the Y value increases. This is a traditional setting in the computer graphics world, but it can be changed using transforms.

Canvas Coordinates

The 2D Context type is part of the HTML specification and is an implementation of the CanvasRenderingContext2D interface. This means that when the context identifier 2d is used as an argument of getContext() method of a Canvas Element the user agent must return a new CanvasRenderingContext2D object. The API interface conformance is defined here.


The API

The CanvasRenderingContext2D API interface allows you to draw and manipulate images and graphics on a <canvas>. The interface puts at your disposal multiple functions some of which we used in the introductory article of this series.

The CanvasRenderingContext2D API contains an entire group of functions devoted to drawing shapes. In addition, the color of the geometry drawn can be set using the fillStyle and strokeStyle attributes.

Let us cover some of those functions in more detail.

Rectangles

Like any other formal drawing API, in CanvasRenderingContext2D you have to specify every vertex of a polygon in order to draw a geometric figure on the screen. However, rectangles are an exception; the API provides three functions solely for this purpose.

fillRect(x, y, w, h) - draws the given rectangle onto the canvas.

Example:

          // Get a reference to the canvas 
          var canvas = document.getElementById('canvas');

          // Get a reference to the drawing context 
          var ctx = canvas.getContext('2d');               

          //create a gradient object
          var gradient = ctx.createLinearGradient(0, 0, 300, 0);

          // Add the colors with fixed stops at 1/4 of the width.
          gradient.addColorStop("0","magenta");
          gradient.addColorStop(".25","blue");
          gradient.addColorStop(".50","green");
          gradient.addColorStop(".75","yellow");
          gradient.addColorStop("1.0","red");

          // Set the gradient as a fill pattern
          ctx.fillStyle = gradient;

          // Draw the rectangle
          ctx.fillRect (0,0,300,250);

          // Set the fill pattern to red
          ctx.fillStyle = "red";

          // Draw the rectangle
          ctx.fillRect(250,300,300,250);

          // Set the fill pattern to the hex code for yellow
          // Notice how we are using CSS color patterns
          ctx.fillStyle = "#FFFF00";

          // Draw the rectangle
          ctx.fillRect(500,0,300,250);

fillRect

strokeRect(x, y, w, h) - draws the box that outlines the given rectangle onto the canvas.

Example:

          // Get a reference to the canvas 
          var canvas = document.getElementById('canvas');

          // Get a reference to the drawing context 
          var ctx = canvas.getContext('2d');

          // Get a reference to the canvas 
          var canvas = document.getElementById('canvas');

          // Get a reference to the drawing context 
          var ctx = canvas.getContext('2d');

          // Blue rectangle outline
          ctx.lineWidth = "3";
          ctx.strokeStyle = "blue";
          ctx.strokeRect(5, 5, 300, 250);

          // Red rectangle outline
          ctx.lineWidth = "5";
          ctx.strokeStyle = "red";
          ctx.strokeRect(150, 200, 300, 150);

          // Green rectangle outline with round corners
          ctx.lineJoin = "round";
          ctx.lineWidth = "10";
          ctx.lineWidth = "7";
          ctx.strokeStyle = "green";
          ctx.strokeRect(250, 50, 150, 250);

strokeRect

clearRect(x, y, w, h) - clears all pixels on the canvas in the given rectangle to transparent black.

Example:

          // Get a reference to the canvas 
          var canvas = document.getElementById('canvas');

          // Get a reference to the drawing context 
          var ctx = canvas.getContext('2d');

          // Clear the center 80% of the canvas.
          ctx.clearRect(canvas.width * .1, canvas.height * .1, canvas.width * .8, canvas.height * .8);        

clearRect

Path API

In order to draw convex and concave polygons different from rectangles we need to use a Path object. When using Path objects we need to define each polygon segment using the lineTo() function and then instruct the API to draw the define path using the stroke() function.

lineTo(x, y) - Adds a new point to a sub-path and connects that point to the last point in the sub-path by using a straight line.

          // Get a reference to the canvas 
          var canvas = document.getElementById('canvas');

          // Get a reference to the drawing context 
          var ctx = canvas.getContext('2d'); 

          // Let's translate the drawing to the center of the canvas
          ctx.translate( canvas.width/2, canvas.height/2);

          // Set the stroke pattern to red
          ctx.strokeStyle = "#FF0000";

          // Set the fill pattern to grey
          ctx.fillStyle = "grey";

          // Set the triangle side size
          var side = 200;

          // Calculate the triangle height
          var height = side * Math.cos( Math.PI / 4 );

          // Reset the path
          ctx.beginPath();

          // Let's move to our starting point
          ctx.moveTo( 0, -height / 2);

          // Let's draw our triangle lines
          ctx.lineTo( -side / 2, height / 2);
          ctx.lineTo(side / 2, height / 2);
          ctx.lineTo(0, -height / 2);

          ctx.stroke();
          ctx.fill(); 

          // Close the path
          ctx.closePath();

lineTo

Other Shared Path API methods

The API also provides other functions with the purpose of drawing curved shapes.

arc(x, y, radius, startAngle, endAngle, anticlockwise) - Adds points to a path that represents an arc.

Example:

          // Get a reference to the canvas 
          var canvas = document.getElementById('canvas');

          // Get a reference to the drawing context 
          var ctx = canvas.getContext('2d'); 

          var colors = [ "#01C001", "#0152D8", "#E46F0F", "#868686", "#FF2727", "#FF0000" ];
          var colorIndex = 0;

          for (var i = 0; i < 2; ++i) {

            for (var j = 0; j < 3; ++j) {

              ctx.beginPath();
              ctx.strokeStyle = colors[colorIndex];

              var x = 25 + j * 50;
              var y = 25 + i * 50;

              var radius = 20;
              var startAngle = 0;
              var endAngle = Math.PI + (Math.PI * j) / 2;

              var anticlockwise = i % 2 == 0 ? false : true;

              ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);

              // Draw the arc.
              ctx.stroke();

              colorIndex++;
            }          
          }

arc

arcTo(x1, y1, x2, y2, radius) - Draws an arc of a fixed radius between two tangents that are defined by the current point in a path and two additional points.

Example:

          // Get a reference to the canvas 
          var canvas = document.getElementById('canvas');

          // Get a reference to the drawing context 
          var ctx = canvas.getContext('2d');

          // Drawing tangents

          ctx.beginPath();
          ctx.lineWidth = "3";
          ctx.strokeStyle = "black";

          // Horizontal line
          ctx.moveTo(80, 100);          
          ctx.lineTo(240, 100);

          // Vertical line
          ctx.moveTo(200, 60);
          ctx.lineTo(200, 220);

          // Draw the lines
          ctx.stroke();

          ctx.beginPath();
          ctx.strokeStyle = "red";
          ctx.lineWidth = "5";

          ctx.moveTo(120, 100);   

          // Horizontal line          
          ctx.lineTo(180, 100);   // Draw a horizontal line.                     

          // Draw an arc to connect the horizontal and the vertical line
          ctx.arcTo(200, 100, 200, 120, 20);

          // Vertical line
          ctx.lineTo(200, 180);    // Continue with a vertical line of the rectangle.                        

          // Draw the lines
          ctx.stroke();

          // Use the translate method to move the second example down. 
          ctx.translate(0, 220);              

arcTo

Text

The CanvasRenderingContext2D API also provides functions to draw text, which is another exception when compared to other mature APIs like OpenGL or Direct3D.

font - gets or sets the font for the current context.

fillText*(text, x, y) - Renders filled text to the canvas by using the current fill style and font.

Example:

          // Get a reference to the canvas 
          var canvas = document.getElementById('canvas');

          // Get a reference to the drawing context 
          var ctx = canvas.getContext('2d');

          gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);

          // Add the colors with fixed stops at 1/4 of the width.
          gradient.addColorStop("0", "magenta");
          gradient.addColorStop(".25", "blue");
          gradient.addColorStop(".50", "green");
          gradient.addColorStop(".75", "yellow");
          gradient.addColorStop("1.0", "red");

          // Set the fill pattern
          ctx.fillStyle = gradient;

          // Set the font
          ctx.font = "italic 200 36px/2 Unknown Font, sans-serif"

          var i;
          for (i = 0; i < 450; i += 50) {
             ctx.fillText("Canvas 2D", i, i);
          }          

fillText

strokeText(text, x, y) - Renders the specified text at the specified position by using the current font, lineWidth, and strokeStyle property.

Example:

          // Get a reference to the canvas 
          var canvas = document.getElementById('canvas');

          // Get a reference to the drawing context 
          var ctx = canvas.getContext('2d');

          gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);

          // Add the colors with fixed stops at 1/4 of the width.
          gradient.addColorStop("0", "magenta");
          gradient.addColorStop(".25", "blue");
          gradient.addColorStop(".50", "green");
          gradient.addColorStop(".75", "yellow");
          gradient.addColorStop("1.0", "red");

          // Set the fill pattern
          ctx.strokeStyle = gradient;

          // Set the font
          ctx.font = "italic 200 36px/2 Unknown Font, sans-serif"

          var i;
          for (i = 0; i < 450; i += 50) {
             ctx.strokeText("Canvas 2D", i, i + 50);
          }          

strokeText

Transformations

This set of functions is meant to alter the drawn geometry location and orientation within the Canvas Element. Their use involves some basic math understanding, and we will cover them separately in other articles. Regardless here is a list of all the available functions for this purpose.

State
  • save
  • restore
Matrix Transformations
  • scale
  • rotate
  • translate
  • transform
  • setTransform

Examples

The source code for these examples is hosted on GitHub and you can download it and do whatever you want with it. Likewise, the live demos can be accessed by clicking this link.


Conclusion

In this article, we discussed basic drawing functions provided the CanvasRenderingContext2D API. This API is widely used for different purposes, among them, chart libraries and data visualization tools. This demonstrates the importance of these graphic tools in a real world example, like data driven applications.

Moving forward in this series, we will use these functions to create simple 2D games. Once we have mastered the simpler Canvas 2D Context API, we will move to the 3D space.