The Canvas with CSS in Angular2

I’ve been working at creating Components in Angular2. One of the first efforts is a Canvas-based control. Since my discoveries were hard-won, I thought I’d share what I came up with:

The Problem

I am a strong believer in the separation between content (HTML) and design (CSS). You need to be able to change the design and layout without changing your code. Separating stylesheet layouts from HTML content and JavaScript code allows you to keep your application looking fashionable — even as fashions change — without having to touch working code.

A difficulty arises when working with the Canvas object. With the Canvas object, you draw colors and test using JavaScript that has no knowledge of CSS settings. Because you specify fonts and colors within JavaScript, these choices have no relationship to CSS stylings. Consequently, were you to update a site with a new stylesheet, the legacy JavaScript colors and fonts may no longer match.

A great solution would be to enable the Canvas drawing tools to leverage the current stylesheet.

The Solution

Angular2 has built-in support for StyleSheets in Components. However, this is designed to solve a different problem: encapsulating Component content (including StyleSheets). In other words, this enables Components that require a specific CSS file in order to run to load such a stylesheet. This solution has nothing to do with our problem; our problem has to do with inheriting pages styles for our Canvas drawing.

In the code below, the Angular2 App Component calls our stylized Component, creatively called “AppComponent2”. The AppComponent2 creates a div tag with a specific class. Because this tag is stylized by the CSS stylesheet, we can capture the stylesheet options. In this example, we simply display the relevant values as the div content. In a real scenario, we’d use these values to construct the canvas draw commands.

Starting with the Angular2 Quick Start, you need to add a stylesheet and modify the app.component,ts and index.html files. The stylesheet can has any values you like. I used the a class styling, as follows:

.cvsTest {
 background: black;
 color: white;
 font-family:'Gill Sans', Calibri, 'Trebuchet MS', sans-serif;
 font-size:1rem;
 text-align:center;
 font-style: italic;
}

With this CSS definition, my canvas drawing should use these corresponding fonts, sizes, and colors. Changing them to match a new design will be as simply as updating the CSS file.

Of course, if you already have a stylesheet, you can just add the above definition. If not, you will also also need to modify the home page to reference the new stylesheet. Within the Head tags, add (assuming that you called your stylesheet app.css):

<link rel="stylesheet" href="app/app.css" />

Finally, modify the app.component.ts file to load the new Components:

import { Component, ViewChild, Directive } from 'angular2/core';

@Component({
selector: 'my-app2',
template: '<div #divElement class="cvsTest">Loading...</div>', 
})
export class AppComponent2 {
@ViewChild('divElement') divElement: any;

private ngAfterViewInit() {
var el: HTMLDivElement = this.divElement.nativeElement;
window.onresize = this.resizeCanvas.bind(this);
var style: CSSStyleDeclaration;
if (this.divElement) {
el = this.divElement.nativeElement;
var width = el.clientWidth;
var height = el.clientHeight;
if (el) {
style = window.getComputedStyle(el);
if (style) {
var report: string =
    "dimensions: " + width + 
    "px x " + height + "px \r\n" +
"backgroundColor: " + style.backgroundColor + "\r\n" +
    "color: " + style.color + "\r\n" +
    "direction: " + style.direction + "\r\n" +
    "font: " + style.font + "\r\n" +
    "fontFamily: " + style.fontFamily + "\r\n" +
    "fontSize: " + style.fontSize + "\r\n" +
    "fontSizeAdjust: " + style.fontSizeAdjust + "\r\n" +
    "fontStyle: " + style.fontStyle + "\r\n" +
    "fontVariant: " + style.fontVariant + "\r\n" +
    "fontWeight: " + style.fontWeight + "\r\n" +
    "textAlign: " + style.textAlign + "\r\n" +
    "textDecoration: " + style.textDecoration + "\r\n" +
    "visibility: " + style.visibility + "\r\n"
el.innerText = report;
}
}
}
}
private resizeCanvas(event:UIEvent): void {
var el: HTMLDivElement = this.divElement.nativeElement;
var width = el.clientWidth;
var height = el.clientHeight;
var report: string = "dimensions: " + width + "px x " + height + "px";
el.innerText = report;
}
}


@Component({
selector: 'my-app',
template: '<my-app2></my-app2>',
directives: [AppComponent2],
styles: [`
.my-app {
color: gray;
}
`]


})
export class AppComponent { }

You may notice that when you run this, when the window is resized, the content updates with the display of the resized values. That is the other tricky thing about the canvas: because you have to specify the size, you need to detect when the size should change.

You’re welcome.