Introduction to Canvas – Part II
This article is the second part to Introduction to Canvas, so I recommend reading it if you haven’t yet.
In this article, we’ll see a bit more of how canvas works and how to do transforms and blending.
To recap, this is the basic usage of the canvas object:
<!-- Create the canvas object, with defined width and height --> <canvas id="container" width="640" height="480"></canvas>
// Get the canvas object var canvas = document.querySelector('#container') // Get the rendering context, which we will use to draw our graphics var ctx = canvas.getContext('2d') // Set the fill and stroke colors ctx.fillStyle = '#39f' ctx.strokeStyle = '#016' ctx.lineWidth=4 // Draw ctx.fillRect(100,100,200,200) ctx.strokeRect(100,100,200,200)
Now, an important concept to grasp about canvas is that you don’t set properties, like colors, to objects – you set them to the context, and everything that is drawn afterwards will have that property.
You can see this pattern all over the canvas object – it applies not only to colors, but also to transforms, opacity, etc. Let’s look into a few examples.
To rotate objects, use the
rotate function before drawing:
ctx.rotate(Math.PI/4) // angle in radians - equals to 45 degrees ctx.fillRect(0,0,100,100)
Since the square is getting clipped at the left, we could try moving it a little to the right, but we’ll quickly notice the square isn’t exactly in the position we would expect:
ctx.rotate(Math.PI/4) //angle in radians ctx.fillRect(100,0,100,100)
That happens because:
rotatetransform is applied to all coordinates, including positioning.
- It’s rotating around the default origin point, which is the top left corner of the canvas, instead of being relative to the object.
To get around that we’ll have to use another transform function,
translate, which is used to move objets around. We can use it to set the origin point.
So, our previous example would look like this:
ctx.translate(100,0) ctx.rotate(Math.PI/4) ctx.fillRect(0,0,100,100)
Note that we have to call
translate before rotating, otherwise the translating will be rotated as well.
Also note that we are drawing the square at the position
0,0. That’s because the square’s position is relative to the transform, that is, it’s relative to the position we set with the
Now, what happens if we want to draw an upright shape after we have drawn a rotated one?
ctx.fillStyle='#39f' ctx.translate(100,100) ctx.rotate(Math.PI/4) ctx.fillRect(-50,-50,100,100) ctx.fillStyle='#FAF' ctx.fillRect(-20,-20,100,100)
Of course, the next shape is rotated as well. A possible fix could be simply rotating to the inverse of the angle we have rotated to before. However, that requires keeping track of the previous rotation, and that can get complicated when we have many objects. Transforms are relative, meaning that calling
rotate(0) doesn’t reset the rotation, but rather adds 0 to the current rotation.
Thankfully, canvas does have an easier way for dealing with that.
save function saves the current canvas state – meaning the current transform, colors, etc. – and
restore gets the last saved state.
They are pretty simple to use – just call
save() before rotating, then when you want to go back to the default, call
ctx.translate(100,100) ctx.save() ctx.rotate(Math.PI/4) ctx.fillRect(-50,-50,100,100) ctx.restore() ctx.fillStyle='#FAF' ctx.fillRect(-50,-50,100,100)
It’s important to note that calling
save multiple times adds the current state to a stack, rather than overwriting the previous
restore gets the state that is on the top of that stack, so calling it multiple times lets you restore from previous
Going back to transforms, the third transform we have is
scale. It takes two arguments,
y scale multipliers. The function otherwise works the same way as the other transform functions – it’s relative rather than absolute, you should use
translate to set the transform origin, and you should
save before you scale if you want to reset the scaling later with
To set the opacity of subsequent drawings, you can use the
Alternately, you can set the
strokeStyle properties with
rgba, like in CSS:
Canvas also supports color blending and compositing operations, through the
What is interesting is that besides blending, composite operations can be used for things like clipping and masking.
Note that the default composite operation is
source-over (rather than
normal or something similar), so besides using
restore, that’s how you can reset it.