Click here to Skip to main content
Click here to Skip to main content

Draw Smooth Line through points with HTML5 Canvas

, 26 Aug 2013
Rate this:
Please Sign up or sign in to vote.
Cardinal splines at your service - no control points needed

Note: Version 2 and onward is maintained at Github.

Introduction

Canvas comes with a few functions to draw smooth lines such as Bezier-curves. However, sometimes we just need to draw smooth lines between and through a few points without worrying about control points, or calculating pseudo control points by means of trigonometric approaches.

Here is a simple function that allows you to feed it a simple array of X and Y coordinates and it will draw smooth lines (cardinal spline) between them.

Using the Code

Obvious first step: reference the file in your HTML code or merge it with the existing JavaScript code. The usage is straight forward. Define a simple array with X and Y coordinates in succession.

Then call the function with the canvas context and array as argument. Optionally, you can set a tension value between 0 and 1 (if none is given, it uses the default 0.5).

var myPoints = [1,2, 10,20, 30,40 ]; //here 3 points: x1,y2, x2,y2, x3,y3

drawCurve(ctx, myPoints);

The function comes with a set of options which may or may not be useful - the full function looks like this:

drawCurve(context, points, [tension], [isClosed], [numOfSegments], [showPoints])  
  • context - the canvas' 2D context which you allocate yourself from the canvas element.
  • points - the array of points, minimum two points. The array is cloned internally as it needs to duplicate the start and end points which otherwise would alter the input array (feel free to modify if you need to squeeze a few more milliseconds out of it).
  • tension - (optional) typically a value between 0 - 1, default value is 0.5.
  • isClosed - (optional) if you want a closed loop. Default is open curve. Please note that in the current version, one junction point will not be smoothed (haven't wrapped my head around this one - suggestions are welcome!). NOTE: From version 1.3 this option is obsolete as I have found no solution to this problem yet. The argument is still present but nothing will happen using it.
  • numOfSegments - (optional) segments between each set of points, or the line resolution if you like. Defaults to 16. Good initial range is between 10-16, 20-25 for long curves/lines.
  • showPoints - (optional) draw a small square where each actual points are. Mainly for debugging purposes. Default is false.

From version 1,1, the main function is split into three functions: the drawCurve() works as before. Use the getCurvePoints() to get an array with points for a smoothed line based on the input points, Use drawLines() to draw any array with x,y points.

getCurvePoints(points, tension, isClosed, numberOfSegments)   

As you can see, it follows the same pattern as the drawCurve() with the exception of context and showPoints. This function can therefore be useful outside a canvas context as well. All the math is now in this function and is used internally by drawCurve().

The third function is the simple drawLines() function which takes an array of points and draws lines between them:

drawLines(context, points) 
  • context - the canvas' 2D context which you allocate yourself from the canvas element.
  • points - the array of points, minimum two points. This can be any array of points in the arrangement x1,y1,x2,y2,...xn,yn.

This change allows you to easily convert a point array into an array consisting of points for a smooth curve without rendering anything, which of course can be used for anything including merging lines.

Extending the Canvas (Optional)

You can easily extend the canvas context if you prefer to call the drawCurve on the context object instead.

In order to extend it, add these few lines to the JavaScript (included from version 1.3):

if (CanvasRenderingContext2D != 'undefined') {
    CanvasRenderingContext2D.prototype.drawCurve = 
        function(pts, tension, isClosed, numOfSegments, showPoints) {
       drawCurve(this, pts, tension, isClosed, numOfSegments, showPoints)}
} 

Now you can call the function like this instead.

context.drawCurve(myPoints);

Under the Hood

This is a "native" function in the sense that all point and line calculations are done within the function and not by using other canvas curve functions such as the Bezier curves.

The function segments the distance between two points. For each segment, it uses the points plus one point before and one after to calculate the cardinal points used for interpolation.

The number of segments determines the "resolution" of the curve. The less segments there are, the more crude the curve, and of course, the more the merrier. However, this is useful only up to a certain point where we won't be able see much difference in smoothness. From experience, a value between 10 and 16 segments is a good starting point for typical uses. If you have great distance between two points, then you will probably need more segments as the number of segments are the same for all lines no matter length.

The tension value determines how "round" the connection between the lines will be drawn. In essence, a value of zero is the same as drawing straight lines between the points. A value of 1 will draw them very rounded. Typically a value of 0.5 is most usable as this gives a smooth curve without exaggerated curves.

You can always go outside the 0-1 interval to get curly junctions.

The smoothed lines are guaranteed to go through the actual points.

Demo

The archive attached to the tip comes with a demo page which allows you to play around with different segment and tension values.

There are two versions of the function included, one developer version and one minimized version for production use. There are no dependencies other than that it obviously requires a HTML5 canvas (you can use excanvas with IE8 or lower versions) and JavaScript enabled.


History

Version 1.3

  • More optimizations for even higher speeds
  • Corrected mangled power calculations as a result of former optimization (1.2)
  • Removed support for closed curves (for now) - no change in order of arguments

Version 1.2

  • Highly optimized code for even faster rendering
  • Additional file that extends Canvas to have context.curve(points, tension, segments) The extension version comes in an even more optimized state due to be all inline

Version 1.1

  • Split main function into three functions which offer more versatility
  • A couple of performance optimizations (both demo and function)

License

This article, along with any associated source code and files, is licensed under The MIT License

About the Author

Ken Fyrstenberg

United States United States
Binary since 1982 from the age of the Vics, guru meditations and 01000100k asm, to .Net and web apps.

Comments and Discussions

 
QuestionI voted 5! PinmemberKashif_Imran19-Nov-13 11:50 
AnswerRe: I voted 5! PingroupKen Fyrstenberg Nilsen19-Nov-13 15:50 
QuestionNot an article PinmemberKarthik Harve14-Mar-13 21:31 
AnswerMessage Removed PingroupAbdias Software14-Mar-13 23:13 
GeneralRe: Not an article PinmemberKarthik Harve14-Mar-13 23:20 
AnswerRe: Not an article PinmemberSoMad15-Mar-13 18:34 
GeneralRe: Not an article [modified] PingroupAbdias Software16-Mar-13 10:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140709.1 | Last Updated 26 Aug 2013
Article Copyright 2013 by Ken Fyrstenberg
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid