An example of canvas Path2D objects

  Read comments...

Summary
A description and examples of the new canvas Path2D object which can be used to cache path drawing commands and replay or test them as necessary

Warning: The features described on this page are only implemented in some browsers at the current time (January 2015)!

Introduction

The new Path2D object has now been implemented and is available in some browsers (see below). Previously canvas had only one path that you could manipulate and then stroke and/or fill. Once you do that and start a new path the first path is lost. Path2D objects allow you to retain your paths and either replay them as required or test them using the isPointInPath() function. This last feature - hit testing with isPointInPath() - will make hit testing with canvas faster and simpler.

Currently if you want to test whether a pair of coordinates are within a square you have to either check that those coordinates are within the X/Y/W/H boundaries of the square or replay the path, not stroke or fill it and then use the isPointInPath() function.

With the new Path2D object, in conjunction with the isPointInPath(), function it's a simple matter of checking the result of that function (either true or false). It has the potential to eliminate lots of code - particularly when dealing with more complex shapes.

Browser support

Initially Only Google Chrome supported the Path2D object. Now though Firefox also has support for it (and also other new canvas features as detailed here). In time other browsers will presumably get support.

Your browser:


Chrome:
Yes
Firefox:
Yes
Internet Explorer:
No
Safari:
No
Opera:
No

An example

This is an example of adding two paths to the canvas - a square and a rounded rectangle. The old way of testing each path for clicks would involve either geometric calculations or if the path is complex it may require replaying the entire path again, not stroking or filling it, and then using the isPointinPath method(). With the Path2D object you can just pass the path that you want to check to the isPointInPath() function.

<script>
    window.onload = function ()
    {
        canvas  = document.getElementById('cvs');
        context = canvas.getContext('2d')

        // A square
        path1 = new Path2D();
        path1.rect(105,105,90,90);

        // A rounded rectangle
        path2 = new Path2D();
        path2.arc(50,50,45,Math.PI / 2,Math.PI * 1.5,false);
        path2.lineTo(200,5);
        path2.arc(200,50,45,Math.PI * 1.5,Math.PI / 2,false);
        path2.closePath();
    
        context.strokeStyle = 'black';
        context.fillStyle   = 'red';

        context.stroke(path1);
        context.stroke(path2);

        context.fill(path1);
        context.fill(path2);
        
        
        
        // Add hit testing for the shapes tht have been drawn
        canvas.onmousemove = function (e)
        {
            var context = e.target.getContext('2d');
            var coordX  = e.offsetX;
            var coordY  = e.offsetY;
            
            // Test the square for clicks
            if (context.isPointInPath(path1, coordX, coordY)) {
                e.target.style.cursor = 'pointer';
                return;
            }
            
            // Test the rounded rectangle for clicks
            if (context.isPointInPath(path2, coordX, coordY)) {
                e.target.style.cursor = 'pointer';
                return;
            }
            
            // Reset the pointer to the default
            e.target.style.cursor = 'default';
        }
    }

The Path2D constructors argument

The Path2D object can be instantiated with either no arguments - in which case an empty path is created or you could pass the constructor an existing Path2D object. You can also pass it a string which should be SVG path commands (see below). If you pass it an existing Path2D object then its drawing commands will be added to the new path object and you can then execute further drawing commands on the new Path2D object.

path1 = new Path2D();
path1.rect(10,10,50,50);

path2 = new Path2D(path1);
path2.rect(100,100,50,50);

context.stroke(path2);

The net result of the above code is that both of the paths will be stroke to the canvas.

SVG path syntax

As well as being another Path2D object - the Path2D constructors argument can also be a string consisting of SVG path data. SVG path data is a string that concisely describes the path. An example of SVG path syntax is:

path = new Path2D('M 100,100 h 50 v 50 h 50');
path.stroke();

The M refers to a moveTo() call and the h refers to a horizontal line (hence only the X coordinate is given and it's drawn relative to the current position). One advantage of this is that it allows you pass around whole paths - including transmitting them over a network or store them in a database - and still retain the path information.

Note:

Incidentally, and somewhat inspired by SVG paths - RGraph has an existing RGraph.path() function that resembles SVG path syntax and can prove useful to both make defining paths more concise and it also facilitates the easy passing around of those paths.

Note:

This RGraph.path function has also now been seperated out into a standalone path function that you can use and/or add to your own projects - without any RGraph dependencies. You can download the path function here, add it to its own file, include it in your page - and suddenly life becomes much easier (with significantly less typing)!

The path function does not attempt to follow SVG path notation - it's a function to make canvas paths and operations much easier. There's examples of the function on the linked page.

The Path2D object API

The Path2D object API shares functions with the 2D context API and those functions are:

Other functions available on the path object are:

path = new Path2D();
path = new Path2D(path);
path = new Path2D(svg_path);
Creates a new path. The constructor can either take no arguments in which case a new path is created, an existing path object in which case the new path object is a copy of the one that you pass it or it take an SVG path string - in which case the Path consists of the path defined by the SVG path.

path.addPath(path, transform)
path.addPathByStrokingPath(path, styles, transform) [Not implemented]
Adds the given path (the first argument) to the path object. The stroking variant takes styles (eg line dash settings) from the styles argument - which should be a DrawingStyles object.

path.addText(text, styles, transform, x, y [, maxWidth ]) [Not implemented]
path.addText(text, styles, transform, path [, maxWidth ]) [Not implemented]
path.addPathByStrokingText(text, styles, transform, x, y [, maxWidth ]) [Not implemented]
path.addPathByStrokingText(text, styles, transform, path [, maxWidth ]) [Not implemented]
Adds the given text to the path. The different variants of these functions allow you to either give X/Y coordinates or a path object/. If you give a path the text will be drawn along that path instead of horizontally. You can also give a maximum width setting - and if you do the text will be scaled to fit that width if necessary.

How can I use them now?

Path2D objects are only implemented in Chrome canary as of March 2014. You could do something such as create a way of parsing SVG path syntax. You could then pass the strings around and replaying them (in order to test for clicks) would be easier - it might be easier though to use an array of letters (to represent actions) than strings which would need to be parsed - this is what the RGraph.Path() function mentioned previously does. Or, like RGraph, you could employ shape testing (eg a bar chart is all rectangles - so the hit testing function is less complex).

An example of the RGraph.Path() function where actions are defined by letters is:

RGraph.Path(context, ['b', 'm', 50, 50, 'r', 50, 50, 100, 100, 's', 'black', 'f', 'red]);

This creates a path which moves to the coordinates [50,50] (which technically is unnecessary) and then draws a rectangle at [50,50] which is 100px wide and 100px high. It then strokes the path in black and fills it in red. You can read more about this function in the API documentation.

Example pages

Here are some simplified examples of the Path2D objects use. More examples will be added over time.

Share RGraph
X

Comments