JavaScript Canvas - Bezier Curves
Written by Ian Elliot   
Monday, 16 November 2020
Article Index
JavaScript Canvas - Bezier Curves
String Paths
InkScape

Bezier and String Paths

We have already discovered that you can specify both sorts of Bezier curve using a string path:

  • Qx1 y1 x y = quadraticCurveTo(x1.y1,x,y)

  • Cx1 y1 x2 y2 x y = bezierCurveTo(x1,y1,x2,y2,x,y)

There are two additional commands that sometimes make things easier.

  • Tx y = smooth quadratic Bézier curveto

  • Sx2 y2 x y = smooth curveto

 

In each case these commands are to be used after the usual quadratic or cubic commands and their purpose is to continue the curve smoothly. If you use them following any other command then the missing control point is taken to be the current position.

What does smooth mean in the case of a quadratic Bezier? The answer is that the control point for the second part of the curve has to be on a line through the first control point and the end point. The reason is simply that the curve has to be a tangent to this line as it approaches the end point and the only way it can be continued in a smooth way is if it continues to be at a tangent to the same line as it leaves the first end point.

bezier10
You can see that the only degree of freedom you have in the curve, if it is to be smooth, is the position of the final end point. The curve continues in the same direction through the first end point, but then smoothly turns towards the final end point.

For example:

path1=new Path2D("M50 100 Q75 50 100 100 T200 50");

produces:

bezier11
The command:

Sx2 y2 x y

continues a cubic Bezier curve in the same way as T and you only specify the second control point and the end point. In this case the control point inferred to make the curve smooth is on the line that connects the second control point to the first end point and at an equal distance from that end point. As the curve has to be tangent to this line as it approaches and leaves the first end point, you can see that this does give a smooth curve. Notice that now we have more control over how the curve is continued because we have the final end point and its control point.

 bezier12


 For example:

path1=new Path2D("M50 100 C100 50 150 200 200 100 S250 50 300 100");

produces:

bezier17 Creating Bezier Paths

Bezier paths are powerful but they are difficult to draw by just programming. In most cases if you want a circle or an ellipse, or even a simple shape such as an arrow, you can put together the commands needed to create it. When it comes to more complex shapes then you need help. There are a number of different ways of obtaining or creating Bezier paths.

The first is to use one of the many SVG icons that are available for free on the web. For example the following is a search icon and this is the path tag that creates it.

<path d="M31.008 27.231l-7.58-6.447c-0.784-0.705-1.622-1.029-2.299-0.998 1.789-2.096 2.87-4.815 2.87-7.787 0-6.627-5.373-12-12-12s-12 5.373-12 12 5.373 12 12 12c2.972 0 5.691-1.081 7.787-2.87-0.031 0.677 0.293 1.515 0.998 2.299l6.447 7.58c1.104 1.226 2.907 1.33 4.007 0.23s0.997-2.903-0.23-4.007zM12 20c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8z"></path>

To use this you simply put the path drawing commands into a string and create the path:

path1=new Path2D("M31.008 27.231l-7.58-6.447c-0.784-0.705-1.622-1.029-2.299-0.998 1.789-2.096 2.87-4.815 2.87-7.787 0-6.627-5.373-12-12-12s-12 5.373-12 12 5.373 12 12 12c2.972 0 5.691-1.081 7.787-2.87-0.031 0.677 0.293 1.515 0.998 2.299l6.447 7.58c1.104 1.226 2.907 1.33 4.007 0.23s0.997-2.903-0.23-4.007zM12 20c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8z"); 

which produces:

bezier13

The only real problem that can occur is that the path uses absolute positioning. In the example above you can see that there is an M31.008 27.231 at the start and an M12 20 towards the end. The first is easy to deal with as it can simply be removed as the rest of the instructions will then be relative to the current position. The second is slightly more difficult. It follows a z command which closes the first curve and moves the current position back to the start, i.e. 31.008, 27.231. Relative to this the point 12,20 is at -19.008 -7.23. So the second move command can be made relative.

The complete string is:

path1=new Path2D("M100 100 l-7.58-6.447c-0.784-0.705-1.622-1.029-2.299-0.998 1.789-2.096 2.87-4.815 2.87-7.787 0-6.627-5.373-12-12-12s-12 5.373-12 12 5.373 12 12 12c2.972 0 5.691-1.081 7.787-2.87-0.031 0.677 0.293 1.515 0.998 2.299l6.447 7.58c1.104 1.226 2.907 1.33 4.007 0.23s0.997-2.903-0.23-4.007zm-19.008 -7.23c-4.418 0-8-3.582-8-8s3.582-8 8-8 8 3.582 8 8-3.582 8-8 8z"); 

where the absolute move has been added to the start to draw the icon at 100,100. Having a completely relative path allows you to set the position of the entire path very easily.



Last Updated ( Monday, 16 November 2020 )