Path2D objects
Written by Richard Heyes, RGraph author, on 29th March 2014- Introduction
- Browser support
- An example
- The Path2D constructor argument
- SVG path syntax
- The Path2D object API
- How can I use them now?
Introduction
The new Path2D
object has now been implemented and is available in Google Chrome
canary. Currently,
canvas
has only one path that you can 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 (this is what RGraph does).
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
At the time of writing (29th March 2014) Only Google Chrome canary supported the
Path2D
object. As time goes on this
will proliferate down to the stable version of Chrome and in time other browsers will
(presumably) support the object.
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> var canvas = document.getElementById('cvs'); var 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 that 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 constructor argument
The Path2D
object can be created 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 drawn onto the canvas
.
SVG path syntax
As well as being another Path2D
object - the Path2D
constructor
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-axis
coordinate is given and it's drawn
relative to the current position).
One advantage of this is that it allows you to 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. It can prove useful
in making the definition of paths more
concise and also facilitating the easy passing around of
those paths.
The Path2D object API
The Path2D
object api
shares functions with the 2D context api
and those
functions are:
closePath()
moveTo()
lineTo()
quadraticCurveTo()
bezierCurveTo()
arcTo()
arc()
rect()
ellipse()
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 can take an
svg
path
string - in which case the path consists of the path defined by the svg
path data.
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 (to test
for clicks) would
be easier. It might be easier though to use an array of letters (to represent actions) than
strings that 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 that 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.