JavaScript
Article
By Roman Lubushkin

Introducing GraphicsJS, a Powerful Lightweight Graphics Library

By Roman Lubushkin

HTML5 is the backbone of the modern web. And nowadays, when it comes to creating interactive images, SVG and Canvas are often the technologies of choice — Flash has been forgotten, Silverlight is a rare unicorn that dwells on the outskirts of the Web, and there are few who remember 3rd party plugins.

The pros and cons of each are well documented, but in a nutshell, SVG is better for suited to creating and handling interactive elements. This is because SVG is an XML-based vector format, and when an image is loaded into a page using an <svg> tag, every element within it becomes available in the SVG DOM.

In this article, I want to introduce you to GraphicsJS, a new and powerful open-source JavaScript drawing library, which is based on SVG (with VML fallback for old IE versions). I’ll start with a quick introduction to its basics, and then showcase the functionality of the library with the help of two short, yet spectacular samples: the first one is all about art, whereas the second one illustrates how to code a simple time-killer art game in less than 50 lines.

Why GraphicsJS

There are a lot of libraries out there that can help developers work with SVG: Raphaël, Snap.svg, and BonsaiJS to name a few of the best. Each of these has its own strengths and weaknesses, but their thorough comparison will be the subject of another article. This article is all about GraphicsJS, so let me explain what makes it good and special.

GraphicsJS, a lightweight and powerful SVG-based JavaScript graphics library by AnyChart

First of all, GraphicsJS is lightweight and has a very flexible JavaScript API. It implements many rich text features, as well as a virtual DOM — detached from the browser-specific implementation of the HTML DOM.

Secondly, it is a new open-source JavaScript library that was published as recently as last fall by AnyChart, one of leading global software developers in the field of interactive data visualization. AnyChart has been using GraphicsJS to render charts in its proprietary products for at least three years (since the release of the AnyChart 7.0) so GraphicsJS has been fully battle-tested. (Disclaimer, I am the head of R&D at AnyChart and the lead developer of GraphicsJS)

Thirdly, unlike AnyChart’s JavaScript charting libraries, GraphicsJS can be used for free in both commercial and non-profit projects. It is available on GitHub under the Apache license.

Fourthly, GraphicsJS is cross-browser compatible, supporting Internet Explorer 6.0+, Safari 3.0+, Firefox 3.0+, and Opera 9.5+. It renders in VML in older IE versions and SVG in all the other browsers.

Finally, GraphicsJS allows you to combine graphics and animation to great effect. Check out its main gallery which features an animated bonfire, rotating galaxy, falling rain, procedure generated leaves, playable 15-puzzle, and much more. GraphicsJS contains many further examples in its extensive documentation and its comprehensive API Reference.

GraphicsJS Basics

To start with GraphicsJS, you need to reference the library and create a block-level HTML element for your drawing:

<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>GraphicsJS Basic Example</title>    
  </head>
  <body>
    <div id="stage-container" style="width: 400px; height: 375px;"></div>

    <script src="https://cdn.anychart.com/js/latest/graphics.min.js"></script>
    <script>
      // GraphicsJS code here
    </script>
  </body>
</html>

Then you should create a stage and draw something in it, such as a rectangle, a circle, or other shape:

// create a stage
var stage = acgraph.create('stage-container');
// draw a rectangle
var stage.rect(25, 50, 350, 300);

Here is the example on CodePen in which we go a little bit further and draw the Deathly Hallows symbol.

Our First Masterpiece

Fill, Stroke and Pattern Fill

Any shape or a path can be colored using fill settings and stroke settings. Everything has a stroke (border), but only shapes and closed paths have a fill. Fill and stroke settings are very rich, you can go as far as a linear or circular gradient for both fill and stroke. Also, lines can be dashed, and image fill with several tiling modes is supported. But all this is a pretty standard stuff you can find in almost any library. What makes GraphicsJS special is its hatch and pattern fill feature that allows you not only to use one of the 32(!) available hatch fill patterns out of the box, but also to easily create your own patterns made of shapes or text.

Now, let’s see what exactly is possible! I will draw a small picture of a man standing near a house and then enhance it with different pattern and colour fills. For the sake simplicity, let’s make it a naïve art picture (and try not to get into art brut). Here it goes:

// create a stage
var stage = acgraph.create('stage-container');

