MISCELLANEOUS TECHNICAL ARTICLES BY Dr A R COLLINS

Canvas Layers

Benefits of layers

The canvas element is designed to provide native script driven graphics on web pages. The canvas is a 2D drawing surface with a quite powerful set of drawing methods, but the individual items drawn on the canvas are all flattened to create a single image. When a canvas is used for animation, the image requires a complete redraw for each animation frame, even if only one item in the drawing has changed.

A far more efficient animation technique is to use a stack of transparent layers overlaying a background image, with the items to be animated each being drawn on a separate layer. Only the layers that change on each frame are redrawn and the other layers are left unchanged. The graphics engine can sort out the exposed areas to be redrawn from underlying layers without user code being required.

The CanvasStack

A stack of canvas layers suitable for animation on a web page can be created and managed using a JavaScript CanvasStack. This provides an (initially empty) array of transparent canvas layers directly overlaying an existing canvas along with methods for creating new layers or deleting existing layers.

The existing canvas element becomes the background layer to the canvas stack. Each new layer added is a transparent canvas the same size as the background canvas and positioned directly over it. It may be drawn onto with the same methods as the background or any canvas element. Those areas left blank are transparent and the underlying canvas content will be visible. A layer's contents may be erased and the underlying drawing so exposed will show through with no user code required for redrawing.

In cases where the canvas dimensions have been styled to change with any resize of its containing element, Version 1v06 adds a resize event handler to ensure that layers track the background canvas dimension.

Creating the CanvasStack requires a HTML canvas element to exist on the page, after the document is loaded the CanvasStack constructor is called passing the ID of the canvas. The CanvasStack object creates an (empty) array for the overlay canvases. Each subsequent calls to the CanvasStack method createLayer will create a new transparent overlay canvas. Typical setup code is as follows:

Firstly download the library canvasStack1v06.js and ensure that it will be loaded by including in the document header the following:

  <script type="text/javascript" src="canvasStack1v06.js"></script>

To insert a CanvasStack over a canvas element, make sure the background canvas element has a unique ID. The following HTML snippet shows and example. The width and height attributes set the number of drawing pixels on the canvas:

<canvas id="cvs1" width="500" height="300"></canvas>

The background color of the canvas can be set by the CSS style it has been assigned. If the 'background-color' property is not set, the background canvas will be transparent by default, and the contents of the page beneath the canvas will show through. If the background color is specified, clearing the background canvas will fill it with the background color.

The canvas stack is created by passing the ID of the background canvas to the CanvasStack constructor as follows:

  var cvsStk = new CanvasStack("cvsId");

The returned CanvasStack object has the following methods:

getBackgroundCanvasId,
createLayer,
deleteLayer,
deleteAllLayers.

Drawing in the background canvas or in any layer is just the same as drawing in any canvas. First get a reference to the canvas element by passing the canvas ID to the 'document.getElementById' method. To access the background canvas, use the ID of the canvas already on the page, or for consistency, the 'cvsStk.getBackgroundCanvasId' method is available. To draw in this canvas, a graphics context must be created using the canvas element 'getContext("2D")' method as follows:

  var bkgID = cvsStk.getBackgroundCanvasId();
  var ctx = document.getElementById(bkgID).getContext('2D');

To create a Cango graphics context use the Cango constructor. This must be passed the ID string of the canvas as follows:

  var bkgID = cvsStk.getBackgroundCanvasId();
  var cgo = new Cango(bkgID);

Creating a new layer

To create a CanvasStack layer, call the cvsStk.createLayer method, the ID of the canvas created will be returned. The new transparent canvas will be placed on top of all the other layers already in the stack. A typical call to create a layer is as follows:

  var layer1_ID = cvsStk.createLayer();  // create a new layer, its ID string is returned

Drawing in the layer

Once the new layer has been created a graphics context must be created to draw into the new canvas. To create a native canvas context, a reference to the canvas element is used as follows:

  var L1_ctx = document.getElementById(layer1_ID).getContext('2D');

If drawing with the Cango library the call is:

  var L1_ctx = new Cango(layer1_ID);

CanvasStack example

Figure 1 shows a clock, its a reproduction of the clock from the Mozilla canvas animation tutorial, but here it has been drawn using the Cango library. In this demonstration two layers have been used, the background canvas holds the image of the face and the second layer is used to draw the hands. Every 250 msec the 'hands' layer is cleared and the hands are redrawn rotated to show the new time.

