MISCELLANEOUS TECHNICAL ARTICLES BY Dr A R COLLINS

Canvas Graphics Library

Cango, a graphics library for the canvas element

Cango (CANvas Graphics Object) is an open source JavaScript library designed to assist drawing on the HTML5 canvas element. The core Cango module provides a set of objects: Path, Shape, Img, Text and ClipMask that are easily styled and transformed. It also provides methods to set up a world coordinate system and render these objects to the canvas. Several extension modules are available to give enhanced capabilities for Animation, Text manipulation and so on. The Cango User Guide provides a detailed reference for the objects and methods of the core module.

The current version of Cango is 27, it is open source and the source code is available at Cango-27v00.js and the minified version at Cango-27v00-min.js.

Cango basic drawing capability

Figure 1. Examples of drawing using the Cango graphics library.

Features of Cango

  • User defined World Coordinates - Cango supports independent X and Y scaling. Data may to be plotted in its original units removing the need to scale all values to canvas pixels. Each Cango instance can have its own world coordinates. Multiple graphics contexts may exist together on a single canvas.
  • Right Handed Cartesian (RHC) and SVG style (Left Handed Cartesian) coordinate systems support - Cango supports both RHC coordinates which have Y values increasing UP the canvas, and SVG coordinates which have Y values increase DOWN the canvas. Since each Cango context instance has its own world coordinates, the different coordinate systems may co-exist on a canvas.
  • Canvas Layers - The functionality of a CanvasStack is built into Cango. Transparent canvas overlays can be created to assist in drawing cursors, data overlays and animation without re-drawing any static objects on the background canvas or other layers.
  • Objects - Cango supports five core drawing objects; Path, Shape, Img, Text and ClipMask. These are the building blocks of all the drawing done by the Cango library.
  • Groups and inherited transforms - Objects can be grouped as children of a Group object. Groups can have more Groups as children, creating a family tree of objects. Group methods enable transforms to apply to to all children of the Group in a single call. Child objects inherit the transforms applied to parent Groups.
  • Pointer events - Both Groups and individual objects can be enabled for mouse click and drag-n-drop events. All the event handling support code is built-in, applications just specify the callback functions.
  • Color Gradients, Drop Shadows and Borders - Cango objects can have border and shadows with a variety os styles and colors.

Getting Started

To use the Cango, download the minified version Cango-27v00-min.js. Save the file on your server or in a directory accessible to JavaScript code running in the web page, then add the following line to the web page header:

<script src="[path to Cango file/]Cango-27v00-min.js"></script>

Within the body of the web page insert a canvas element. The only attribute required by Cango is a unique id string.

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

Cango drawing starts by creating a Cango context, the only parameter of the constructor is the ID of the canvas.

var cgo = new Cango(canvasID);

Note: Cango can also draw on an off-screen canvas, just pass a reference to the canvas object rather than an ID.

Setup the world coordinate system

Cango uses a gridbox to provide a reference for the world coordinate origin and the X and Y scale factors. By default the gridbox covers the full canvas. The gridbox dimensions can be defined by setGridbox or gridboxPadding methods.

The world coordinates are defined by calling either the setWorldCoordsRHC method to set up a Right Hand Cartesian system, or the setWorldCoordsSVG method to set up an SVG (left hand cartesian) system. Each method specifies the X,Y world coordinate value of the gridbox origin (lower left for RHC and upper left for SVG systems) and the width and height of the gridbox in world coordinates. If the height parameter is omitted then Y axis scaling is the same as the X axis scaling (square pixels).

Cango can draw paths, shapes, images and text. The simplest way to do this is using any of the Cango methods:

cgo.drawPath(pathDef, options);
cgo.drawShape(outlineDef, options);
cgo.drawImg(imgDef, options);
cgo.drawText(strg, options);

Each of these methods creates an instance of the corresponding Path, Shape, Img or Text object and renders it to the canvas. The 'options' object properties are used to apply transforms to the descriptor coordinates to translate the object's coordinates, or to scale, rotate or flip it. These properties can also set style properties such as color, line width, borders, drop shadows etc.

