Enhance your media with Canvas Over technique

13.10.2016

Web is full of images. They serve as background, supplement our content or are put in the center of attention. A well-known technique for making media more engaging, especially in the background, is parallax scrolling. It adds some joy to the experience through spectacular motion. In this article, I’m going to explore another trick for enhancing pictures on the web, which I call Canvas Over.

The idea is very simple: we put <canvas> element on top of <img> and draw on it. How it’s supposed to improve the experience? Let’s dive into examples!

Example #1: Night sky

Screenshot of "Night sky" example of Canvas Over technique

Here we have a landing/intro page for astronomical observatory. A nice, full-page photo of the building is used in the background, and we’d like to add a subtle animation to it, say lighting stars. We could do it using animated GIF or video, but these techniques have some drawbacks:

Instead, we can draw the stars directly over our image using JavaScript. We start by adding canvas element to markup:

1
2
3
4
5
6
<body>
  <canvas id="canvas"></canvas>
  <header>
    <!-- Content goes here -->
  </header>
</body>

In this example, page background is set on <body> element with CSS, and the <header> contains all the content. We haven’t added width and height attributes to canvas, because they’re going to be set with JS to match page dimensions:

1
2
3
4
5
6
7
8
9
10
11
12
// Cache page dimensions
var bodyWidth = $('body').width(),
    bodyHeight = $('body').height();

// Stretch canvas
$('#canvas').attr({
  width: bodyWidth,
  height: bodyHeight
});

// Get canvas context
var ctx = $('#canvas')[0].getContext('2d');

Since both child elements in our template are absolutely positioned, the body gets 0 height, which affects our calculated canvas dimensions. Let’s fix this in CSS:

1
2
3
4
body {
  /* ... */
  height: 100vh;
}

A note on positioning: if you plan to have some content over the canvas (like we do), you need to watch the stacking order so this content can appear on top of the canvas. Usually it’s done simply by the right placement of elements in the markup (last in source, top on the screen), but z-index can help, too.

Now let’s add the mentioned stars to our page. We’re going to push pixels of random shade of grey…

1
2
3
4
5
6
7
// Random grey generator
function getRandomGrey() {
  var shade = Math.floor(Math.random() * 101) + 70,
      grey = '#' + shade.toString(16).repeat(3);

  return grey;
}

…to the random position on our canvas…

1
2
3
4
5
6
7
8
9
// Place star on the canvas
function putStar() {
  var starX = Math.random() * bodyWidth,
      starY = Math.random() * bodyHeight / 3,
      starColor = getRandomGrey();

  ctx.fillStyle = starColor;
  ctx.fillRect(starX, starY, 2, 2);
}

…in a simple loop:

1
2
3
4
5
6
7
// Draw stars in a loop
var starCount = 0;
function drawStar() {
  putStar();
  starCount++ >= 100 || setTimeout(drawStar, 10);
}
drawStar();

Notice how I set the starY variable in putStar() function - by dividing body height by 3, we assure that stars will appear in the top 3rd of the screen, so they will be drawn only on the sky, not building.

I’m keeping this case simple, but it’s also possible to support more complicated shapes. For example, we could trace contours of the observatory with Canvas path and, in every call to drawStar() function, check if the intended star falls into the shape using isPointInPath() method. This way, we could decide whether to draw the star or shuffle a new position.

Have a look at the final effect:

See the pen on CodePen.

With a few lines of code, we added pretty nice and unusual effect to our site. A short script is obviously smaller than video or GIF, and our decoration will always look crisp at all screen resolutions.

I like how easy it is to modify the outcome. Amount or lightness of stars can be adjusted by changing single value. With some extra code, we could make them blink gently or move across the sky.

Example #2: Concerned young adult

In the previous example, we used Canvas Over technique to make our background nicer. What could we do to improve some actual content, rather than decorating? I hope the following example will give you some idea about the possibilities.

Screenshot of "Concerned young adult" example of Canvas Over technique"

We have an article about some made-up social topic. It’s illustrated with single photography of concerned man. While the picture is sufficient companion to the text, it doesn’t feel as engaging as it could.

We’re going to put an animated list of questions in the empty space to the left of subject’s face. For this, we’ll of course use Canvas API.

This time I’ll skip the coding part and present you the result:

See the pen on CodePen.

Look how the extra text on the picture supplements the article topic. Movement grabs the reader’s attention, just like leveraging color, size and position of elements on screen.

Again, I’d like to highlight the flexibility of this solution. Imagine the similar picture implemented as video. What would happen if the journalist wanted some changes to the piece, for example add another question or get the font larger? At every such request, the video editor person would need to open the project, make the changes and render the file. It might quickly get frustrating, especially in busy news office.

In the browser, our animation renders from the code, and any changes are applied as soon as our journalist edits simple script (please feel free to play with it on CodePen):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Config
var cfg = {
  textStyle: '14px sans-serif',
  textColor: '#fff',
  lineSpacing: 30,
  positionX: 30,
  positionY: 40,
  moveDuration: 500,
  moveRepeat: 1000
};

// Doubts list
var doubts = [
  'Rent or own a house?',
  /* ... */
];

The JS way allows to try different options in the realtime to pick the best look. Also, in case we have multiple versions of enhanced media, comparing plain text files side by side is easier than diff-ing images. Talking about version control, I wouldn’t expect content writers to use Git, but many CMSes save edit history - if script body is updated along with the article, it’s trivial to bring back an older variant of animation or add more questions after our topic research is completed.

Canvas Over may shine especially in long-form stories, where images are important parts of the tale. I’m thinking about:

Summary

Let’s recap all the pros from both examples of Canvas Over:

The described technique is all about bringing more life and engagement to the web media. I hope this article aroused your imagination and some actual usages will be seen on the internet.