HexagonJS
Edit Page
Drawing
A library for creating interactive drawings.
Examples

HTML

<div id="drawing" class="drawing-example"></div>

JavaScript

(function () {
  const speed = 0.1;
  const size = 16;
  const spacing = 3.4;
  const hexGridWidth = 10;
  const hexGridHeight = 15;

  // set up the drawing
  const drawing = new hx.Drawing('#drawing');
  drawing.enablePan();
  drawing.enableZoom();
  drawing.enablePerformanceGauge();
  drawing.camera.zoomMin = 0.05;
  drawing.camera.zoomMax = 3;
  drawing.camera.zoom = 1 * drawing.dpr;
  drawing.camera.position.x = size * (hexGridWidth - 0.5) * spacing / 2;
  drawing.camera.position.y = size * (hexGridHeight - 0.5) / 2;

  // create the grid
  const hexagonCurve = (function() {
    var j, results;
    results = [];
    for (i = j = 0; j < 6; i = ++j) {
      results.push([
        Math.sin((2 * i + 1) * Math.PI / 6) * size,
        Math.cos((2 * i + 1) * Math.PI / 6) * size
        ]);
    }
    return results;
  })();

  const colors = [
    '#3D3D3D',
    '#74B06B',
    '#D89C4D',
    '#697584',
    '#C24563',
    '#98719D',
    '#D0D0D0',
  ];

  hexGrid = (function() {
    var j, ref, results;
    results = [];
    for (x = j = 0, ref = hexGridWidth - 1; (0 <= ref ? j <= ref : j >= ref); x = 0 <= ref ? ++j : --j) {
      results.push((function() {
        var k, ref1, results1;
        results1 = [];
        for (y = k = 0, ref1 = hexGridHeight - 1; (0 <= ref1 ? k <= ref1 : k >= ref1); y = 0 <= ref1 ? ++k : --k) {
          hex = drawing.create('shape');
          hex.set('polygon', hexagonCurve);
          hex.set('position.x', size * spacing * x + size * spacing / 2 * (y % 2));
          hex.set('position.y', size * 1 * y);
          hex.set('fill.enabled', true);
          hex.set('fill.color', colors[1]);
          object = {
            generationsAlive: 0,
            drawingObject: hex,
            alive: Math.random() > 0.5,
            nextAlive: false
          };
          results1.push(object);
        }
        return results1;
      })());
    }
    return results;
  })();

  // utility methods for the grid
  const getSurroundingCells = function(x, y) {
    var ref, ref1, ref2, ref3, ref4, ref5, xx;
    xx = x + (y % 2) - 1;
    return [(ref = hexGrid[xx]) != null ? ref[y - 1] : void 0, (ref1 = hexGrid[x]) != null ? ref1[y - 2] : void 0, (ref2 = hexGrid[xx + 1]) != null ? ref2[y - 1] : void 0, (ref3 = hexGrid[xx]) != null ? ref3[y + 1] : void 0, (ref4 = hexGrid[x]) != null ? ref4[y + 2] : void 0, (ref5 = hexGrid[xx + 1]) != null ? ref5[y + 1] : void 0].filter(function(d) {
      return d != null;
    });
  };

  const getSecondarySurroundingCells = function(x, y) {
    var ref, ref1, ref2, ref3, ref4, ref5, xx;
    xx = x + (y % 2) - 1;
    return [(ref = hexGrid[x - 1]) != null ? ref[y] : void 0, (ref1 = hexGrid[xx]) != null ? ref1[y - 3] : void 0, (ref2 = hexGrid[xx]) != null ? ref2[y + 3] : void 0, (ref3 = hexGrid[xx + 1]) != null ? ref3[y - 3] : void 0, (ref4 = hexGrid[xx + 1]) != null ? ref4[y + 3] : void 0, (ref5 = hexGrid[x + 1]) != null ? ref5[y] : void 0].filter(function(d) {
      return d != null;
    });
  };

  const iterateGrid = function(f) {
    var j, ref, results;
    results = [];
    for (x = j = 0, ref = hexGridWidth - 1; (0 <= ref ? j <= ref : j >= ref); x = 0 <= ref ? ++j : --j) {
      results.push((function() {
        var k, ref1, results1;
        results1 = [];
        for (y = k = 0, ref1 = hexGridHeight - 1; (0 <= ref1 ? k <= ref1 : k >= ref1); y = 0 <= ref1 ? ++k : --k) {
          results1.push(f(x, y, hexGrid[x][y]));
        }
        return results1;
      })());
    }
    return results;
  };

  let last = (new Date()).getTime();

  // main loop
  drawing.on('update', function() {
    // control the update rate
    if ((new Date()).getTime() - last >= 100 / speed) {
      last = (new Date()).getTime();
      //hexagonal game of life rules: http://www.well.com/~dgb/hexrules.html
      iterateGrid(function(x, y, cell) {
        var c, j, k, len, len1, ref, ref1, total;
        total = 0;
        ref = getSurroundingCells(x, y);
        for (j = 0, len = ref.length; j < len; j++) {
          c = ref[j];
          if (c.alive) {
            total += 1;
          }
        }
        ref1 = getSecondarySurroundingCells(x, y);
        for (k = 0, len1 = ref1.length; k < len1; k++) {
          c = ref1[k];
          if (c.alive) {
            total += 0.3;
          }
        }
        return cell.nextAlive = total >= 2.0 && total < 3.3;
      });
      // draw
      return iterateGrid(function(x, y, cell) {
        cell.alive = cell.nextAlive;
        cell.generationsAlive = cell.alive ? cell.generationsAlive + 1 : 0;
        const color = colors[Math.min(cell.generationsAlive, 4) + 1];
        return cell.drawingObject.set('fill.color', color, 250);
      });
    }
  });
}())

