liveBook · Manning

Canvas element

As explained in D3.js in Action, Third Edition

The Canvas API is the browser scripting interface for drawing on and reading from an HTML canvas bitmap.

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.
(-) The canvas element can be given an aria-label attribute but otherwise is treated like a regular raster image by 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

How D3.js in Action, Third Edition covers it

How deep it goes
Builds responsive high-density 2D setup and interaction strategies, while deferring WebGL and 3D graphics.
Examples
Recreates a shape gallery and uses hidden uniquely colored painting circles to detect hover targets.
What it assumes
Uses JavaScript and D3 to append and configure the element while introducing Canvas drawing setup directly.
What you can do after
Build it from scratch as a responsive 2D drawing surface and add color-based hover detection.
How it connects
Contrasts SVG, DOM, CSS, devicePixelRatio, WebGL, mixed-mode rendering, and getImageData().

Also explained in

Compare how 4 books explain canvas element →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of D3.js in Action, Third Edition. The section links point to the parts of the book it draws on.