tl;dr – If you want responsive canvases to be a part of your RWD website, use scrawl-canvas.
The Responsive Web Design (RWD) Wikipedia article defines RWD as: … an approach to web design aimed at crafting sites to provide an optimal viewing and interaction experience – easy reading and navigation with a minimum of resizing, panning, and scrolling – across a wide range of devices […] A site designed with RWD adapts the layout to the viewing environment by using fluid, proportion-based grids, flexible images, and CSS3 media queries.
The key phrase for this discussion is “flexible images”. Translated, this means that:
- images should display cleanly, within the bounds set for them, and scale to fit into the browser (or grid) view appropriately; and
- image filesize should be appropriate for the device consuming them – smaller files should be served to handheld devices, compared to the image files required to display on large screens.
HTML5 introduced the new <picture> element to meet the requirements of RWD flexible images. However this new element doesn’t (yet) solve the problem for images used as part of a <canvas> element’s display; by definition, canvas elements can only source their image inputs from <img>, <video> and other <canvas> elements.
A further problem is that CSS treats <canvas> elements in much the same way as it treats <img> elements – setting the width and height of a canvas via CSS can distort the canvas’s output. Canvas dimensions are set directly as attributes on the element (measured in px), and are not the same as CSS block dimensions which, for the purposes of RWD, should ideally be set in proportional units.
Finally, programming the interactions with a <canvas> element – by mouse or by touch – can be difficult at the best of times. But when trying to do this with a “flexible canvas” the task of converting mouse/touch coordinates (measured in px) into the correct position on a canvas can quickly turn into a nightmare.
Implementing a responsive canvas in a web page
If we want to implement a responsive canvas in a page layout we will have to tackle all the above issues, which we can break down as follows:
- Make the <canvas> element “flexible”
- Keep the <canvas> dimensions in proportion to prevent display distortions
- Use “flexible images” to reduce page weight and download times
- Accurately position graphics and text on the canvas, whatever its current dimensions
- Interact with the canvas, using both mouse and touch, accounting for its current dimensions
Make the <canvas> element “flexible”
There’s no getting away from the fact that if we want to resize the canvas, then we have to do it programmatically. Setting the canvas width and height values via CSS will change its visible size, but not its drawing dimensions. The output will rarely be crisp, especially if a canvas with small drawing dimensions is forced to cover a much larger visual area.
This approach has the added benefit of being able to integrate the canvas with CSS media queries. However care should be taken when applying padding, border and margin values to the container: ideally these should all be set to zero, to minimize confusion when extracting its current dimensions. Such styling could instead be applied to an outer container.
Keep the <canvas> dimensions in proportion
Scaling a canvas in line with its container is all well and good, but what happens if the container width halves while its height trebles? Most likely we will end up with a poorly positioned, or distorted, canvas scene. This could occur if the canvas is part of a fluid grid system where each cell in a row has equal height.
Use “flexible images” to reduce download times
If we are going to use images in our canvas, then we need to use them “flexibly”. But as we saw above, we cannot (yet) make use of the inherently flexible <picture> element as our image source.
Fortunately most modern browsers now allow <img> elements to use the picture element’s srcset and sizes attributes. This allows us to specify a range of images which the browser can choose from, and outline parameters to help the browser choose the most appropriate image for its current display. Finally the src attribute can be used to specify the default image for browsers that do not support the new attributes.
The 2d Canvas API is, for once, kind to us when dealing with flexible images. The API doesn’t care about choosing an appropriate file to download. It cares only that the <img> element downloads a file, and uses that data as its image source.
There is one gotcha to watch out for with this approach. Certain browsers (Chrome, I’m looking at you!) have taken the functionality further: if the environment in which the image displays changes significantly, then the browser will dynamically download and display a more appropriate image file. This can have unexpected consequences for an unwary canvas implementation.
Accurately position graphics and text on the canvas
On the face of it, maintaining an accurate representation of a canvas display would seem to be one of the most difficult issues to solve. Canvas composition relies on lines, shapes, texts and images being absolutely positioned using pixel coordinates – and pixel coordinates, by definition, don’t scale.
Canvas drawing now become another 2-step operation. First, draw everything onto the hidden canvas. Then, when this has completed, copy the invisible canvas display over to the visible <canvas>. Job done!
Interact with the canvas, using both mouse and touch
RWD assumes nothing about the device on which a web page will be displayed, thus it is up to us to add functionality for handling user interaction with the <canvas> element from both mouse and touch events. In an ideal world every browser would understand and use pointer events. We do not live in an ideal world; it is our job to make sure our responsive canvas can handle input from a variety of sources.
Just as important – possibly more important: don’t forget accessibility issues. Can a user interact with your canvas using the keyboard? Have you included descriptions alongside the canvas scene to explain its purpose and usability to those not using visual browser technology? Web designers and developers have a legal duty to make their sites accessible!
Whichever method we use to gather information on the coordinates of a user click or touch, this information will only relate to the visible <canvas> element. If (as recommended) we are using a hidden fragment canvas for most of our scene composition and interaction then those coordinates will need to be converted. For x coordinates, divide the value by the visible canvas width and multiply it by the hidden canvas width; use height values to get the y coordinate.
Using Scrawl-canvas to generate responsive canvases
The tour page on this site meets the requirements for RWD: it uses a fluid grid for positioning components, and media queries to change the layout according to the environment in which the page displays. All images used on the site use srcset and sizes attributes in the <img> elements.
- The tour page uses scrawl stacks to act as the canvas containers.
- Scrawl treats each visible <canvas> as a Pad object, which creates a hidden canvas attached to a DOM fragment.
- These base cell canvases are given static dimensions – for the tour page, we use 800px width and 400px height as we designed the tour page fluid grid columns to never exceed a width of 800px.
- When resizing occurs scrawl automatically handles any outfall from a browser downloading a more appropriately sized image file.
- We define and position scrawl entitys using relative (%-based) coordinates, which means we don’t have to worry about pixel calculations.
- The scrawl display cycle handles updating the hidden canvas and copying the hidden canvas over to the visible canvas.
- We use scrawl event listeners to monitor mouse, touch and pointer interactions with the canvas for each canvas; these event listeners automatically handle coordinate calculations for us.
- We check the browser environment using the scrawl.device object, which allows us to tailor each canvas scene to match the browser’s, and device’s, capabilities.
- We use the scrawl animation functionality to drive canvas animations and interactions; to minimise the tour page’s impact on the browser only one canvas is animated at any one time.
- Many of the animation routines use scrawl tweens and timelines, which have no problem using relative (%-based) positions for their calculations.