Here is the code snippet to create the canvas for the clock face and the canvasStack holding a layer for the hands and other layers for 'Hullo World' message:

The 'drawClock' function creates the clock face in the background layer. It then creates the hands which are to be drawn using a different graphics context which is associated with a layer. Here are relevant few lines:

...
g = new Cango(cvsID);         // g is the graphics context for the background
g.gridboxPadding(25, 5, 25, 5);     // start drawing the face in the background canvas
g.fillGridbox("white");
g.setWorldCoordsRHC(-175, -175, 350);

this.clockCS = new CanvasStack(cvsID);  // create a CanvasStack
L1_ID = this.clockCS.createLayer();     // create a layer for the clock hands
gL1 = new Cango(L1_ID);     // setup gL1 as the drawing context for 'hands' layer
gL1.dupCtx(g);              // copy canvas scaling etc from the background layer

drawClock();
updateClock();
setInterval(updateClock, 250);

Here is the clock drawn on a "white" square in the "wheat" colored canvas.

Figure 1. The basic clock with hands drawn on a separate layer.

To demonstrate the independence of the layers in the CanvasStack let's create a new layer, write "Hullo World" across the clock. In the flattened canvas drawing, such as used in the Mozilla tutorial, the 'Hullo World' would be written into the same canvas as the clock face and hands. When the next redraw of the hands occurs, a few milliseconds later, the "Hullo World" would be cleared. With a CanvasStack, "Hullo World" is on a separate layer and the clock hands will keep on being drawn and erased on their own layer.

To try this code, click here. It will draw "Hullo World" over the clock in Fig 1. Click here to erase the "Hullo World". The clock hands continue ticking along, independent of the other layers.

The source code for drawing 'Hullo World' is shown below, Note: the CanvasStack on which the clock is drawn is the global clockCS:

function drawHullo()
{
  var oCtx, hullo;

  if (!ovlyID)
  {
    ovlyID = clock.clockCS.createLayer(); // create a canvas layer (save ID as global to remove later)
    oCtx = new Cango(ovlyID);             // create an graphics context for overlay
    oCtx.gridboxPadding(25, 5, 25, 5);   // define square viewport width=50% of canvas width
    oCtx.setWorldCoordsRHC(-175, -175, 350);        // scale x axis 0 to 10, and y axis 0 to 10
    oCtx.drawText("Hullo World", {
      x:0,
      y:75,
      degs:20,
      fillColor:"#ffa500",
      fontSize:36,
      lorg:5,
      fontWeight: "bold" });
  }
}

CanvasStack

CanvasStack constructor

Syntax:

var cvsStk = new CanvasStack(canvasID);

Description:

Creates a new CanvasStack object and an array (initially empty) of transparent canvases overlying an existing canvas element.

Parameters:

canvasID:String - The ID of the canvas that acts a the background to all the overlay canvases in the stack. The width and height of this canvas sets the size of the canvases in the stack.

Returns:

Object - Reference to the CanvasStack object just created. This object has a set of methods, described below.

CanvasStack methods

getBackgroundCanvasId

Syntax:

var bkgId = cvsStk.getBackgroundCanvasId();

Description:

Returns the ID string of the background canvas element. Identical to the 'canvasId' string passed in to the CanvasStack constructor.

The ID is required to create a graphics context for drawing in the background canvas.

Parameters:

none.

Returns:

String - The canvas element ID attribute. This ID is required to create a graphics context to enable drawing into the canvas layer.

createLayer

Syntax:

var layerId = cvsStk.createLayer();

Description:

Creates a canvas layer the size of the background canvas and positioned to directly overlay it. The canvas is stacked to be above all previously created layers. The canvas is transparent.

Parameters:

none.

Returns:

String - The canvas element ID attribute. This ID is required to create a graphics context to enable drawing into the canvas layer.

deleteLayer

Syntax:

cvsStk.deleteLayer(layerId);

Description:

Deletes the canvas layer with ID, layerId. The layers canvas element is deleted from the document. Any further references to drawing in this layer will fail. The background canvas cannot be deleted.

Parameters:

layerId:String - The ID of the canvas to be deleted.

deleteAllLayers

Syntax:

cvsStk.deleteAllLayers();

Description:

Deletes all the canvas overlay layers from the 'cvsStk'. The background layer is not deleted. Each layer's canvas element is deleted from the document.

Parameters:

none.