CSS

.drawing-example {
  height: 300px;
}
Api
Prototypes
hx.DrawingextendsEventEmitterdeprecated
Deprecated
This module has been deprecated and will be moved to a standalone library in a later release.
Used to create interactive drawings.
Constructors
hx.DrawingselectorString
Creates a new Drawing.
Arguments
selectorString
A CSS selector which uniquely identifies the element to create the drawing in.
hx.DrawingselectorHTMLElement
Creates a new Drawing.
Arguments
selectorHTMLElement
The HTMLElement where the drawing should be created.
Properties
cameraCamera
The camera object used by this drawing. See the Camera class docs for more details.
sidebar
A property that is added after the sidebar is enabled with the enableSidebar method.
Methods
createtypeStringidStringDrawingObject
Creates a new object in the default layer.
Arguments
typeString
The type of primitive to create, should be one of:
  • rectangle
  • circle
  • line
  • text
  • grid
  • shape
  • composite
By default a random id is created for each new object. To override this behaviour, supply an id here.
Default: hx.randomId()
Returns
DrawingObject
Returns one of Rectangle, Circle, Line, Text, Grid, Shape or Composite, depending on the type given.
createLayerLayer
Creates a new layer in this drawing.
Returns
Layer
The newly created layer
deleteobjDrawingObject
Removes an object from the drawing
Arguments
objDrawingObject
The object to remove
deleteAll
Removes all objects from the drawing
enablePan
Enables mouse/touch control for panning around the drawing.
enablePerformanceGauge
Shows a performance gauge in the corner of the drawing, displaying how many frames per second the drawing is achieving, and how long a frame takes to render in milliseconds.
enableSearchbox
Shows a search box in the corner of the drawing, and causes the drawing to emit 'search' events when the user performs a search.
enableSelection
Allows objects to be selected by clicking on them. Selection is disabled by default as there is a performance penalty when clicking drawings with many (hundreds of thousands) of objects. Individual objects must also have selection enabled using .set('selectable', true) in order to be selectable.
enableSidebarpositionStringtogglePositionStringpopulateFunction
A function for enabling and populating the drawing sidebar.
Arguments
positionString
The position of the sidebar. Accepted values are 't', 'r', 'b', 'l' and correspond to top, right, bottom and left respectively.
togglePositionString
The position of the toggle button for opening/closing the sidebar. This consists of a two character string (e.g. 'bl') which corresponds to one of the corners of the drawing. If this value is not provided, no toggle button is shown and the second argument becomes the populate function.
populateelemHTMLElement
The function called to populate the sidebar. It is called when the sidebar is enabled to give the sidebar content.
Arguments
elemHTMLElement
The sidebar element to populate.
enableZoom
Enables mouse wheel/pinch-to-zoom control for zooming in on drawings.
findidStringDrawingObject
Find a drawing object by id. This is an O(1) lookup.
Arguments
The id of the object to find. This will be the random id that gets generated automatically for an object, or the custom id that was supplied when it was created. See the create method for more details.
Returns
DrawingObject
the drawing object with the id provided, or undefined if no object with that id was found.
findByindicatorFunctionDrawingObject
Find an object using an indicator function. This will loop through all objects in the drawing and find the first that satisfies the condition. This is an O(n) (where n is the number of objects in the drawing)
Arguments
indicatorobjDrawingObjectBoolean
Arguments
objDrawingObject
Returns
true if the object satisfies the conditions being searched for.
Returns
DrawingObject
the first drawing object that satisfied the indicator function, or undefined if none was found.
followobjDrawingObjectzoomOutNumbercontinuallyEvaluateZoom
Make the camera follow an object as it moves around the drawing. If the user pans with the mouse, then this follow instruction is cancelled.
Arguments
objDrawingObject
The drawing object to follow.
zoomOutNumber
How much to zoom the camera out by when following the object.
Default: 1
continuallyEvaluateZoom
Whether or not the zoom level should be re-evaluated every update when following an object.
Default: false
hideSidebar
A method for hiding the sidebar once enableSidebar has been called.
resume
Resumes the drawing after stop has been called.
selectobjDrawingObject
Select an object in the drawing. This requires that enableSelection has been called in order to work.
Arguments
objDrawingObject
The object to select
selectedArray[DrawingObject]
Returns the list of selected drawing objects.
Returns
Array[DrawingObject]
showobjDrawingObjectzoomOutNumber
Make the camera show an object. This is the same as follow, except that the camera will not continue to follow the object after showing it in view.
Arguments
objDrawingObject
The drawing object to show.
zoomOutNumber
How much to zoom the camera out by when showing the object.
Default: 1
showLayer
Shows the layer supplied, and hides all other layers.
showSidebar
A method for showing the sidebar once enableSidebar has been called.
stop
Stops the drawing from updating (until resume is called). This should be also be called when removing a drawing from the page, as could happen in single page apps.
toggleSidebar
A method for toggling the sidebar once enableSidebar has been called.
unselect
Unselects an object in the drawing.
unselectAll
Unselects all selected objects in the drawing.
Camera
Every drawing has one of these camera instances accessible at drawing.camera.
Properties
position
An object with x and y properties that contain the position of the camera.
xMaxNumber
The maximum x level allowed. position.x cannot be set to a value larger than this.
xMinNumber
The minimum x level allowed. position.x cannot be set to a value smaller than this.
yMaxNumber
The maximum y level allowed. position.y cannot be set to a value larger than this.
yMinNumber
The minimum y level allowed. position.y cannot be set to a value smaller than this.
zoomNumber
The zoom level of the camera. 1 shows everything at normal scale, 0.5 shows everything at 50% scale (zoomed out), 150% shows things at 150% scale (zoomed in).
zoomMaxNumber
The maximum zoom level allowed. zoom cannot be set to a value larger than this.
zoomMinNumber
The minimum zoom level allowed. zoom cannot be set to a value smaller than this.
Methods
setupBoundszoomMinNumberzoomMaxNumberxMinNumberyMinNumberxMaxNumberyMaxNumber
A helper method for setting up the bounds for camera position and zoom levels (xMin, xMax, ..., zoomMin, zoomMax)
Arguments
zoomMinNumber
zoomMaxNumber
xMinNumber
yMinNumber
xMaxNumber
yMaxNumber
Circle
A primitive type for drawing a circle. Create with drawing.create('circle') or layer.create('circle').
Methods
getnameStringAny
Gets the current value of one of the properties of this object.
Arguments
nameString
The name of the property to get. The available properties are:
  • position.x
  • position.y
  • radius
  • fill.enabled
  • fill.color
  • outline.enabled
  • outline.color
  • outline.width