// draw the frame
var frame = stage.rect(25, 50, 350, 300);

// draw the house
var walls = stage.rect(50, 250, 200, 100);
var roof  = stage.path()
  .moveTo(50, 250)
  .lineTo(150, 180)
  .lineTo(250, 250)
  .close();

// draw a man
var head = stage.circle(330, 280, 10);
var neck = stage.path().moveTo(330, 290).lineTo(330, 300);
var kilt = stage.triangleUp(330, 320, 20);
var rightLeg = stage.path().moveTo(320, 330).lineTo(320, 340);
var leftLeg = stage.path().moveTo(340, 330).lineTo(340, 340);

Check out the result on CodePen.

As you can see we are using variables now — all methods that draw something on the stage return a reference to the object created, and this link can be used to alter or remove the object.

Also note how chaining, which is everywhere in GraphicsJS, helps shorten the code. Chaining (e.g. stage.path().moveTo(320, 330).lineTo(320, 340);) should be used carefully, but it does make the code compact and easier to read if properly applied.

Now, let’s give this coloring page to a child and let them color it. Because even a child can master the following technique:

// color the picture
// fancy frame
frame.stroke(["red", "green", "blue"], 2, "2 2 2");
// brick walls
walls.fill(acgraph.hatchFill('horizontalbrick'));
// straw roof
roof.fill("#e4d96f");
// plaid kilt
kilt.fill(acgraph.hatchFill('plaid'));

Here’s what our example looks like now.

Now we have a picture of a highlander in a kilt, who is standing near his brick castle with a straw roof. We can even go out on a limb and say it is indeed a piece of art which we want to copyright. Let’s do that with the custom text-based pattern fill:

// 169 is a char code of the copyright symbol
var  text = acgraph.text().text(String.fromCharCode(169)).opacity(0.2);
var  pattern_font = stage.pattern(text.getBounds());
pattern_font.addChild(text);
// fill the whole image with the pattern
frame.fill(pattern_font);

As you can see, this is very easy to do: you create an instance of a text object, then form a pattern in a stage, and put a text into the pattern.

See the Pen colored copyrighted house / graphicsjs by SitePoint (@SitePoint) on CodePen.

Create a Time-Killer Art Game in Less Than 50 Lines of Code

In the next part of this article, I want to show you how to create a Cookie Clicker type game with GraphicsJS in less than 50 lines of code.

The name of the game is “Street Sweeper in the Wind”, and the player takes the role of a street sweeper cleaning a street during a windy fall afternoon. The game uses some code from the procedure generated leaves sample in the GraphicsJS gallery.

You can check out the finished game on CodePen (or at the end of the article).

Layers, zIndex and the Virtual DOM

We start off by creating a stage (as before), then declaring some initial variables:

// create stage
var stage = acgraph.create("stage-container");

// color palettes for leaves
var palette_fill = ['#5f8c3f', '#cb9226', '#515523', '#f2ad33', '#8b0f01']; 
var palette_stroke = ['#43622c', '#8e661b', '#393b19', '#a97924', '#610b01'];

// counter
var leavesCounter = 0;

For this game we’re going to be working with Layer — an object intended for grouping elements in GraphicsJS. Elements must be grouped if you want to apply similar changes to them, such as transformations. You can change layers when in suspended mode (more about this later), which improves performance and the user experience.

In this demo, we are using the layer functionality to help us group leaves together and avoid them covering the label (which tells us how many were swiped). To do this, we create a label and then call the stage.layer method, which create stage bound layer. We assign this layer a lower zIndex property than that of the label.

// create a label to count leaves
var counterLabel = stage.text(10,10, "Swiped: 0", {fontSize: 20});

// a layer for the leaves
var gameLayer = stage.layer().zIndex(counterLabel.zIndex()-1);

After doing that, no matter how many leaves we create in the layer, we can be sure they won’t cover the text.

Transformations

Next, let’s add a function to draw our leaves. This will make use of the convenient GraphicsJS transformations API that allows you to move, scale, rotate and shear both elements and groups of elements. When used in conjunction with layers and a virtual DOM, this is a very powerful tool.

