Adobe Creative Cloud

January 26, 2016 /Code /

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.

Drawing

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)

See the Pen WrOQpK by Lucas Bebber (@lbebber) on CodePen.

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.

brush

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.

Transform

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)

See the Pen wMqMgR by Lucas Bebber (@lbebber) on CodePen.

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)

See the Pen XXaXRd by Lucas Bebber (@lbebber) on CodePen.

That happens because:

  • The rotate transform 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.

See the Pen MKovdp by Lucas Bebber (@lbebber) on CodePen.

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.

See the Pen xZLwvV by Lucas Bebber (@lbebber) on CodePen.

So, our previous example would look like this:

ctx.translate(100,0)
ctx.rotate(Math.PI/4)
ctx.fillRect(0,0,100,100)

See the Pen eJELKJ by Lucas Bebber (@lbebber) on CodePen.

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 translate function.

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)

See the Pen LGjJXO by Lucas Bebber (@lbebber) on CodePen.

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 and Restore

The 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 restore().

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)

See the Pen xZLyGP by Lucas Bebber (@lbebber) on CodePen.

It’s important to note that calling save multiple times adds the current state to a stack, rather than overwriting the previous save. Likewise, restore gets the state that is on the top of that stack, so calling it multiple times lets you restore from previous save calls.

<hr>

Going back to transforms, the third transform we have is scale. It takes two arguments, x and 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 restore.

See the Pen GovYWa by Lucas Bebber (@lbebber) on CodePen.

Opacity and Blending

To set the opacity of subsequent drawings, you can use the globalAlpha property:

ctx.globalAlpha=0.5

See the Pen MKEgpJ by Lucas Bebber (@lbebber) on CodePen.

Alternately, you can set the fillStyle and strokeStyle properties with rgba, like in CSS:

ctx.fillStyle='rgba(128,128,128,0.5)'

Canvas also supports color blending and compositing operations, through the globalCompositeOperation property.

ctx.globalCompositeOperation='screen'

See the Pen pgWzdN by Lucas Bebber (@lbebber) on CodePen.

The complete list of all the globalCompositeOperation available can be seen here.

What is interesting is that besides blending, composite operations can be used for things like clipping and masking.

See the Pen zrEYrm by Lucas Bebber (@lbebber) on CodePen.

Note that the default composite operation is source-over (rather than normal or something similar), so besides using save and restore, that’s how you can reset it.

Code