Returns
Any
The value of the property
setnameStringvalueAnydurationNumberanimationEndFunction
Sets the value of one of the properties of this object, optionally animating the change.
Arguments
nameString
The name of the property to set. The available properties are:
Properties that can be animated:
  • position.x
  • position.y
  • radius
  • fill.color
  • outline.color
  • outline.width
Properties that can't be animated:
  • fill.enabled
  • outline.enabled
valueAny
The value to set the property to.
durationNumber
If set, the property will be animated to this value, and the animation will take duration milliseconds.
animationEndcompleteBooleanvalueAtInterruptAny
A function to call at the end of the animation.
Arguments
completeBoolean
true if the animation completed
valueAtInterruptAny
If complete is false, then this will contain the value of the property when the animation was interrupted.
Composite
A primitive type for grouping other primitives together. Create with drawing.create('composite') or layer.create('composite').
Methods
createtypeStringnameStringDrawingObject
Creates a new object in this composite object.
Arguments
typeString
The type of primitive to create, should be one of:
  • rectangle
  • circle
  • line
  • text
  • grid
  • shape
  • composite
nameString
The name which can be used to access the object.
Returns
DrawingObject
Returns one of Rectangle, Circle, Line, Text, Grid, Shape or Composite, depending on the type given.
deletenameString
Removes an object from this composite object.
Arguments
nameString
The name of the object to remove.
getnameStringAny
Gets the current value of one of the properties of this object, or one of the nested objects.
Arguments
nameString
The name of the property to get. The available properties are:
  • position.x
  • position.y
  • angle
  • scale
  • objectName._
Returns
Any
The value of the property
setnameStringvalueAnydurationNumberanimationEndFunction
Sets the value of one of the properties of this object, optionally animating the change.
Arguments
nameString
The name of the property to set. The available properties are:
Properties that can be animated:
  • position.x
  • position.y
  • angle
  • scale
  • objectName._ (these properties can only be animated if the property of the nested object being referred to supports animation)
valueAny
The value to set the property to.
durationNumber
If set, the property will be animated to this value, and the animation will take duration milliseconds.
animationEndcompleteBooleanvalueAtInterruptAny
A function to call at the end of the animation.
Arguments
completeBoolean
true if the animation completed
valueAtInterruptAny
If complete is false, then this will contain the value of the property when the animation was interrupted.
DrawingObject
The base class for all primitives
Grid
A primitive type for drawing a grid. Create with drawing.create('grid') or layer.create('grid').
Methods
getnameStringAny
Gets the current value of one of the properties of this object.
Arguments
nameString
The name of the property to get. The available properties are:
  • position.x
  • position.y
  • cellSize.x
  • cellSize.y
  • gridSize.x
  • gridSize.y
  • gridLines.color
  • gridLines.width
  • gridLines.enabled
  • cells.enabled
  • cells.states
  • cells.palette
Returns
Any
The value of the property
setnameStringvalueAnydurationNumberanimationEndFunction
Sets the value of one of the properties of this object, optionally animating the change.
Arguments
nameString
The name of the property to set. The available properties are:
Properties that can be animated:
  • position.x
  • position.y
  • cellSize.x
  • cellSize.y
  • gridLines.color
  • gridLines.width
Properties that can't be animated:
  • gridLines.enabled
  • cells.enabled
  • cells.states
  • cells.palette
  • gridSize.x
  • gridSize.y
