Now available in paperback or ebook from Amazon.
- Getting started with Canvas
- Plot paths
Extract: Basic paths
Extract: BÃ©zier curves
- Outline and fill
Extract: Line properties
Extract: Filling and holes
Extract: Personalized contact details
Extract the state of the graphics **NEW!
Extract: Text, Typography & SVG
- Cutting, composition and effects
Extract: Clipping & Basic Compositing
- Generating bitmaps
Extract: Introduction to bitmaps
- WebWorkers & OffscreenCanvas
Extract: Bit manipulation
- Typed arrays
- Files, blobs, urls and fetch
Extract: Blobs and files
Extract: Read / write local files
- Image processing
Extract: The Filter API
- WebGL 3D
- 2D WebGL
Extract: WebGL Convolutions
In the chapter but not in this excerpt
Stack of States
In general, drawing many shapes involves changing the transformation and attributes used to draw the path. After modifying elements, you often want to restore the state of the original canvas. The state of the canvas is a set of properties that determines how graphics primitives will be drawn.
The state consists of:
the current transformation matrix
the current clipping region – see below
all drawing attributes such as fillStyle, lineWidth and so on.
In short, it includes everything that determines what the result of a drawing operation actually produces. Note that any current drawing operation, such as the current path or the current bitmap, is not part of the report.
Why are we concerned with defining the state of the context?
The answer is that there is a backup method that saves the current state to an internal state stack and a restore method that sets the state to the current top of the stack.
So, for example, you can define a fill color and save it to the report stack:
ctx.fillStyle = "rgb(200,0,0)"; ctx.save(); ctx.fillStyle = "rgb(0,200,0)"; ctx.save(); ctx.fillStyle = "rgb(0,0,200)"; ctx.fillRect (10, 10, 55, 50);
At this point, the current fill color is blue with green and red on the stack. Therefore, we have just drawn a blue rectangle. If we now restore from the top of the stack and draw a rectangle, it will be green:
ctx.restore(); ctx.fillRect (20, 20, 55, 50);
Repeat this one more time and we draw a red rectangle:
ctx.restore(); ctx.fillRect (30, 30, 55, 50);
Note that in this case we are only changing the fill color, but in practice the entire state of the drawing is saved and restored.
You can use the state stack to change the state of the drawing to draw a sub-object, and then restore the state to continue drawing the main object.
Active transformations and state
There is another use of the state stack – another holistic approach to systematic drawing.
We have already encountered the idea of ââdefining a path to draw a “standard” shape. You draw all your paths starting from 0.0 and with a unit size. Then when you want a shape at x, y, and size s, you translate the coordinate system to x, y and the scale to s. Of course, if you save the state before drawing and restore it after, nothing has changed and you are ready to draw the next standard shape.
For example, if you define a unit square:
var path1 = new Path2D(); path1.rect(0,0,1,1);
you can draw it at 100 200 and size 400 using:
ctx.save(); ctx.translate(100,200); ctx.scale(400,400); ctx.lineWidth=1/400;Â Â Â ctx.stroke(path1); ctx.restore();
Note that you must perform the transformation in the reverse order of what you might think and you must remember to specify the row width in the new units. Due to backup and restore, you can draw the next standard shape using the same method.
This is a particularly useful approach for working with a library of complex shapes. For example, the spaceship SVG string used previously can be changed to be drawn from 0.0:
path1=new Path2D( "m 0,0 c 2.891926,-25.77092 -1.958475,
-65.0136 -13.037221,-73.92172 -11.072474,
8.90817 -17.526591,48.1508 -14.634563,
73.92172 -9.479011,8.46903 -9.015897,
17.40218 -9.068381,29.71617 l 5.92022,
-0.074 c 0,0 2.493141,-15.15787 5.105513,
-16.98251 l 3.15542,8.06751 -1.537022,
9.15649 c 13.277647,-0.17974 7.242537,
-0.17974 20.52018,0 l -1.537022,-9.15649 3.15552,
-8.06751 c 2.979241,2.08093 3.605942,
17.00168 3.605942,17.00168 l 6.61799,0.0548 c -0.0526,
-12.31399 1.21238,-21.24714 -8.266576,-29.71617 z");
It can now be drawn at any position, angle, and scale using:
ctx.save(); ctx.translate(200,200); ctx.rotate(Math.PI/8); ctx.scale(2,2); ctx.lineWidth=1/2; ctx.stroke(path1); ctx.restore();
The only problem with this is defining the “center” of the shape. The center is the point of the shape that is located at 0.0 when the shape is drawn at 0.0. Usually you want it to be in a “natural” position – the center of the rocket or the tip of the nose. The reason is that the pixel at 0.0 when the shape is drawn is the one at x, y when you translate to x, y and this is the point around which any rotation is done. It is generally easy to fix the center when building shapes by hand – it is more difficult when using an automatic method such as InkScape. In these cases, the simplest solution is to find an initial offset that places the center where you want it when the shape is drawn at 0, 0. For the spaceship, changing the initial m command to m 13.5,74.5 places the center at the top of the nose.
Transformations include rotate, scale, and translate.
You can write all three as a simple matrix if you use homogeneous coordinates (x, y, 1)
You can set the transformation matrix directly or use one of the utility methods to set the scale, rotate, and translate.
The order in which you apply the transformations makes a difference.
You can think of transformations as actively moving something or just changing coordinates. Canvas transformations change the coordinate system.
If you want to think “actively,” think about what transformations you want to perform on a shape, and then apply them in reverse order before you draw the shape.
One approach to organizing graphics is to draw everything centered at the origin and scale to unity, then use transformations for the size, rotation, and position where you really want to draw the shape.
You can change the coordinate system used to whatever suits the current drawing task.
You can save the state of the drawing before changing the coordinate system so that it can be restored.
Drawing state includes current transformation matrix, clipping region, and all drawing attributes
or send your comment to: [email protected]