Remember that we've used two canvases for this visual, one canvas to draw the static background scene with the map and the airports and one canvas for the dynamic flight animation. We did this because it kept drawing concerns separate.
Another reason for using an additional canvas is increased performance. We can use one canvas as an in-memory buffer to pre-render elements and just copy its contents onto the main visible canvas. This saves render costs as drawing on a visible canvas is less performant than drawing on a non-visible canvas to then copy over the image to the main canvas. Performance further rejoices as the context's drawImage() method we will use to copy over the image from the buffer to the display canvas, is hardware accelerated (meaning it uses the parallel processing powers of the GPU) by default.
For our little app, the animated elements are the plane circles. Instead of drawing them with the drawPlane() function for each update, we can first create a single image of a circle on a small buffer canvas and then use drawImage() to port it over to the canvasPlane.
We create a single plane image in global scope:
function createPlaneImage() {
var planeImg = document.createElement('canvas');
planeImg.width = planeImg.height = 2;
var contextPlaneImg = planeImg.getContext('2d');
contextPlaneImg.beginPath();
contextPlaneImg.fillStyle = 'tomato';
contextPlaneImg.arc(planeImg.width/2, planeImg.height/2, 1, 0,
2*Math.PI);
contextPlaneImg.fill();
return planeImg;
}
We create our buffer canvas called planeImg in thin air, set its width and height to 2 (double the plane's desired radius of 1), and retrieve its context. We'll draw a tomato colored circle on it before we return it.
We call this function once when initializing the planes object and store it as an image in the planes object:
var planes = {
items: [],
icon: createPlaneImage(),
getPlane: function(planeRoute) {
// ...
Finally, we just have to remove our drawPlane() function we used to draw the circle on every update. Instead, we add a new function called drawPlaneImage() to the planes object that uses drawImage() to add our plane icon (the circle) to the context we determine:
drawPlaneImage: function(ctx, x, y) {
ctx.drawImage(this.icon, x, y);
}
Lastly, we don't call drawImage() in the animate() function, but drawPlaneImage():
function animate() {
planes.clearPlanes(contextPlane);
planes.items.forEach(function(el) {
planes.updatePlane(el);
planes.drawPlaneImage(contextPlane, el.x, el.y);
});
requestID = requestAnimationFrame(animate);
}