valueAny
The value to set the property to.
durationNumber
If set, the property will be animated to this value, and the animation will take duration milliseconds.
animationEndcompleteBooleanvalueAtInterruptAny
A function to call at the end of the animation.
Arguments
completeBoolean
true if the animation completed
valueAtInterruptAny
If complete is false, then this will contain the value of the property when the animation was interrupted.
Layer
Returned from Drawing.createLayer
Properties
alphaNumber
The opacity of the layer. A value between 0 and 1.
visibleBoolean
Whether or not this layer is visible. The default is true (visible).
Methods
createtypeStringidStringDrawingObject
Creates a new object in this layer.
Arguments
typeString
The type of primitive to create, should be one of:
  • rectangle
  • circle
  • line
  • text
  • grid
  • shape
  • composite
By default a random id is created for each new object. To override this behaviour, supply an id here.
Default: hx.randomId()
Returns
DrawingObject
Returns one of Rectangle, Circle, Line, Text, Grid, Shape or Composite, depending on the type given.
deleteobjDrawingObject
Removes an object from the layer
Arguments
objDrawingObject
The object to remove
deleteAll
Removes all objects from the layer
findidStringDrawingObject
Find a drawing object by id within this layer. This is an O(1) lookup.
Arguments
The id of the object to find. This will be the random id that gets generated automatically for an object, or the custom id that was supplied when it was created. See the create method for more details.
Returns
DrawingObject
the drawing object with the id provided, or undefined if no object with that id was found.
findByindicatorFunctionDrawingObject
Find an object using an indicator function within this layer. This will loop through all objects in the layer and find the first that satisfies the condition. This is an O(n) (where n is the number of objects in the layer)
Arguments
indicatorobjDrawingObjectBoolean
Arguments
objDrawingObject
Returns
true if the object satisfies the conditions being searched for.
Returns
DrawingObject
the first drawing object that satisfied the indicator function, or undefined if none was found.
setAlphaCurvetypeStringstartNumberendNumber
Links the alpha value of this layer to the zoom level of the drawing.
Arguments
typeString
The type of curve to use, currently the available types are <a href="http://en.wikipedia.org/wiki/Triangular_function">'triangle'</a> and <a href="http://en.wikipedia.org/wiki/Ramp_function">'ramp'</a>.
startNumber
The start point of the ramp or triangle.
The end point of the ramp or triangle.
Line
A primitive type for drawing a line. Create with drawing.create('line') or layer.create('line').
Methods
getnameStringAny
Gets the current value of one of the properties of this object.
Arguments
nameString
The name of the property to get. The available properties are:
  • start.x
  • start.y
  • end.x
  • end.y
  • color
  • width
Returns
Any
The value of the property
setnameStringvalueAnydurationNumberanimationEndFunction
Sets the value of one of the properties of this object, optionally animating the change.
Arguments
nameString
The name of the property to set. The available properties are:
Properties that can be animated:
  • start.x
  • start.y
  • end.x
  • end.y
  • color
  • width
valueAny
The value to set the property to.
durationNumber
If set, the property will be animated to this value, and the animation will take duration milliseconds.
animationEndcompleteBooleanvalueAtInterruptAny
A function to call at the end of the animation.
Arguments
completeBoolean
true if the animation completed
valueAtInterruptAny
If complete is false, then this will contain the value of the property when the animation was interrupted.
Rectangle
A primitive type for drawing a rectangle. Create with drawing.create('rectangle') or layer.create('rectangle').
Methods
getnameStringAny
Gets the current value of one of the properties of this object.
Arguments
nameString
The name of the property to get. The available properties are:
  • position.x
  • position.y
  • width
  • height
  • fill.enabled
  • fill.color
  • outline.enabled
  • outline.color
  • outline.width
Returns
Any
The value of the property
setnameStringvalueAnydurationNumberanimationEndFunction
Sets the value of one of the properties of this object, optionally animating the change.
Arguments
nameString
The name of the property to set. The available properties are:
Properties that can be animated:
  • position.x
  • position.y
  • width
  • height
  • fill.color
  • outline.color
  • outline.width
Properties that can't be animated:
  • fill.enabled
  • outline.enabled
valueAny
The value to set the property to.
durationNumber
If set, the property will be animated to this value, and the animation will take duration milliseconds.
animationEndcompleteBooleanvalueAtInterruptAny
A function to call at the end of the animation.
Arguments
completeBoolean
true if the animation completed
valueAtInterruptAny
If complete is false, then this will contain the value of the property when the animation was interrupted.
Shape
A primitive type for drawing an arbitrary shape. Create with drawing.create('shape') or layer.create('shape').
Methods
getnameStringAny
Gets the current value of one of the properties of this object.
Arguments
nameString
The name of the property to get. The available properties are:
  • position.x
  • position.y
  • fill.enabled
  • fill.color
  • outline.enabled
  • outline.width
  • outline.color
  • curve
  • polygon
Returns
Any
The value of the property
setnameStringvalueAnydurationNumberanimationEndFunction
Sets the value of one of the properties of this object, optionally animating the change.
Arguments
nameString
The name of the property to set. The available properties are:
Properties that can be animated:
  • position.x
  • position.y
  • fill.color
  • outline.width
  • outline.color
Properties that can't be animated:
  • fill.enabled
  • outline.enabled
  • curve
  • polygon