Path and Shape objects can have their outline path defined by either a String or an Array of SVG "path" format commands ("M", "l", "C", "a", "A" etc) with their associated parameters and coordinates specifying the Path or Shape outline, a PathSVG object (from the PathSVGUtils extension) or a Path2D object (native canvas Object). Path objects are rendered as outlines, Shape objects are always filled with color.

Img objects can take a String specifying an image file URL, an ImageSegment object, an HTML Image object or another Canvas as their descriptor argument. If a URL is passed then the image is loaded into an HTML Image object. The image is asynchronously loaded (if not pre-loaded) and then rendered to the canvas. An ImageSegment displays a cropped version of an Image object.

Text objects are specified by a String. The options properties then define the font-family, font size and weight etc.

Simple drawing example

A line graph can be very simply plotted using the Path object since the Cango allows the path to be specified as an array of x,y number pairs. This represents a concession to the strict SVG syntax which always requires an initial 'M' command before an array of coordinates are recognized as line segments. The world coordinates should first be set to suit the range of x values and y values expected in the data. As an example Fig 3 shows a graph of a sine wave of amplitude 50 over the range 0 to 2π.

Figure 3. Graph plotting using 'drawPath'.

function plotSine(cvsID)
{
  const g = new Cango(cvsID);
  g.gridboxPadding(10);
  g.setWorldCoordsRHC(0, -50, 6, 100);

  const data = [];
  for (let i=0; i<=2*Math.PI; i+=0.03) {
    data.push(i, 50*Math.sin(i));
  }
  g.drawPath(data, {strokeColor:'red'});
}

Drawing Objects

Once a canvas drawing gets more complex with shapes needing modification or if we are going to animate them, the single use draw methods are not well suited. Cango supplies Path, Shape, Img, Text and ClipMask classes which take a descriptor and options as described for the basic draw methods but in addition they have methods to apply dynamic transforms, enable drag-and-drop change style properties etc.

Any object are created by calling its constructor.

var pathobj = new Path(pathDef, options);
var shapeobj = new Shape(outlineDef, options);
var imgobj = new Img(imgDef, options);
var txtobj = new Text(strg, options);
var mask = new ClipMask(outlineDef, options);

Each class constructor take as arguments a descriptor and an options object specifying various transforms and style specifiers that will determine the appearance of the object when rendered to the canvas. Once instantiated these objects may be rendered by different Cango instances on any layer.

Groups

Cango provides the Group object to enable multiple objects to be treated as a single entity when applying transforms and rendering to the canvas. Since other Groups can be added to a Group, family trees of objects can be created and so provide a mechanism for inheritance of transforms by descendant objects.

Dynamic transforms

All individual objects and Groups have a transform methods that apply translation, rotation, scale or skew transforms. The accumulated transforms are applied in the order to build a transform matrix which is applied to the object coordinates when rendered. These transforms do not affect the object definition, the array of transforms is cleared after the object is rendered, although they can be reapplied with the transformRestore method. The transforms applied to a Group are inherited by its children. Transforms on an object are applied before transforms inherited from a parent Group. These transform methods are:

obj.translate(xOfs, yOfs);
obj.scale(xScl, yScl);
obj.rotate(degs);
obj.skew(degH, degV);

Pointer event methods

Handling of drag-n-drop or mouse click events on any object or Group can be enabled by the obj.enableDrag or obj.enableClickmethods. The 'enableClick' method takes a single parameter, the callback function to be called on a click event. The 'enableDrag' method takes three parameters, each a callback function which will be called when any mousemove, mousedown or mouseup event occurs. When called the callback functions are passed an EventData object which holds properties: target, pointerPos, dragPos, dragOfs, dragAngle and dragOfsAngle useful. Enabling drag-n-drop or click on a Group recursively enables drag-n-drop or click on all the group's descendent objects.

Cango provides the transformRestore which are used to restore the dynamic transforms to an object so that drag transforms need only be incremental modifications.

Fig 4. shows a simple example, a rectangle Shape object. Just click and drag it to see the effect. The source code is shown to the left.

Figure 4. Example of drag-n-drop, click and drag the rectangle.

