Important: The RGraph website has recently experienced some downtime. It's now back up and available once more but keep this in mind as you may experience errors or oddities when browsing the site.

An example of canvas paths

Notice: You may be also interested in the new Path2D object

Introduction

Paths on canvas are the building blocks used to construct the shapes that you need to. A path consists of various drawing commands which can then be painted on to the canvas by using the stroke() or fill() functions. There can normally only be one "active" path in canvas - though with the addition of the Path2D object (which was added to Chrome in March 2014) this will change and you'll be able to create paths and pass them around in your programs, retain them for click testing and replay them with ease.

So currently, for hit testing, you need to retain the coordinates for the path (and possibly the required shapes) and then replay the paths to test for clicks. By not stroking or filling the path it won't get painted onto the canvas but you can still use the isPointInPath() function to test a pair of X/Y coordinates. This is currently how the Funnel chart tests for clicks and mousemove events.

Update:

You can also replay the path in full but not stroke or fill it and then use either the isPointInPath or the isPointInStroke function to see whether the mouse coordinates are over the relevant area.

The current set of path functions consists of the following:

beginPath()

This starts a new path - wiping out the current path (if any). If you're finding that in animations you're getting a connecting line from the end of you path to the beginning it may be that you haven't used the beginPath() function to start your path.

closePath()

This closes the path drawing a line from whatever the current position of the "pen" is back to the start point For example to draw a triangle you could draw two of the necessary sides and then call this method so that the third side is drawn by the closing of the path.

moveTo(x, y)

This moves the "pen" to the given X/Y coordinates without drawing a intermediate connecting line.

lineTo(x, y)

This draws a line from whatever the current coordinates of the "pen" are to those specified in the arguments.

rect(x, y, width, height)

This draws a rectangle at the X/Y coordinates that you give with the given width and height. This function differs from the fillRect() and strokeRect() functions in that those two functions paint on to the canvas immediately when called whereas the rect() function adds to the current path - and nothing gets drawn until you call stroke() or fill().

quadraticCurveTo(cpx, cpy, x, y)

This function draws a quadratic curve from the current pen position to the second two coordinates given, using the first two coordinates as the control point. There's a interactive illustration of how this function works here.

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

This function draws a bezier curve from the current pen position to the third two coordinates given, using the first two pairs of coordinates as the control points. There's a interactive illustration of how this function works here.

arc(x, y, radius, startAngle, endAngle, anticlockwise)

This function can be used to draw an arc or a full circle at the X/Y coordinates given. The radius argument is self explanatory. The startAngle and endAngle are the angles at which the arc begins and ends. Two things make note of are that the angles begin at the right hand side (ie that's where 0 is). The last argument dictates the direction to go in and is not quite so important if you're drawing a full circle but becomes much more so when you're only drawing a partial circle. And also very important to remember is that the angles are measured in RADIANS - not degrees. To convert an angle in degrees to radians you must divide it by (180 / Math.PI). Roughly - one radian is equal to 57.29 degrees - the same as (180 / 3.14). If you find that these angles are not accurate enough for you then you should use the Javascript Math.PI constant.

arcTo(x1, y1, x2, y2, radius)

The arcTo() function draws arcs similar to the arc() function and uses the current position of the "pen" as the start point then draws an arc with the given radius to [x2, y2] using [x1, y1] as a control point.

An example of the path functions

This is an example of drawing some things - a rect, a few lines and an arc - onto the canvas. Keep in mind that unless a moveTo() is performed you may get connecting lines being drawn. An example here is the line leading to the rect.

<script>
    window.onload = function ()
    {
        // Get references to the canvas and the context
        var canvas  = document.getElementById('cvs');
        var context = canvas.getContext('2d');

        // This starts a new path and in doing so clears any current path that is active. Strictly speaking it's
        // not always necessary But using the beginPath() function can prevent headaches and difficult to track bugs
        context.beginPath();

            // Start on the right hand side of the lower-left semi-circle and draw half a circle to the left side
            context.arc(150,150,50,0,Math.PI, false);
            
            // Draw a line to the coordinates [300,300]. This is actually off the bottom of the canvas so you'll only see
            // part of the line
            context.lineTo(300,300);
            
            // Move the pen to the coordinates [300,150]. Because it's a move - no connecting line is drawn
            context.moveTo(300,150);
            
            // Draw a rectangle at [5,5] that's 150 pixels wide and 150 pixels high.
            context.rect(5,5,150,150);

            // Draw another arc in the top right corner. Because the "pen" is over at the rectangle on the left
            // hand side of the canvas - a connecting line is drawn to the start point of the arc.
            context.arc(150,150,50,0,Math.PI, false);
        
        // Call the stroke() function to draw the path onto the canvas.
        context.stroke();

        // This is not part of the path that has been constructed - so is shown in red to distuinguish it
        // from the path. It's drawn using the fillRect() function so it's drawn immediately to the canvas when
        // the fillRect function is called
        context.fillStyle = 'red';
        context.fillRect(545,205,50,40);
    }
</script>

Which produces this:

[No canvas support]

Stroking and filling the path

To paint your path to the canvas you call either the stroke() function or the fill() function - or both! They do as their names suggset and the colors that are used are set via the strokeStyle and fillStyle properties. The colors can be set before the stroke/fill and as many times as you wish - but the color that's used is whatever the strokeStyle/fillStyle is when you call the stroke() or fill() functions. So if, half way through constructing the path, you set the color to be green and then just before you call stroke() you change it to red - the whole path will be colored red.

The fillRect and strokeRect functions

If you use these you need to keep in mind that they're NOT path functions. You don't need to use the beginPath() or closePath() functions with them - you can just call them to paint immediately onto the canvas.

Hit detection with the isPointInPath() function

With basic shapes like squares and rectangles it's a simple matter to test for hits by checking that the mouse coordinates are within the boundaries of the shape. However it becomes trickier (and sometimes entirely unrealistic) when the shape is complex, irregular (a parallelogram for example) or involves curves. In this case hit detection is made much simpler by employing the isPointInPath() function to do the testing for you.

This function accepts the X coordinate and Y coordinate as arguments and returns true or false as to whether the pair of coordinates are within the confines of the current path. Since there can currently only be one active path in canvas that's the one that gets tested. However, like the Funnel chart, what you can do if you have multiple shapes to test is go through them one-by-one adding the path to the canvas *but not stroking or pathing them* and then use the isPointInPath() function. There's another article about the isPointInPath() function with an example available here.

Examples

These examples are specifically simplified so that extraneous HTML is reduced.

This is an example of the isPointInPath() function used for hit detection.

These two examples are interactive and show you how the curve functions work.