valueAny
The value to set the property to.
durationNumber
If set, the property will be animated to this value, and the animation will take duration milliseconds.
animationEndcompleteBooleanvalueAtInterruptAny
A function to call at the end of the animation.
Arguments
completeBoolean
true if the animation completed
valueAtInterruptAny
If complete is false, then this will contain the value of the property when the animation was interrupted.
Text
A primitive type for drawing text. Create with drawing.create('text') or layer.create('text').
Methods
getnameStringAny
Gets the current value of one of the properties of this object.
Arguments
nameString
The name of the property to get. The available properties are:
  • position.x
  • position.y
  • color
  • font
  • size
  • text
  • align.x
  • align.y
Returns
Any
The value of the property
setnameStringvalueAnydurationNumberanimationEndFunction
Sets the value of one of the properties of this object, optionally animating the change.
Arguments
nameString
The name of the property to set. The available properties are:
Properties that can be animated:
  • position.x
  • position.y
  • color
  • size
  • text
Properties that can't be animated:
  • font
  • align.x
  • align.y
valueAny
The value to set the property to.
durationNumber
If set, the property will be animated to this value, and the animation will take duration milliseconds.
animationEndcompleteBooleanvalueAtInterruptAny
A function to call at the end of the animation.
Arguments
completeBoolean
true if the animation completed
valueAtInterruptAny
If complete is false, then this will contain the value of the property when the animation was interrupted.
Functions
hx.drawingoptionsObjectSelection
Creates a new Drawing set up on a detached element, wrapped in a selection
Arguments
options
See the options object for constructing Drawing
Returns
Selection
A selection containing an element with an Drawing initialised on it
Classes
hx-drawing-sidebar
The class given to the sidebar element when it is created.
Classes
hx-drawing-sidebar-content
A class to give to the sidebar content when populating the sidebar.
hx-drawing-sidebar-title
The class to give to title elements to show within the drawing sidebar. It adds padding and a background color to the element.
Getting Started
Getting Started
Intro
The drawing api exposes a simple api for drawing diagrams in the browser. It uses a model-view set-up for describing the drawings - you have to create the objects you want to display, and manipulate their properties, and the drawing api will ensure that the objects described in your diagram get rendered to the screen.
It currently uses the canvas element to do the drawing, although there is no reason it couldn't be extended to support SVG or WebGL rendering in the future. For large diagrams (that are bigger than the screen) with relatively few objects, SVG has the potential to perform better.
Creating a blank canvas
The following example sets up an empty drawing

HTML

<div id="empty-drawing"></div>

JavaScript

var drawing = new hx.Drawing('#empty-drawing');
Pretty boring, right?
Note that a border has been added to all the examples on this page to make it easier to see. This isn't something that is necessary for normal apps.
Adding something to the drawing
Adding a rectangle to the drawing is almost as easy.

HTML

<div id="rectangle-drawing" class="docs-drawing"></div>

JavaScript

var drawing = new hx.Drawing('#rectangle-drawing');

var rect = drawing.create('rectangle');
rect.set('position.x', -25)
rect.set('position.y', -25)
rect.set('width', 50);
rect.set('height', 50);
rect.set('fill.color', '#776655');
The drawing will redraw at 60fps (or as close to that as it can achieve). Having the drawing redraw constantly like this allows animations to appear smooth.
Note
The render loop will eventually be able to turn off when there are no changes to display. This will result in less cpu consumption and save the battery life for mobile devices. This functionality has not yet been added - however this is something that will happen internally, so there will be no change to the api in order for this optimisation to happen.
Enabling mouse controls
Enabling mouse and touch controls can be done with the enablePan and enableZoom methods on the drawing object.

HTML

<div id="controlled-drawing" class="docs-drawing"></div>

JavaScript

var drawing = new hx.Drawing('#controlled-drawing');

drawing.enablePan();
drawing.enableZoom();

var rect = drawing.create('rectangle');
rect.set('position.x', -25)
rect.set('position.y', -25)
rect.set('width', 50);
rect.set('height', 50);
rect.set('fill.color', '#335544');
What does the drawing api provide over using bare canvas/SVG?
The drawing api exposes a set of features that are not dependent on the type of drawing engine being used. This gives the advantage that we could in theory swap all rendering over to use WebGL in the future once browser support is mature enough.
The drawing api also takes care of all the conversions from drawing coordinates to screen coordinates. This allows you to draw things at any scale, and have the api scale them to fit on the page for you.
The drawing api also comes with a built in camera object, which allows panning and zooming on the diagram, and also ways to detect clicks on objects in the drawing.
The api is also designed around the idea that diagrams will be being updated via a live feed, most probably using websockets.
These are all features that do not come natively with the canvas element (since the canvas element is essentially just a bitmap).
Primitives
Available Types
There are currently 7 different primitives drawing types in the api:
  • Circle
  • Rectangle
  • Text
  • Line
  • Grid
  • Shape
  • Composite
These can all be created through the 'create' method on the drawing object. Each primitive type has it's own class which is described in the api section.
Example
This example shows how each primitive can be created, and shows use of some of the properties.

JavaScript

var primitiveDrawing = new hx.Drawing('#drawing-example-primitives');
primitiveDrawing.enablePan();
primitiveDrawing.enableZoom();
primitiveDrawing.camera.setupBounds(0.25, 10, -100, -100, 100, 100);

var colors = [
  'rgb(192,46,29)',
  'rgb(241,108,32)',
  'rgb(236,170,56)',
  'rgb(92,167,147)',
  'rgb(17,120,153)',
  'rgb(13,60,85)'
];

