Adobe

decor

Web Platform Team Blog

Making the web awesome

New canvas features

A number of Canvas 2D features features recently landed in WebKit, Chrome and Firefox. Though they were proposed and drafted some time ago, no one was yet actively working on implementing them and polishing the spec. This was a great opportunity for the Adobe Web platform team to clarify those features and work with the various browser vendors to get them implemented.

Most of these features are cutting edge, so to see the running examples below, you need nightly WebKit, daily Chromium, Opera developer or Firefox Nightly. To turn on experimental canvas features in Chrome or Opera, browse to “chrome://flags“, turn on “Enable experimental canvas features” and relaunch. Firefox has individual flags for each feature. They are accessed by going to “about:config“, clicking on “I’ll be careful, I promise” and setting the flag to true. For this article, turn on turn on “canvas.path.enabled” and “canvas.focusring.enabled“. Most of the time, you need to re-launch for the setting to take effect. For features that are marked as ‘shipping‘, you don’t have to use experimental browsers; the latest version of that browser will do.

Path2D objects

The Path2D object lets you cache drawing commands. Previously, if you had a series of drawing commands, you had to repeat them for each frame of your animation. Using Path2D you can record those calls and play them back quickly. This simplifies your code and could increase the performance of your application dramatically. You can even initialize the path with SVG path data. This is faster and you could even store the path data in an SVG path element that is part of your markup.

Path2D can be constructed with the following interface:

 
  path = new Path2D(); // Creates a new empty Path2D object
  path = new Path2D(path); // Creates a new Path2D object that is a copy
  path = new Path2D(d); // Creates a new path with a string interpreted
                        // as SVG path data

The object also exposes the canvaspathmethods so all the path drawing APIs from canvas – such as lineTo, arc and rect – are available.

Canvas 2D context APIs for Path2D

To actually use this new object, the 2D canvas context was extended with the following API’s:

 
  void fill(Path2D path, optional CanvasFillRule fillRule = "nonzero");
  void stroke(Path2D path);
  void clip(Path2D path, optional CanvasFillRule fillRule = "nonzero");
  boolean isPointInPath(Path2D path, double x, double y,
                        optional CanvasFillRule fillRule = "nonzero");
  boolean isPointInStroke(Path2D path, double x, double y);

These methods use the passed-in path object as opposed to the current default path of the existing APIs. Here’s an example that draws a lot of fish bones:

 
      ctx.save();
      ...
      ctx.translate(-60,-25);
      ctx.fill(p); // fill the path
      ctx.restore();
Check out this Pen!

If you profile this CodePen in Chrome, Safari or Firefox, little time is spent in Javascript. If you use individual draw calls, most of the time is spent running scripts. As a result, the performance is much slower.

This example also shows how you can use SVG as a source of path data:

 
var pathstring = document.getElementById("p").attributes.d.value;
var p = new Path2D(pathstring);

This API is enabled on WebKit and Firefox Nightly. It is experimental in Chrome Canary.

ImageData constructors

If you ever use a JavaScript routine or web workers to create a buffer of image data, you know that there was no convenient way to pass that to a canvas putImageData call.

It seems that you should be able to do the following:

 
var buffer = new Uint8ClampedArray(256*256);
... // populate the image data
ctx.putImageData({width: 256, height: 256, data: buffer}, 0,0);

However, because of the way browsers are implemented, you get an exception when executing putImageData. Browsers expect that you pass in a browser created object as opposed to one with the same signature that you created yourself.

The workaround was to call getImageData on a context to create a ‘dummy’ Imagedata object and then update it with your data.

With the new ImageData constructor this is no longer necessary and you can write the following code:

 
var data = new Uint8ClampedArray(256*256);
... // populate the image data
ctx.drawImage(new ImageData(data, 256), 0,0);

ImageData is now also in the global space for web workers. It’s not a transferable object yet like in Internet Explorer 11 so the members of ImageData still have to be transferred individually. This API is available on WebKit and Firefox, and is planned for Chrome.

Opaque canvas

The canvas constructor was extended so you can set its initial color to opaque black.

Because the browser now knows that the backdrop for drawing is always opaque, it can make certain assumptions that speed up drawing of transparent content and images. It also helps performance when the canvas context is drawn into the web page since it can assume the everything underneath the canvas pixels can be removed.