function dragBox(cvsID)
{
  const boxOutline = ['M',0,0, 'l',40,0, 0,100, -40,0, 'z'];
  const box = new Shape(boxOutline, {
    fillColor:'orange',
    border:true,
    strokeColor:"brown"});

  function dragObj(evt)
  {
    evt.target.transformRestore();
    evt.target.translate(evt.dragOfs.x, evt.dragOfs.y);
    g.render(evt.target, true);    // true = clear canvas
  }

  const g = new Cango(cvsID);
  g.setWorldCoordsRHC(-100, -100, 400);

  box.enableDrag(dragObj);
  g.render(box);
}

Rendering Objects and Groups to the canvas

Once an object is created it can be drawn onto the canvas by the 'render' method of any Cango instance.

cgo.render(obj [, clear]);

The render method takes an object or Group as its first parameter, if the optional 'clear' parameter is either Boolean true or string "clear", the canvas is cleared prior to rendering the object or group. If this second argument is undefined any other value the canvas is not cleared and the object is rendered onto the canvas in addition to any existing drawing. For animations the canvas should cleared before rendering each frame, so the second argument should be true for rendering each frame.

Cango Extensions

Various modules are available to assist more specialised drawing. The modules can provide Cango Extension Objects and additional methods for these objects and for Cango itself. Here is a brief description of some of the modules currently available.

PathSVG Utilities module

The PathSVGUtils module, independent of Cango but is an extremely useful adjunct to Cango. It enables the creation and manipulation of SVG path data that can then act as the descriptors for Cango's Path, Shape or ClipMask objects. The PathSVG object behaves somewhat like the Path2D canvas utility but with many more methods. It takes either a string or array of SVG path data and breaks it into an array of segments. These segment data are then available for manipulation using the PathSVG methods. These can translate, scale, rotate the path. They can join two paths, reverse path direction and so on.

PathSVG also has static methods which generate predefined paths and shape outlines; circle, arc, arcTo, ellipse, square, rectangle, triangle, cross and ex.

The PathSVGUtils module also provides the static methods splineFit and polyline. PathSVG.splineFit takes an array of data points and returns a path that is a smooth fit through the points. PathSVG.polyline takes an array of data points and returns a path joining the points with straight line segments.

Cango Animation module

If the CangoAnimation module is loaded then Cango.animation method becomes available. This method takes as arguments a 'drawFn' that actually draws each frame and 'pathFn' that that is called prior to each frame to configure and transform the animated objects.

For a given canvas all animations, regardless of which layer they are on, are controlled by playAnimation, pauseAnimation, stopAnimation and stepAnimation methods on a single master timeline ensuring animations are synchronized.

The Tweener and PathTweener objects are provided to interpolate parameters between key frames.

The module also provides the BoneArray and WideBoneArrayobjects to assist the animation of segmented objects. The elements of the arrays inherit any rotation applied to preceding segments like the bones of an arm.

Cango Zoom and Pan module

If the CangoZoomPan-2v00.js module is loaded the initZoomPan function becomes available. 'initZoomPan' creates an overlay canvas holding the zoom and pan controls in the upper right corner, these are configured to zoom or pan the objects drawn by specified Cango graphics contexts. This is achieved by scaling their world coordinates and calling the specified re-draw function.

Cango Text Utilities module

If the CangoTextUtils module is loaded, several Cango Extension Objects that simplify drawing specialised types of Text become available. These are HTMLMathText, MathText, TextOnPath, VectorText, ExpNotationText and SciNotationText.

Cango Arrows module

If the CangoArrows module is loaded two additional Cango methods; drawArrow and drawArrowArc become available.

Cango Axes module

If the CangoAxes module is loaded the Cango Extension Objects; Axes, BoxAxes, xaxis, yaxis become available along with the several global support functions: toEngFixed, toEngPrec, toEngNotation and toSciNotation that are useful in formatting numbers for graphs and technical drawings.

Further details

The Cango User Guide the provides detailed reference for all the Cango methods and utilities. Examining the source code of the Filter Design, Astronomical Clock, Armstrong Pattern, Screw Thread Drawing, Sonar Ray Tracing, Spectrum Analyser, Zoom FFT, Gear Drawing, Flintlock and Wheellock pages will give numerous examples of Cango in use.