// create a circle
var circle = primitiveDrawing.create('circle');
circle.set('position.x', -80);
circle.set('fill.color', colors[0]);
circle.set('radius', 10);

// create a rectangle
var rectangle = primitiveDrawing.create('rectangle');
rectangle.set('position.x', -60);
rectangle.set('position.y', -10);
rectangle.set('height', 20);
rectangle.set('width', 20);
rectangle.set('fill.color', colors[1]);

// create a text object
var text = primitiveDrawing.create('text');
text.set('position.x', -35);
text.set('color', colors[2]);
text.set('text', 'text');
text.set('align.y', 'middle');

// create a line
var line = primitiveDrawing.create('line');
line.set('start.x', -5);
line.set('end.x', 15);
line.set('color', colors[3]);

// create a grid
var grid = primitiveDrawing.create('grid');
grid.set('position.x', 20);
grid.set('position.y', -12.5);
grid.set('gridSize.x', 5);
grid.set('gridSize.y', 5);
grid.set('cellSize.x', 5);
grid.set('cellSize.y', 5);
grid.set('gridLines.color', colors[4]);

// create a shape (described with a cubic curve)
var shape = primitiveDrawing.create('shape');
shape.set('position.x', 60);
shape.set('fill.color', colors[5]);

var edges = 10;
var size = 10;

//construct a curve to draw
var curve = hx.range(edges+1).map(function(i){
  return [
    Math.sin((2*i + 1) * Math.PI / edges) * size,
    Math.cos((2*i + 1) * Math.PI / edges) * size,
    Math.sin((2*i + 2) * Math.PI / edges) * size*0.5,
    Math.cos((2*i + 2) * Math.PI / edges) * size*0.5
  ]
});

shape.set('curve', curve);

// make the primitiveDrawing fit the canvas
primitiveDrawing.show(drawing, 2);

CSS

.docs-drawing {
  height: 200px;
}
Setting Properties
The primitives each have a separate set of properties that can be set. However, the method for setting the properties is always the same: using the set method:
obj.set('property', value);
The properties that are available to set are described in the api section - they differ for each primitive type.
Composite Objects
Objects can be grouped together using composite objects. This makes it possible to create more detailed objects in the scene (rather than having just a simple rectangle for displaying something, for example). Composite objects are intended to be used for small, reusable drawing objects.
The objects contained in the composite object can be referenced through the composite objects set and get methods.

JavaScript

var drawing = new hx.Drawing('#composite-example');

drawing.enablePan();
drawing.enableZoom();
drawing.camera.setupBounds(0.25, 10, -100, -100, 100, 100);

function createFace() {
  var composite = drawing.create('composite');

  composite.create('rectangle', 'face');
  composite.create('rectangle', 'lefteye');
  composite.create('rectangle', 'righteye');
  composite.create('rectangle', 'lips');

  composite.set('face.width', 10);
  composite.set('face.height', 10);
  composite.set('face.position.x', -5);
  composite.set('face.position.y', -5);
  composite.set('face.fill.color', '#996622');

  composite.set('lefteye.width', 2);
  composite.set('lefteye.height', 2);
  composite.set('lefteye.position.x', -3);
  composite.set('lefteye.position.y', -3);
  composite.set('lefteye.fill.color', '#FFF');

  composite.set('righteye.width', 2);
  composite.set('righteye.height', 2);
  composite.set('righteye.position.x', 1);
  composite.set('righteye.position.y', -3);
  composite.set('righteye.fill.color', '#FFF');

  composite.set('lips.width', 6);
  composite.set('lips.height', 1);
  composite.set('lips.position.x', -3);
  composite.set('lips.position.y', 2);
  composite.set('lips.fill.color', '#FFF');

  return composite;
}

var face1 = createFace();
var face2 = createFace();
var face3 = createFace();

// each composite objects that have been created can be acted on as if they are a single object
face1.set('face.fill.color', '#225599');
face1.set('position.x', 0);
face1.set('angle', -0.5);

face2.set('face.fill.color', '#552299');
face2.set('position.x', 20);
face2.set('scale', 1.5);

face3.set('face.fill.color', '#995522');
face3.set('position.x', 40);
face3.set('angle', 0.5);

// make the drawing fit the canvas
drawing.show(drawing, 1.5);
Camera Manipulation
Enabling mouse controls
Enabling the drawing to be controlled with the mouse / touch can be done as follows:
var drawing = new hx.Drawing('#drawing-id');

// enable panning
drawing.enablePan();

// enable mouse-wheel zoom, and pinch zoom on touch devices.
drawing.enableZoom();
Moving the camera
The position of the camera can be set directly using the following:
drawing.camera.position.x = 100;
drawing.camera.position.y = 250;
Showing an object
The position and zoom of the camera can be changed so that a chosen object appears at the centre of the screen:
drawing.show(obj);
See the api docs for more details.
Following an object
The camera can also be set to follow an object using:
drawing.follow(obj);
Again, see the api docs for more details.
Limiting the camera's movements
The minimum and maximum zoom of the camera can be set to limit the zoom range:
drawing.camera.zoomMin = 0.5;
drawing.camera.zoomMax = 1.5;
Both zoomMin and zoomMax are scale factors - so 1 represents no zooming, 0.5 represents zooming out, and showing everything at 50% scale, and 1.5 represents zooming in, showing everything at 150% scale.
The area that the camera can pan around can also be limited by setting the xMin, xMax, yMin and yMax properties on the camera object:
drawing.camera.xMin = -100;
drawing.camera.xMax = -100;
drawing.camera.yMin = 100;
drawing.camera.yMax = 100;
Animation
Animating properties
Animations are a useful way to convey changing state to a user. Animated drawings don't just make a page more engaging; an animated drawing contains more information than a static one.
Almost every property of a drawing object can be animated. This means you can animate positions, sizes, colors, and even text containing numbers. There are a couple of properties where it does not make sense to be able to animate, and these are outlined in the api docs.
<div class="hx-layout hx-group hx-horizontal"> <div class="hx-section hx-medium">
Static

