Page 1 of 3
Now available as a paperback or ebook from Amazon.
- Getting Started With Canvas
- Drawing Paths
- Stroke and Fill
- Clipping, Compositing and Effects
- Generating Bitmaps
- WebWorkers & OffscreenCanvas
Extract 1: OffscreenCanvas
- Typed Arrays
- Files, blobs, URLs & Fetch
- Image Processing
Extract 1:ImageData **NEW!
- 3D WebGL
- 2D WebGL
A Programmer's Guide to Canvas
Getting Started With SVG
Getting started with WebGL
Even if you can make your animation run fast enough, you still have the problem of the pauses that occur whenever the UI thread has to deal with something else. There are situations when your animation can freeze for a considerable time. The solution is to move the animation from the UI thread to a different thread.
It is worth making clear that while OffscreenCanvas has been introduced as something to make animation smoother, almost any intensive graphics operation is better implemented as a web worker. Indeed any intensive operation of any kind is best implemented in this way.
Before we look at how to use OffscreenCanvas we need to find out the basics of using a web worker. This is explained in detail in the chapter but here is a summary:
Communication between the UI and worker thread is via events fired by one thread and received by the other.
Each thread only processes events when not occupied with running code.
This means that events may not be dealt with promptly.
Data can be transferred between threads using the event object that is made available to the event handler.
Data is not shared – a copy is made for the receiving thread.
The UI thread is generally set up to respond to events promptly, but the worker thread isn’t.
A graphics-oriented worker thread is the exception to the rule as it generally gives up its thread with each call to requestAnimationFrame.
Sections in book but not in this extract:
- Basic Web Worker
- The Trouble With Threads
- Basic Communication Methods
- UI Thread to Worker Thread
- Worker Thread to UI Thread
- Transferable Objects
An OffscreenCanvas is a canvas object that isn’t part of the screen display. It is simply an area of memory that you can draw into using all of the familiar methods. The fact that it isn’t part of the display, and isn’t part of the DOM, means that it can be used from the UI thread and from a worker thread. When used from the UI thread it behaves a lot like a canvas object that you haven’t added to the DOM, but it also has some additional methods.
You create an OffscreenCanvas object using:
var offCanvas=new OffscreenCanvas(width,height);
Notice that as this is not part of the DOM it doesn’t have a style width and height like a canvas object. It does have width and height properties, however. It also has a getContext method that you can use to draw on it and a pair of new methods:
convertToBlob() converts the image to a binary blob using the format of any of the supported graphics file types – jpg, png. For lossy compression you can also specify a quality parameter.
transferToImageBitmap() returns an ImageBitmap object based on the current contents of the OffscreenCanvas.
OffscreenCanvas in the UI Thread
As already mentioned, you can use an OffscreenCanvas wherever you would otherwise use a canvas object not added to the DOM and, as long as you are only targeting browsers that support OffscreenCanvas, it is sensible to use it instead. However, at the time of writing only Chrome supports it in the 2d context.
For example, the bitmap for the ball in the example at the end of the previous chapter can be created using, in the main program;
var ctx2 = new OffscreenCanvas(40, 40).getContext("2d");
var path = new Path2D();
var r = 20;
path.arc(20, 20, r, 0, 2 * Math.PI);
And to make this visible you simply use, in render:
ctx.drawImage(ctx2.canvas, this.pos.x - r,
this.pos.y – r);
which is exactly how you would do it with a standard canvas object.
You can see the complete modified program at www.iopress.info.
Alternatively you could create an ImageBitmap and use it in place of the canvas:
You would then render the ball using:
ctx.drawImage(ballImage, this.pos.x - r, this.pos.y – r);
which is in principle faster.