In a world of shaders, meshes, and filters, Canvas2D might not get you excited. But it should! 30-40% of web pages have a element and 98% of all canvases use a Canvas2D rendering context. There are Canvas2Ds in cars, on fridges, and in space (really).
Admittedly, the API is a bit behind the times when it comes to state-of-the-art 2D drawing. Fortunately we’ve been hard at work implementing new features in Canvas2D to catch up to CSS, streamline ergonomics and improve performance.
Part 1: catching up with CSS
CSS has a few drawing commands that are sorely missing from Canvas2D. With the new API we’ve added a handful of the most requested features:
Round rect
Rounded rectangles: the cornerstone of the internet, of computing, nigh, of civilization.
In all seriousness, rounded rectangles are extremely useful: as buttons, chat bubbles, thumbnails, speech bubbles, you name it. It’s always been possible to make a rounded rectangle in Canvas2D, it’s just been a bit messy:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.fillStyle = “magenta”; const top = 10;
const left = 10;
const width = 200;
const height = 100;
const radius = 20;
ctx.beginPath();
ctx.moveTo(left + radius, top);
ctx.lineTo(left + width - radius, top);
ctx.arcTo(left + width, top, left + width, top + radius, radius);
ctx.lineTo(left + width, top + height - radius);
ctx.arcTo(
left + width, top + height, left + width - radius,
top + height, radius);
ctx.lineTo(left + radius, top + height);
ctx.arcTo(left, top + height, left, top + height - radius, radius)
ctx.lineTo(left, top + radius);
ctx.arcTo(left, top, left + radius, top, radius)
ctx.stroke();
All this was necessary for a modest, simple rounded rectangle:
With the new API there’s a roundRect()
method.
ctx.roundRect(upper, left, width, height, borderRadius); So the above can be wholly replaced by:
ctx.roundRect(10, 10, 200, 100, 20);
ctx.roundRect()
also takes in an array for the borderRadius
argument of up to four numbers. These radii control the four corners of the rounded rectangle the same way as for CSS. For example:
ctx.roundRect(10, 10, 200, 100, [15, 50, 30])
Check out the demo to play around!
Conic Gradient
You’ve seen linear gradients:
const gradient = ctx.createLinearGradient(0, 0, 200, 100);
gradient.addColorStop(0, "blue");
gradient.addColorStop(0.5, "magenta");
gradient.addColorStop(1, "white");
ctx.fillStyle = gradient;
ctx.fillRect(10, 10, 200, 100);
Radial gradients:
const radialGradient = ctx.createRadialGradient(150, 75, 10, 150, 75, 70);
radialGradient.addColorStop(0, "white");
radialGradient.addColorStop(0.5, "magenta");
radialGradient.addColorStop(1, "lightblue"); ctx.fillStyle = radialGradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
But how about a nice conic gradient?
const grad = ctx.createConicGradient(0, 100, 100);grad.addColorStop(0, "red");
grad.addColorStop(0.25, "orange");
grad.addColorStop(0.5, "yellow");
grad.addColorStop(0.75, "green");
grad.addColorStop(1, "blue");
ctx.fillStyle = grad;
ctx.fillRect(0, 0, 200, 200);
Text modifiers
Canvas2Ds text rendering capabilities have been woefully behind. Chrome has added several new attributes to Canvas2D text rendering:
- ctx.letterSpacing
- ctx.wordSpacing
- ctx.fontVariant
- ctx.fontKerning
- ctx.fontStretch
- ctx.textDecoration
- ctx.textUnderlinePosition
- ctx.textRendering
These attributes all match their CSS counterparts with the same names.
Part 2: ergonomic tweaks
Previously, some things with Canvas2D were possible, but needlessly complicated to implement. Here are some quality-of-life improvements for javascript developers who want to use Canvas2D:
context.reset()
To explain clearing a canvas, I’ve written a silly little function to draw a retro pattern:
draw90sPattern();
Great! Now that I’m done with that pattern, I want to clear the canvas and draw something else. Wait, how do we clear a canvas again? Oh yeah! ctx.clearRect()
, of course.
ctx.clearRect(0, 0, canvas.width, canvas.height);
Huh… that didn’t work. Oh yeah! I’ve got to reset the transform first:
ctx.resetTransform();
ctx.clearRect(0, 0, canvas.width, canvas.height);
Perfect! A nice blank canvas. Now let’s start drawing a nice horizontal line:
ctx.moveTo(10, 10);
ctx.lineTo(canvas.width, 10);
ctx.stroke();
Grrrr! That’s not right! 😡 What’s that extra line doing here? Also, why is it pink? Okay, let’s just check StackOverflow.
canvas.width = canvas.width;
Why is this so silly? Why is this so hard?
Well, it’s not any more. With the new api we have the simple, elegant, beautiful groundbreaking:
ctx.reset();
Sorry that took so long.
Filters
SVG filters are a world unto themselves. If they’re new to you I highly recommend reading “The Art Of SVG Filters And Why It Is Awesome“, which shows some of their amazing potential.
SVG style filters are already available for Canvas2D! You just have to be willing to pass the filter as a url pointing to another SVG filter element on the page:
<svg>
<defs>
<filter id="svgFilter">
<feGaussianBlur in="SourceGraphic" stdDeviation="5"/>
<feConvolveMatrix kernelMatrix="-3 0 0 0 0.5 0 0 0 3"/>
<feColorMatrix type="hueRotate" values="90"/>
filter>
defs>
svg>
const canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 400;
const ctx = canvas.getContext("2d");
document.body.appendChild(canvas); ctx.filter = "url('#svgFilter')";
draw90sPattern(ctx);
Which messes up our pattern pretty