HTML

<div id="basic-drawing"></div>

JavaScript

var drawing = new hx.Drawing('#basic-drawing');

var colors = [
  'rgb(192,46,29)',
  'rgb(241,108,32)',
  'rgb(236,170,56)',
  'rgb(92,167,147)',
  'rgb(17,120,153)',
  'rgb(13,60,85)'
];

var circle = drawing.create('circle');
circle.set('fill.color', colors[5]);

function randomCol() {
  return colors[Math.floor(Math.random()*6)];
}

drawing.on('update', function(i) {
  if(i%120==0){

    var x = (Math.random() - 0.5) * 200;
    var y = (Math.random() - 0.5) * 150;

    circle.set('position.x', x);
    circle.set('position.y', y);
    circle.set('fill.color', randomCol());

  }
});
</div>
<div class="hx-section hx-medium">
Animated

HTML

<div id="animated-drawing"></div>

JavaScript

var drawing = new hx.Drawing('#animated-drawing');

var colors = [
  'rgb(192,46,29)',
  'rgb(241,108,32)',
  'rgb(236,170,56)',
  'rgb(92,167,147)',
  'rgb(17,120,153)',
  'rgb(13,60,85)'
];

var circle = drawing.create('circle');
circle.set('fill.color', colors[5]);

function randomCol() {
  return colors[Math.floor(Math.random()*6)];
}

drawing.on('update', function(i) {
  if(i%120==0){

    var x = (Math.random() - 0.5) * 200;
    var y = (Math.random() - 0.5) * 150;

    circle.set('position.x', x, 2000);
    circle.set('position.y', y, 2000);
    circle.set('fill.color', randomCol(), 2000);

  }
});
</div> </div>
The same method used to set property values is used to animate them. The third parameter to the set method is the duration in milliseconds that the transition should take. The animation will always start from it's current value and end at the value provided.
obj.set('property', value, duration);
If a property is set half way through an animation, the animation will be discarded, and the property value will be set. If another animation is applied whilst another animation is running, the current animation will stop, and the new one will start from the current state.
End of animation callback
obj.set('property', value, duration, function(){
  // gets called when the animation finishes
});
An optional callback can be supplied to be called at the end of a property animation. This callback will also be called if the animation is interrupted. See the api section for more details.
Interactivity
Making objects selectable

HTML

<div id="selection-example" class="docs-drawing"></div>

JavaScript

var drawing = new hx.Drawing('#selection-example');
drawing.enablePan();
drawing.enableZoom();
drawing.enableSelection();

drawing.camera.zoomMin = 0.25;
drawing.camera.zoomMax = 10.0;
drawing.camera.xMin = -100;
drawing.camera.yMin = -50;
drawing.camera.xMax = 100;
drawing.camera.yMax = 50;

// create a circle
var circle = drawing.create('circle');
circle.set('position.x', -25);
circle.set('fill.color', '#224466');
circle.set('radius', 10);
circle.set('selectable', true);

var circle = drawing.create('circle');
circle.set('position.x', 0);
circle.set('fill.color', '#446622');
circle.set('radius', 10);
circle.set('selectable', true);

var circle = drawing.create('circle');
circle.set('position.x', 25);
circle.set('fill.color', '#664422');
circle.set('radius', 10);
circle.set('selectable', true);

// make the drawing fit the canvas
drawing.show(drawing, 2);
Objects can be made selectable by calling:
obj.set('selectable', true);
In addition to this, the drawing must also have selection enabled:
drawing.enableSelection();
The selected objects can be accessed via the selected method on the drawing:
var selected = drawing.selected();
Objects can also be selected using the .select method on the drawing object:
var circle = drawing.create('circle');
...

drawing.select(circle);
Detecting clicks
The drawing object is an event emitter, which enables you to detect clicks as follows:
drawing.on('click', function(position){
  // position is an object with x and y properties which give
  // the screen coordinates relative to the drawing and wx and
  // wy properties which give world coordinates.
  console.log(position);
})
Drawings can be given a search box to give users a way to search for objects within the drawing:
drawing.enableSearchbox();
This on it's own won't actually do anything. It simply causes a box to be displayed on the drawing that the user can input searches to. To act on these searches, you must listen out for search events being emitted on the drawing object. Here is one way you could act on a search event being emitted.
drawing.on('search', function(searchString){
  var obj = drawing.findBy(function(obj){
    obj.get('attr.name') == searchString
  });

  if (obj) {
    drawing.select(obj);
    drawing.follow(obj);
  }
});
Example: try searching for circle-1, circle-2 or circle-3 in this drawing:

HTML

<div id="search-example" class="docs-drawing"></div>

JavaScript

