A canvas element is an HTML drawing surface used with JavaScript and the Canvas API. Instead of creating a DOM element for every mark, it produces raster graphics by drawing pixels through imperative commands. This makes it useful when a visualization would otherwise create a very large number of SVG elements. section 15.2.1 section 15.1
Choose Canvas when its trade-offs fit the visualization:
- Use it for large datasets, many visualizations on one page, complex animations, or intricate color gradients. More than 1,000 DOM elements is a useful rule of thumb, not a fixed limit. section 15.1
- Prefer SVG for crisp scaling, CSS styling, direct element-level events, accessibility, and easier debugging when performance is adequate. section 15.1
- Combine SVG and Canvas in a mixed-mode view when different visual layers need their respective strengths. section 15.5
Append the canvas element to a container, then set its width and height attributes from the container size. Setting only CSS dimensions can blur raster output. For high-density displays, multiply the drawing-buffer dimensions by devicePixelRatio, keep the element visually constrained with CSS, obtain a 2D context, and scale that context by the same ratio. Update the dimensions when the container changes size. section 15.2.1
Listing 15.3 Correcting the resolution (main.js)
const canvas = d3.select("#canvas")
.append("canvas")
.style("border", "1px solid black")
.style("max-width", "100%"); #1
const container = document.querySelector("#canvas");
const devicePixelRatio = window.devicePixelRatio; #2
const setCanvasSize = () => {
const width = container.offsetWidth;
const height = 0.333 * width;
canvas
.attr("width", width * devicePixelRatio) #3
.attr("height", height * devicePixelRatio);
};
setCanvasSize();
window.addEventListener("resize", setCanvasSize);
This implementation sizes the drawing buffer for device pixel density and recalculates it on browser resize. section 15.2.1
Table 15.1 Comparing SVG with Canvas
|
Feature
|
SVG
|
Canvas
|
|---|---|---|
|
Image quality
|
(+) Vectorial image: crisp looking on all screen sizes, resolutions, and zoom levels
|
(-) Raster image: the image tends to look a little blurry, especially when zooming in or on high-resolution screens. This is especially problematic when the visualization includes text labels.
|
|
Performance
|
(+) Not performant for large numbers of elements but very performant otherwise
|
(+) Very performant when rendering complex animated graphics. For smaller, less complex data visualizations, SVG graphics tend to have a better performance than raster images.
|
|
Can be styled with CSS
|
(+) Yes. SVG also supports CSS transitions and animations.
|
(-) No
|
|
Interactions
|
(+) Event listeners can be attached to SVG elements.
|
(-) Event listeners can be attached to the canvas element itself but not to the drawings inside of it. We must put additional strategies in place to add interactivity.
|
|
Animations
|
(+) SVG can be animated with CSS and JavaScript, but not suited for a high level of complexity like the one we find in games.
|
(+) Suited for handling lots of animations with intricate details and gradients
|
|
Debugging
|
(+) Each SVG element is accessible in the DOM and can be manipulated and inspected.
|
(-) Only the canvas element is rendered into the DOM, and the image it generates can’t be manipulated or inspected with the browser’s inspector tool.
|
|
Accessibility
|
(+) SVG makes it easier to build an accessible experience because all the SVG elements are available in the DOM, and the text SVG elements are accessible to screen readers.
|
Compare raster quality, performance, CSS support, interactions, accessibility, and debugging before replacing SVG with Canvas. section 15.1
Canvas drawings are not separate DOM nodes. A mouse event can be detected on the canvas, but interaction logic must determine which drawing lies at the pointer position. Simple known shapes can be tested from their coordinates; irregular marks can require more involved logic. section 15.4
Hidden color layer for hover
Render the visible painting circles on one canvas and the same circles, each with a unique color, on an overlapping canvas hidden with opacity. Store each color-to-painting association in a Map. On mouse movement, read one pixel from the hidden context with getImageData(), use its color to find the painting, and display its tooltip. section 15.4 section 15.5