function drawLeaf(x, y) {
  // choose a random color from a palette
  var index = Math.floor(Math.random() * 5);
  var fill = palette_fill[index];
  var stroke = palette_stroke[index];

  // generate random scaling factor and rotation angle
  var scale = Math.round(Math.random() * 30) / 10 + 1;
  var angle = Math.round(Math.random() * 360 * 100) / 100;

  // create a new path (leaf)
  var path = acgraph.path();

  // color and draw a leaf
  path.fill(fill).stroke(stroke, 1, 'none', 'round', 'round');
  var size = 18;
  path.moveTo(x, y)
    .curveTo(x + size / 2, y - size / 2, x + 3 * size / 4, y + size / 4, x + size, y)
    .curveTo(x + 3 * size / 4, y + size / 3, x + size / 3, y + size / 3, x, y);

  // apply random transformations
  path.scale(scale, scale, x, y).rotate(angle, x, y);

  return path; 
};

You see that every path is created in the same way, but then gets transformed. This results in a very nice random leaves pattern.

Handling Events

Any object, stage and layer in GraphicsJS can handle events. The full list of supported events is available in the EventType API. Stages have four special events to control rendering.

In this game example, we are using event listeners attached to the leaf objects so they disappear one by one when a user mouses over them. To do this, add the following code to the bottom of the drawLeaves function, before the return statement:

path.listen("mouseover", function(){
  path.remove();
  counterLabel.text("Swiped: " + leavesCounter++);
  if (gameLayer.numChildren() < 200) shakeTree(300); 
});

Here we can also see that we are using the layer to count leaves.

if (gameLayer.numChildren() < 200) shakeTree(300); 

Note that we are not actually storing the number of leaves here. As leaves are paths we add to and remove from a specific layer, this enables us to track how many children we have (and thus how many leaves remain).

GraphicsJS provides a virtual DOM, an abstraction of the HTML DOM, lightweight and detached from the browser-specific SVG/VML implementation. It is useful for doing a lot of great things such as keeping track of all objects and layers, applying transformations to groups, and optimizing rendering with the help of methods that allow us to track and control the rendering process.

Performance Optimization

The virtual DOM, along with event handlers, allows GraphicsJS users to control rendering. The Performance article can give you an idea of the ways these things are related.

When generating leaves in our game, we need to suspend rendering while adding new leaves and resume it only when all changes are done:

function shakeTree(n){
  stage.suspend(); // suspend rendering
  for (var i = 0; i < n; i++) {
    var x = Math.random() * stage.width()/2 + 50;
    var y = Math.random() * stage.height()/2 + 50;
    gameLayer.addChild(drawLeaf(x, y)); // add a leaf
  }

  stage.resume(); // resume rendering
}

This way of handling new elements makes new leaves appear almost instantly.

Finally, kick everything off with a call to shakeTree().

// shake a tree for the first time
shakeTree(500);

The End Result

See the Pen street sweeper in the wind / graphicsjs by SitePoint (@SitePoint) on CodePen.

Сonclusion

The shift to HTML5 has changed the Web. When it comes to modern web applications or even a simple site, we often encounter tasks that require image manipulation. While it is impossible to find a solution that works well in every situation, you should consider the GraphicsJS library. It is open-source, robust, has great browser support and lots of features that make it interesting, handy, and of course helpful.

I would be happy to hear your feedback regarding GrphicsJS in the comments below. Are you using it already? Would you consider using it for a new project? I’d love to hear why, or indeed why not. I am also currently working on a list of major JavaScript drawing libraries and an article that will compare and contrast them all. Also feel free to point me to any libraries you would like to see featured there.

Links for Further Reading

  • MichaelWebDev

    Looking good!
    I first got interested into mo.js. did you try it? Would you recommend using one over the other ?

    • Thank you! Mo.js looks interesting, but it seems to be unfinished yet, because too many features are marked as “coming soon”: http://mojs.io/tutorials/shape/. Thanks anyway for pointing me to this library. I will add it to my watching list.

  • Very interesting. Using svg a lot for some of my project for sure i will test that. Thanks.

  • Anton Baranchuk

    Nice!

  • renambot

    It seems nice but I would need a bit more convincing and comparison to other libraries.
    I’m using Snap. Tell me why I should switch ;-)

  • Andrey Khachaturov

    Hi! Currently GraphicsJS does not support 3D rendering out of the box. We plan to get to this later, probably even this year, but the timeline is not yet established. You can go on and use the current API or fork and make a pull request – we will certainly consider such a code :)

Recommended
Sponsors
Get the latest in JavaScript, once a week, for free.