Typically drawing lots of shapes involves changing the transformation and attributes that are used to draw the path. After changing things you often want to restore the original canvas state. The canvas state 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 later
all of the 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. Notice that any drawing operation that is in progress, such as the current path or current bitmap, are not part of the state.
Why are we concerned with defining the context state?
The answer is that there is a save method which saves the current state to an internal stack of states and a restore method that sets the state to the current top of stack.
So, for example, you can set a fill color and save it on the stack of states:
At this point the current fill color is blue with green and red on the stack. Hence, 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.fillRect (20, 20, 55, 50);
Repeat this another time and we draw a red rectangle:
ctx.fillRect (30, 30, 55, 50);
Notice that in this case we are only changing the fill color, but in practice the entire drawing state is saved and restored.
You can use the state stack to change the drawing state to draw a sub-object and then restore the state to continue with drawing the main object.
Active Transformations and State
There is another use for the stack of states - another overall approach to systematic drawing.
We have already met the idea of defining a path to draw a "standard" shape. You draw all of your paths starting at 0,0 and with a unit size. Then when you want a shape at x,y and size s you translate the co-ordinate system to x,y and scale by s. Of course, if you save the state before drawing and restore it after then nothing has changed and you are ready to draw the next standard shape.
Notice that you have to do the transformation in the opposite order to the one you might think and you do have to remember to specify the line width in the new units. Because of the save and restore you can draw the next standard shape using the same method.
This is a particularly useful approach to working with a library of complex shapes. For example, the space ship SVG string used earlier can be edited so that it is drawn starting at 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, any angle and any scale using:
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 then shape is drawn at 0,0. Usually you want this to be at 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 located at x,y when you do a translate to x,y and it is the point that any rotate is taken about. It is usually easy to fix the center when you hand-construct shapes - it is more difficult when you use an automatic method such as InkScape. In these cases the easiest 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 puts the center at the top of the nose.
Transformations include rotation, scaling and translation.
You can write all three as a simple matrix if you use homogeneous co-ordinates (x,y,1)
You can set the transform matrix directly or use one of the utility methods to set scaling, rotation and translation.
The order in which you apply transformations makes a difference.
You can think of transformations as actively moving something or just changing the co-ordinates. Canvas transforms change the co-ordinate system.
If you want to think “actively” then think of the transformations you want to perform on a shape and then apply them in the reverse order before drawing the shape.
One approach to organizing graphics is to draw everything centered on the origin and at unit scale and then use transformations to size, rotation and position where you really want to draw the shape.
You can change the co-ordinate system in use to anything that suits the current drawing task.
You can save the drawing state before changing the co-ordinate system so that it can be restored.
The drawing state includes the current transformation matrix, clipping region and all drawing attributes