Using the Canvas API in Angular2

Angular 2.0 has finally been officially released. To help develop my own understanding of it and surrounding technologies, I’ve been working on building a simple little multiplayer card game with it. This got me wondering about how to work with the Canvas API in an Angular2 application. None of the tutorials, articles, or books I’ve read have touched on this particular use case. By experimenting, digging into the Angular 2 source, and scouring the web, I came up with a solution that seems work well. I setup a github repo which you can check out and run yourself: . This repo was copied from the Angular2 QuickStart repo, so if you’ve gone through the QuickStart tutorial, the setup should look familiar.

First (lame) Attempt

My first attempt at working with the Canvas involved modifying the example code in the Attribute Directives tutorial from the official Angular2 documentation. Following the example set in that tutorial, I created an attribute directive. The file barchart.component.ts from my repo gives a working example. A screenshot is shown below. The essential structure is that there is a BarchartComponent whose template has a <canvas> element. That <canvas> element has an attribute directive, BarchartDataDirective, applied to it. BarchartDataDirective’s constructor asks Angular to give it an ElementRef (through Angular’s dependency injection mechanism). It uses ElementRef’s nativeElement property to access the <canvas> element, which is needed in order to get the graphics context for Canvas API operations.

Why did I do it this way? Well, I had some trouble figuring out what I could do with ElementRef and it’s nativeElement property. I wanted to just use the single component, and put the <canvas> element in the component’s template. Then, I figured I could just query the nativeElement of the ElementRef passed to the constructor to get a handle on the embedded <canvas> element. But I couldn’t find a good way (at first, as we’ll see) to get at the <canvas> element unless it was the thing ElementRef referred to. By applying an attribute directive directly on the canvas, the ElementRef passed into BarchartDataDirective's constructor refers to the canvas, allowing me to get a handle on it.

This setup works, but it’s not very satisfying. Notice that BarchartDataDirective assumes/expects that it is being applied to a <canvas> element. That’s not good. Also, the BarchartComponent and BarchartDataDirective each have the same set of inputs – BarchartComponent just forwards them along to BarchartDataDirective. BarchartComponent doesn’t really do anything except setup the template and serve as a sort of public interface. We can improve on this by making use of the ViewChild decorator. 

Screen Shot 2016-09-16 at 5.57.55 PM

A better approach via @viewchild decorator

Now, take a look at Barchart2Component. Using the ViewChild decorator and a template reference variable, we can get rid of the BarchartDataDirective and just use BarchartComponent. I apply a template reference variable to the <canvas> element, giving it the name “canvas”, and then setup an ElementRef property in BarchartComponent that is decorated with @ViewChild('canvas'). Now we have a reference to the child <canvas> element, which is what I originally wanted. However, note that it won’t be ready in the constructor, so I added an ngAfterViewInit() method where I can grab the native <canvas>, set it’s dimensions, and finally invoke the drawing routine. Muhhhhhhch better.

I whipped up a PieChartComponent using the same technique. Here are some screenshots. (By the way – the actual canvas drawing code for these charts were adapted from examples in the online book HTML Canvas Deep Dive by Josh Marinacci ).

Screen Shot 2016-09-16 at 10.11.29 AMScreen Shot 2016-09-16 at 6.22.34 PM

And if charts aren’t your thing, I also thew together a <smiley> component that draws a smiley face (Canvas code adapted from examples in an MDN canvas tutorial):

Screen Shot 2016-09-16 at 6.29.31 PM

With all of these components, you can specify the dimensions and colors. Play around with them, go ahead, have a good time.

words of Warning  (alternatively: I know, I know, save it…)

Direct DOM manipulation is generally frowned upon in Angular2 (see the comments for ElementRef in the Angular2 source code) but, as far as I know, there’s no other way to use the Canvas API. Actually I’m not sure if the techniques I’ve shown here really count as “direct DOM manipulation”, since we’re not adding or removing DOM elements, just using one as it is intended to be used.

one last thing…

Finally, if you’re interested in the topic of Canvas+Angular2, you may have run across the article Rendering in Angular2. While it’s an interesting read, it doesn’t address the same “problem” I had in mind. Instead, it describes how you might swap the default DomRenderer with an alternate renderer, such as (for example) a CanvasRenderer to render your entire application in a canvas element (or even an Electron renderer). That’s my understanding, anyway. Apparently it’s still experimental. It’s definitely something to watch.


Leave a Reply

Your email address will not be published. Required fields are marked *