An additional benefit is that the browser can now draw text with sub-pixel anti-aliasing. You can trigger this behavior by passing a JSON object during the canvas context creation:

 
var ctx = canvas.getContext("2d", {alpha: false});

Once you create a context for a canvas element, there is no way to change it back from opaque to non-opaque and vice versa.

For example:

Check out this Pen!

This example draws some text in the small canvas on the left (under “Input canvas”) and then enlarges it in the canvas on the right (under “zoomed in”). Note how the anti-aliasing on the text is colored. This is a trick to give your monitor more horizontal resolution. For more info see this Wikipedia article. This feature improves the readability of text, especially on low-DPI devices. You need to take care to match the resolution of the canvas backing with the screen and don’t do transformations on anti-aliased text. Otherwise, you see the colored edges of the glyphs.

This API is shipping in Chrome and Nightly Firefox. In Safari you can trigger this behavior by filling the entire canvas with a constant color right after construction of the 2D context.

Focus rings

This new API gives you the ability to draw a focus region on your canvas context. To use this feature, you need to provide a sub-DOM to the canvas object in your markup. This could something like this:

 
<canvas width="750" height="220">  <input id="showA" type="checkbox" /></canvas><label for="showA">Show As</label>
<canvas width="750" height="220">  <input id="showB" type="checkbox" /></canvas><label for="showB">Show Bs</label>

The sub-DOM needs to contain elements that can get the focus. These elements (also known as fallback content) should have a pixel representation in the canvas bitmap. When the user uses the tab key, elements in the sub-DOM get the focus. To draw a focus ring for a selectedelement, you use the following API:

 
ctx.beginPath();
ctx.moveTo(...);
ctx.lineto(...);
...; // path around the focus area
ctx.drawFocusIfNeeded(fallbackElement);

drawFocusIfNeeded checks if the element has focus. If it has, it draws a focus ring with the correct style around the path. Here is an example:

 
   context.beginPath();
   context.rect(x-7, y-7, 12 + metrics.width+2, 14);
   if(context.drawFocusIfNeeded) // for older browsers
     context.drawFocusIfNeeded(element);
Check out this Pen!

To try it out, click on one of the checkboxes and notice the focus ring. You can also move the ring by using the Tab key.

To enable this feature in Firefox, turn on “canvas.focusring.enabled” in about:config. This API is also about to land in Chrome as an experimental feature.

Summary

As you can tell, there are a lot of small improvements being made to Canvas 2D. We are also looking into support for hit regions that help you route mouse events and provide accessibility features.

These features are brand new so it’s likely you will find bugs. If so, please tell us about them so we can address them before they ship. Let us know what you think!

5/4/2013 Update 1
- Firefox enabled Path2D by default so no runtime flags are needed
- Mathias Bynens pointed out that the demos are working in Opera Developer as well.

6 Comments

  1. April 01, 2014 at 9:19 am, Richard Heyes said:

    Great news – especially the Path2D object.

  2. April 02, 2014 at 9:30 am, Alex Bell said:

    Fantastic stuff, thanks for the great explanation. The fish bones Codepen seems to be broken, though: blank output :(

    • April 02, 2014 at 10:12 am, Rik Cabanier said:

      It should work.
      What browser did you try it in? You need the latest version + turn on experimental flags.

  3. April 02, 2014 at 9:59 am, Yann Cabon said:

    That’s great! I’m looking for the Path2D Object.
    Could we imagine have some transformation operations on a Path2D to modify all the coordinates?
    like path2d.setTransform("matrix3d(...)")
    that would make it even more awesome :)

    • April 02, 2014 at 10:19 am, Rik Cabanier said:

      We are working on DOMMatrix (http://dev.w3.org/fxtf/geometry/Overview.html#DOMMatrix) which would allow you to do something close to that.
      For now, you can do:
      ctx.save();
      ctx.transform(…);
      ctx.fill(path2dobject); // path object that you want to transform
      ctx.restore();

  4. April 05, 2014 at 4:12 am, Mat Porter said:

    The hit regions support mentioned at the end of the article could be very useful, keeping my eyes peeled for the possible dev.