var searchDrawing = new hx.Drawing('#search-example');
searchDrawing.enablePan();
searchDrawing.enableZoom();
searchDrawing.enableSelection();
searchDrawing.enableSearchbox();

// helper for setting up the bounds for the camera
searchDrawing.camera.setupBounds(0.25, 10, -100, -50, 100, 50);

searchDrawing.on('search', function(searchTerm){
  var obj = searchDrawing.find(searchTerm);
  if (obj) {
    searchDrawing.select(obj);
    searchDrawing.show(obj);
  }
});

// create a circle
var circle = searchDrawing.create('circle', 'circle-1');
circle.set('position.x', -25);
circle.set('fill.color', '#224466');
circle.set('radius', 10);
circle.set('selectable', true);

var circle = searchDrawing.create('circle', 'circle-2');
circle.set('position.x', 0);
circle.set('fill.color', '#446622');
circle.set('radius', 10);
circle.set('selectable', true);

var circle = searchDrawing.create('circle', 'circle-3');
circle.set('position.x', 25);
circle.set('fill.color', '#664422');
circle.set('radius', 10);
circle.set('selectable', true);

// make the searchDrawing fit the canvas
searchDrawing.show(searchDrawing, 2);
Layers
Toggling layer visibilty
Drawings internally by default all draw to a layer that is created by default. More layers-zoom can be added to a drawing via the createLayer method. Layers do one thing: allow you to hide or show groups of objects.
The following example allows you to toggle between two layers which show alternative views of something:

HTML

<button id="toggle" class="hx-btn hx-btn-toggle hx-positive">Toggle Layers</button>
<div id="layers-example" class="docs-drawing"></div>

JavaScript

var drawing = new hx.Drawing('#layers-example');

var colors = [
  'rgb(192,46,29)',
  'rgb(241,108,32)',
  'rgb(236,170,56)',
  'rgb(92,167,147)',
  'rgb(17,120,153)',
  'rgb(13,60,85)'
];

var layer1 = drawing.createLayer();
var layer2 = drawing.createLayer();

createArea = function(x, colIndex) {
  var circle = layer1.create('circle');
  circle.set('position.x', x);
  circle.set('fill.color', colors[colIndex]);
  circle.set('radius', 10);
  circle.set('selectable', true);

  hx.range(100).forEach(function(){
    var miniCircle = layer2.create('circle');

    var normal = colors[colIndex]
    var lighter = hx.color(colors[colIndex]).lighten(0.5).toString()
    var cr = function(x) {
      return  hx.color(normal).mix(hx.color(lighter), x).toString()
    }

    dist = Math.random()*9.5
    angle = Math.random()*Math.PI*2
    miniCircle.set('position.x', x + Math.cos(angle)*dist);
    miniCircle.set('position.y', Math.sin(angle)*dist);
    miniCircle.set('fill.color', cr(Math.random()));
    miniCircle.set('radius', 0.5);
    miniCircle.set('selectable', true);
  });
}

createArea(-25, 1);
createArea(0, 2);
createArea(25, 3);

drawing.show(drawing, 1.1);

drawing.showLayer(layer1);

hx.select('#toggle').on('click', function(){
  if(hx.select('#toggle').attr('data')=='true'){
    drawing.showLayer(layer1);
  } else {
    drawing.showLayer(layer2);
  }
}, true);
Linking zoom level to layer visibility
The visibility of a layer can also be linked to the zoom level of the camera. This make it possible to create diagrams which reveal more detail when zoomed in close, and show an overview when zoomed out.

HTML

<div id="layers-zoom-example" class="docs-drawing"></div>

JavaScript

var drawing = new hx.Drawing('#layers-zoom-example');

var colors = [
  'rgb(192,46,29)',
  'rgb(241,108,32)',
  'rgb(236,170,56)',
  'rgb(92,167,147)',
  'rgb(17,120,153)',
  'rgb(13,60,85)'
];

drawing.enableZoom();

drawing.camera.zoomMin = 0.25;
drawing.camera.zoomMax = 10.0;

var layer1 = drawing.createLayer();
var layer2 = drawing.createLayer();

createArea = function(x, colIndex) {
  var circle = layer1.create('circle');
  circle.set('position.x', x);
  circle.set('fill.color', colors[colIndex]);
  circle.set('radius', 10);
  circle.set('selectable', true);

  hx.range(100).forEach(function(){
    var miniCircle = layer2.create('circle');

    var normal = colors[colIndex]
    var lighter = hx.color(colors[colIndex]).lighten(0.5).toString()
    var cr = function(x) {
      return  hx.color(normal).mix(hx.color(lighter), x).toString()
    }

    dist = Math.random()*9.5
    angle = Math.random()*Math.PI*2
    miniCircle.set('position.x', x + Math.cos(angle)*dist);
    miniCircle.set('position.y', Math.sin(angle)*dist);
    miniCircle.set('fill.color', cr(Math.random()));
    miniCircle.set('radius', 0.5);
    miniCircle.set('selectable', true);
  });
}

createArea(-25, 1);
createArea(0, 2);
createArea(25, 3);

drawing.show(drawing, 1.1);

layer1.setAlphaCurve('ramp', 2, 1);
layer2.setAlphaCurve('ramp', 1, 2);
Overlays
Note
Drawings will receive an update in the future to enable showing overlays that contain regular html elements. This will provide a way to display information on an object that has been selected in drawing.
This will be necessary since selection of text isn't possible when using canvas.