MISCELLANEOUS TECHNICAL ARTICLES BY Dr A R COLLINS

Canvas Layers

Benefits of layers

The HTML canvas element is a 2D graphics surface with a quite powerful set of drawing methods, but when a canvas is used for animation, the entire contents must be completely redrawn 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.

CanvasStack

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

An 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. The canvas layer 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.

In cases where the canvas dimensions have been styled to change with any resize of its containing element, a resize event handler ensures that layers track the background canvas dimensions.

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 background 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.

Getting started

Firstly download the library CanvasStack-2v01.js or the minified version CanvasStack-2v01-min.js and ensure that it will be loaded by including in the document header the following:

  <script src="[source directory/]CanvasStack-2v01-min.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>

  ...
  var cvsStk = new CanvasStack("cvsId");

The returned CanvasStack object has the following methods:

createLayer,
deleteLayer,
deleteAllLayers,
addResizeCallback.

Multiple CanvasStack instances can be created that refer to the same background. The stack properties are stored as properties of the background canvas itself to avoid conflict among CanvasStack instances

Creating a new layer

To create a CanvasStack layer, call the cvsStk.createLayer method, the ID of the HTML canvas element created will be automatically generated and returned as a string. 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

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 Cango3D library the call is:

  var cgoL1 = new Cango3D(layer1_ID);

CanvasStack example

Figure 1 shows a spinning plate demo copied from the Canvas 3D Graphics page. In this demonstration two layers have been used, the background canvas holds the animation of a plate spinning on a stick, every 50 msec the plate is rotated, the background canvas layer is cleared and the plate and stick redrawn in their new orientations.

Here is the code snippet to create the stick and plate:

Figure 1. 3D animation of a spinning plate.

var ovlyID;
function drawPlate(cvsID)
{
  const stick = new Path3D(["M",0,0,0,"C",0,33,0,-5,66,0,-15,100,0], 
                      {y:-100, strokeColor:"sienna", lineWidth:3}),
        plate = new Shape3D(svgToCgo3D(shapeDefs.circle(50)), 
                      {xRot:-75, fillColor:"lime", backColor:"lime"}),
        plateNstick = new Group3D(stick, plate);
  let angle = 0;

  function turnPlate()
  {
    angle += 20;
    if (angle > 360)
      angle -= 360;

    plateNstick.rotateY(angle);  // apply matrix to Group3D
    g.render(plateNstick);
  }

  g = new Cango3D(cvsID),
  g.setWorldCoords3D(-50, -110, 100);
  g.setLightSource(0, 500, 200);
  ovlyID = cvsStk.createLayer(); // create a canvas layer

  setInterval(turnPlate, 50)     // keep doing this forever
}

To demonstrate the independence of the layers in the CanvasStack, the code above created an overlay canvas with ID saved as a global. We can write "Hullo World" on this overlay right across the spinning plate. The spinning plate is erased and redrawn every 50msec but the "hullo World" written on the overlay canvas is unaffected. We can clear the overlay canvas to erase "Hullo World" and not affect the pinning plate animation.

To try this code, click here. It will draw "Hullo World" over the spinning plate animation in Fig 1. Click here to erase the "Hullo World".

The source code for drawing 'Hullo World' is shown below.

function drawHullo()
{
  const oCtx = new Cango3D(ovlyID);  
  oCtx.setWorldCoords3D(-150, -150, 300);
  const txt = new Text3D("Hullo World", {
    zRot: 20,
    y:125,
    strokeColor:"blue",
    fontSize: 24,
    lorg:5,
    fontWeight: 600});
  oCtx.render(txt);
}

function clearHullo()
{
  const oCtx = new Cango3D(ovlyID); 
  oCtx.clearCanvas();
}

CanvasStack

CanvasStack constructor

Syntax:

var cvsStk = new CanvasStack(canvasID, [stackLimit]);

Description:

Creates a new CanvasStack object and an array initially holding only a reference to the background canvas as layer[0] if it doesn't already exist. As layers are created references are stored in the array. The array is unique to the background canvas. It is stored as a property of the canvas object so each background canvas can only host one CanvasStack instance.

Parameters:

canvasID: String - The ID of the back ground canvas. The width and height of this canvas sets the size of the canvases in the stack.

stackLimit: Number - (optional) A limit to the number of layers a background canvas can support. This is provided to prevent recursive or other excessive creation of layers. The default value is 6 representing the background layer plus five overlay canvases.

Returns:

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

CanvasStack methods

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.

addResizeCallback

Syntax:

cvsStk.addResizeCallback(fn);

Description:

Adds a function reference to the array of functions to be called if the CanvasStack background is resized. When a resize event occurs CanvasStack resizes all the layers that have been created. Resizing a canvas element clears all drawing on that canvas so the user may wish to have the contents of the layer redrawn after a resize event. Calling 'addResizeCallback, passing the redraw function pointer will ensure the redrawn is called. Functions are called 250msec after the first resize event to limit multiple calls if the canvas resize is evoked by a drag and drop.

Parameters:

fn: Function reference - A reference to a function to be called in the event of a canvas resize. The function should take no parameters.