Part 1 of this two-part series discussed combining the best aspects of canvas and HTML elements to build rich internet applications.
In this article, review the criteria for selecting a canvas or HTML-centric architecture, and learn about animation considerations and overcoming text rendering limitations by building the foundation for a video game that layers HTML and canvas elements to harness the advantages of each approach. Figure 1 shows the groundwork of the example space shooter game in this article.
Figure 1. Sample application combining HTML and canvas elements
You can download the source code for the examples used in this article.
When architecting an application with significant graphical components, interactive experiences, and visualizations, it's important to be aware of all of the tools available for the job. This section explores using HTML for implementing UI components and canvas for implementing animated components.
Though canvas has the potential for impressive graphical performance, it might not always be the best choice for a rich UI; HTML elements may be better suited. Many rich Internet applications consist of various pieces that each have different purposes and requirements. A hybrid approach that combines the best aspects of canvas and HTML elements could work best to accomplish your goals.
The sample application employs the layering technique in Figure 2. The canvas surface will predominantly be responsible for realtime graphics and animations, while several HTML elements will be overlaid on top to make up various UI components.
Figure 2. Layering HTML elements above a canvas
Consider the requirements of the individual pieces of an application when deciding on the best approach. As a general rule, components of the UI that require a high level of user interactivity—but do not require realtime updates—are typically best suited for the HTML layer. These elements might contain text, hyperlinks, and form elements.
For example, although the sample application may be predominantly a canvas, it has a chat window component that uses purely HTML markup. This is illustrated in Figure 3,
With simple HTML tags and CSS rules you can easily create interactive UI components such as text boxes, scrollbars, and buttons. Why spend many hours of development effort attempting to mimic the look and behavior of UI components in canvas when the browser will do it for you out-of-the-box?
Figure 3. Chat window implemented in HTML
In the chat window, there are no animations involved, and updates to the content are relatively infrequent (whenever a new chat comes in). Therefore, this is a good candidate for an HTML implementation.
Conversely, canvas provides a much better solution for content that:
- Must be updated frequently (for example, every millisecond)
- Requires a constant animation cycle
- Requires only a small degree of user interactivity
Animated content is becoming more prevalent in websites. Whether you want simple animations intended to liven up a website (such as navigation transitions), or a more advanced browser-based game, several options are at your disposal. Often, animations can be accomplished relatively easily using an HTML/CSS model, and use of canvas would be overkill.
Many libraries, such as jQuery, offer convenient tools that provide consistent cross-browser output. By combining the tools from these libraries with a bit of CSS knowledge, you can greatly reduce the effort to accomplish animations—even those that are fairly complex in nature.
In the example, the ship element in Figure 4 moves and rotates according to player control. This behavior can be achieved solely by using HTML/CSS animations. No knowledge of canvas is necessary.
Figure 4. Animated ship element
The HTML/CSS model for animation begins to break down when it comes to scalability. Animating a large number of elements simultaneously requires a great deal of heavy lifting by the browser, which will reduce the overall performance of the application.
Each time a DOM element is repositioned, which must happen multiple times per second for a smooth animation, the browser's layout engine requires a significant amount of overhead to recalculate and repaint the elements in the DOM hierarchy. Scale this up to tens, hundreds, or thousands of elements, and a sizable performance hit can be noticed even on modern computers.
Rendering text onto the screen is such a basic task for any website that we might take for granted the small gears that are turning in the background. When you want text on the screen, you simply type it between a couple of HTML element tags, possibly accompanied by a bit of CSS, and let the browser take over. This is not the reality, however, when it comes to canvas.
When you want to render text with canvas, a few basic tools are available. They allow for nearly all of the basic functions required to accomplish almost anything, but they don't provide much ease-of-use when developing a canvas application.
The canvas context object provides various
properties that you can set when rendering text. It also provides a
function to do the actual rendering. The properties include:
context.fontSetting the value of
context.fontlets you control the font family, size, weight, and style of the text to be rendered. The value assigned is a single string with the various options pieced together and separated by spaces.The input format here is a bit of a hurdle. For instance, the font family must be provided in this string every time the value is updated. Listing 1 shows the setting for small font.
Listing 1. Setting a small fontcontext.font = 'italic 8px Arial'; context.fillText('Variety is the spice of life!', 0, 50);
Listing 2 shows the setting for a large font.
Listing 2. Setting a large fontcontext.font = 'italic 20px Arial'; context.fillText('Variety is the spice of life!', 0, 50);
A user cannot simply set the font family once and adjust other options at a later time depending on the text they wish to render to the screen. An approach to remedy this issue will be discussed later.
context.fillStylecontext.fillStyleis used for various canvas operations; in the case of text, you set the value to control the color of the font to be rendered to the screen. The input format for the value specified conforms to that of CSS. The following are all valid input samples:- Basic colors: 'red', 'blue', 'green', and so on
- Hex values: '#rrggbb'
- 'rgb(r, g, b)'
- 'rgba(r, g, b, a)'
For example, to set the fillStyle use:
context.fillStyle = 'red';.context.fillText()Call this function to render text onto the canvas. It takes the following parameters:
(string) text: The text to draw onto the canvas.(float) x: The x dimension at which to draw the text.(float) y: The y dimension at which to draw the text.[optional] (float) maxWidth: A maximum width to attempt to keep the text under. If possible, a more horizontally condensed font will be used, or even a smaller font.
An important additional tool you can use is a function of the context
object called measureText(), which takes a single
string parameter. The result is an object containing the measured
dimensions of the provided string, as shown in Listing 3.
Listing 3. Determining width of a string of text
context.font = '30px Arial';
var dim = context.measureText(
'Hello, world!'
);
alert(
'width: ' + dim.width + '\n' +
'height: ' + dim.height
);
|
Listing 3 will display an alert with an output similar to what is in Listing 4.
Listing 4. Alert
width: 164
height: undefined
|
Notice the resulting undefined for height.
Strangely, all browsers will always return an undefined result, so it's
effectively impossible to determine the exact text height using the
measureText() function. There are a few
techniques available to determine a fairly representative value. For many
fonts, certain letters are quite square, such as the letter M. You can
measure the width of one of these characters and use that value as an
approximate measure for the height of the font.
Another method is to simply use the supplied size of the font as a base for the working height. If a value of 30px is supplied in the example above, you could add a few pixels for vertical padding and use the resulting value as the approximate height.
To increase the efficiency of the development process, you can use the
previously mentioned tools to perform basic operations in a friendlier
manner by creating a simple wrapper class. The class would automate the
setting and swapping of the various context properties and ultimately call
for the rendering of the desired text to the canvas. You can automate the
issue with the context.font property. The
example in Listing 5 was used
in Part 1 of this series.
Listing 5. Rendering text with a dynamic style in canvas
context.font = '18px Arial';
context.fillStyle = 'green';
context.fillText('Variety', 0, 50);
context.translate(60, 0); //move 60 pixels to the right (a)
context.font = '12px Arial';
context.fillStyle = 'blue';
context.fillText('is the', 0, 50);
context.translate(35, 0); //move 35 pixels to the right (b)
context.font = 'italic bold 12px Arial';
context.fillStyle = 'red';
context.fillText('spice of life!', 0, 50); // (c)
|
Figure 5 shows the three steps from Listing 5 for rendering text in canvas.
Figure 5. Rendering text with a dynamic style in canvas
Using the raw canvas API, you had to write a fair amount of code. If you could simplify the code required to achieve the example above, it would increase the efficiency when rendering text to the canvas. For instance, instead of numerous lines of raw code, you could use the code in Listing 6.
Listing 6. A concept of workflow to augment canvas text rendering
var myText = new CanvasText();
myText
.family('Arial')
.size('18px')
.weight('bold')
.color('green')
.append('Variety')
.size('12px')
.weight('normal')
.color('blue')
.append('is the')
.style('italic')
.color('red')
.append('spice of life!')
.render();
|
Figure 6 illustrates the code in Listing 6. The example uses chaining, which is a clean and simple syntax commonly used in jQuery and jQuery plugins.
Figure 6. Augmenting canvas text rendering
In this way, the process is simplified. Though there's nearly the same number of lines of code, the complexity of each line is greatly reduced. And, you no longer need to manually position each block of text with differing styles. If you decide later to adjust some of the properties of any of the blocks of text, you've removed the necessity of manually repositioning the following blocks.
Recall that the canvas.font property is a
combination of multiple properties that decide how text will be rendered.
Between the Variety and subsequent blocks, you
didn't have to specify the font family. Between the
'is the' and
'spice
of life!' blocks, specification of the
size and weight of the font was also not required. A simple mechanism to
remember the properties previously applied was employed here, thus
alleviating the need to reconfigure desired properties of text that were
previously applied. See Resources for a working
example of the CanvasText class.
To achieve the results above, simply group the styling properties of the
text you wish to render, specify the styling properties accordingly, then
call the append() function of the
CanvasText object that closes off the group and
stores it for later use. When you render the text, loop over these groups
and apply the styles as they're specified. As you iterate over the groups,
keep a working history of the previous style state and override as
necessary. These actions achieve the stylistic memory and reduce the
required specification to render text onto the canvas. This is only one
benefit of employing a wrapper class, or set of building blocks, to
automate the process of rendering text to canvas.
Word wrapping is something that many people might take for granted. When an HTML element contains content that is too long to fit on one line, it simply works. We don't have to consider the length of text, the width of the container, and so on. With canvas, however, it's not quite that simple. HTML canvas elements do not currently contain built-in functions to manage such things, so you have to programmatically create the functions using tools and methods previously mentioned.
For example, to have text wrap when it surpasses the width of a container, you must know the width of said container, the width of the text you wish to render, and the height of a line of text. You also need to create the virtual container and provide methods for specifying the width of that container. After you've created a process for doing this, you're ready to begin diving into the logic required to achieve word wrapping in canvas.
Listing 7 elaborates upon
Listing 6 and provides some
arguments to pass into the constructor of the
CanvasText class.
Listing 7. Passing arguments into the constructor of the
CanvasText class
var myText = new CanvasText(
{x: 50, y: 50},
{width: 100, height: 200}
);
myText
.family('Arial')
.size('18px')
.weight('bold')
.color('green')
.append('Variety')
.size('12px')
.weight('normal')
.color('blue')
.append('is the')
.style('italic')
.color('red')
.append('spice of life!')
.render();
|
The additional arguments represent the x and
y coordinates and dimensions of the position of
the text to be rendered and the size of the container, respectively. Note
that an explicit width has been specified to restrain the content
wrapping.
Now that you have a container size to conform to, you can begin to create
the functions to achieve the desired result. In addition to looping over
each differing style, you'll also need to loop over each word to acquire
some data on how wide the text would actually be once rendered. With this
information, you can keep track of the width of the text you've already
rendered and decide whether the next word will render outside the desired
range. Fortunately, the measureText() function
will provide exactly the information needed.
Listing 8 shows the code
needed to implement word wrapping with the
CanvasText class.
Listing 8. Including word wrapping functions
// use measure text
var currentWordWidth = context.measureText(currentWord).width
// word wrap code here
if (textAdjustment.x + currentWordWidth > this._size.x || textToDraw == '\n') {
textAdjustment.x = 0;
textAdjustment.y += parseInt(previousFontOptions.size, 10);
}
|
The end result is shown in Figure 7.
Figure 7. Word wrapping result
See Resources for a working example of the
CanvasText class with word wrapping.
With a fairly simple wrapper class, you automated the styling and word wrapping tasks in canvas. This wrapper class can now be reused wherever you need to render text with canvas.
We tend to take usable UIs for granted. From HTML to Flash, to Silverlight, they all offer an essential set of UI components in the form of text, menus, scrollbars, and form elements.
The next example is a design for the foundation for a simple space shooter game. You'll create: a spaceship component that can fly around the game area, a chat window, and a store. Some of the components require animation, and some provide textual information that might need to be updated or rendered frequently.
See Resources for the complete working example of the space shooter game.
The first step is to try to create each of the game components using basic HTML markup. With a little CSS knowledge, UI components such as the shop and chat system can be created fairly quickly.
Now for the fun part: the space ship. For the HTML implementation, create a simple DIV element, attach a background image for the ship, and add elements to display things such as the player's name and health. The code for these components thus far lies solely in the HTML and CSS of the example.
Listing 9 shows some of the CSS required for the ship and the text that displays underneath it. The position of the DIV with the player class is what you'll be updating later on in the DOM render function to achieve the animation.
Listing 9. CSS for styling of player's ship, name, and health
.player {
position: absolute;
width: 100px;
height: 100px;
}
.text-under-ship {
position: absolute;
top: 100px;
left: 0;
width: 100px;
}
.name {
font-family: Georgia;
font-size: 15px;
font-weight: bold;
color: red;
}
.health {
font-family: Georgia;
font-size: 10px;
color: yellow;
}
|
At this point you can start working with the code involved to handle the
game logic, as shown in Listing 10. The code is largely centralized in the
Game object of the JavaScript. It will be
responsible for handling user input and calling the update and render
functions of related components.
Listing 10. The
gameLoop function used to drive the application
gameLoop: function() {
// calculate time elapsed since last update
var currentTime = new Date().getTime();
var elapsed = currentTime - this._previousTime;
// call updates
Ship.update(elapsed);
// call renders
if (this._canvasRendering) {
CanvasManager.render();
Ship.renderCanvas();
} else {
Ship.renderDOM();
}
// store current time as the previous update time
this._previousTime = currentTime;
}
|
A key piece of the example is the update functions of the
Ship class. These functions are responsible for
managing and updating the speed, direction, and position of the ship that
will be used by the rendering code.
Until now, most of what you've created is reusable, regardless of the
rendering approach. Listing 11 takes a deeper look into the differences
between the DOM and canvas rendering
functions.
Listing 11. HTML
rendering function
renderDOM: function() {
var player = jQuery('#player');
player .css({
left: this._position.x,
top: this._position.y
});
var rotationTransform = 'rotate(' + (this._rotation / 100 * 360) + 'deg)';
var ship = player.find('#ship')
.css('transform', rotationTransform )
.css('-webkit-transform', rotationTransform )
.css('-moz-transform', rotationTransform )
.css('-ms-transform', rotationTransform )
.css('-o-transform', rotationTransform );
}
|
The code in Listing 11 is
minimal because it uses jQuery to position the ship and set the rotation.
For true cross-browser compatibility, various vendor-specific flavors of
the transform property must be set.
The HTML approach ended up being relatively simple to implement, with little complexity, and only a small amount of code required. The disadvantage is that the potential for achieving an acceptable framerate is lacking due to the browser overhead in rendering DOM elements.
Before working with canvas, you need a reference to the canvas object you'll be working with. After you have that, you need to initialize the context you want to work with. This is the context discussed earlier in this article and the means for working with a canvas element.
Employ a simple canvas manager class, as shown
in Listing 12.
Listing 12. Canvas
manager class
var CanvasManager = {
canvas: null,
context: null,
_size: null,
init: function() {
this.canvas = document.getElementById('canvas-game-area');
this.context = this.canvas.getContext('2d');
this._size = {
x: this.canvas.width,
y: this.canvas.height,
}
},
render: function() {
this.context.clearRect(0, 0, this._size.x, this._size.y);
}
}
|
In most applications, each time you want to update what's rendered on the
canvas, you have to first clear what was previously rendered to gain a
clean display area. You can achieve this with the
clearRect() function, as shown in Listing 13.
Listing 13. Canvas rendering function
renderCanvas: function() {
var context = CanvasManager.context;
// save the context to prepare for our
// upcoming translation and rotation
context.save();
// translate the canvas to the ship's center position
context.translate(
this._position.x + 50,
this._position.y + 50
);
// rotate the canvas to show the angle the ship is pointing
context.rotate(this.getRotationInRadians());
// draw the ship with an offset of half the height
// and width to center the image
context.drawImage(
this._displayImage,
-50,
-50
);
// restore the context
context.restore();
this._playerName.render();
}
|
The code in Listing 13 shows some advantages and disadvantages of the canvas approach. The chief benefit is increased performance. The effect is exaggerated even more when you have more components to render, as shown in Figure 8.
Figure 8. Increasing number of ships to exaggerate performance differences
The example is now rendering 50 ships layered on top of one another (see Resources for the working example). Clearly, the HTML version lags quite a bit. Switching to the canvas version dramatically improves the performance.
The amount of code for the two approaches was noticeably different. With HTML, you accomplished the positioning of the ship with one function call by using jQuery. With the canvas approach, you need a bit more.
In Listing 13 there's also an
additional call to draw the player's name. This wasn't necessary in the
HTML method because you were positioning an element that contained both
the ship and the accompanying text. In canvas, though, you don't have this
convenience. To achieve the rendering of the player's name, the example
used the CanvasText class, which effectively
moved all of the work for rendering the ship and its accompanying text out
of the DOM and into canvas.
In this two-part series, you explored the criteria for choosing a canvas or HTML-centric architecture. In this article, you learned about animation considerations and how to overcome text rendering limitations. The examples showed how to bring the concepts together and provided insight into different approaches for hybrid canvas-HTML architectures.
| Description | Name | Size | Download method |
|---|---|---|---|
| Article source code | canvashtmlpt2sourcecode.zip | 20KB | HTTP |
Information about download methods
Learn
- Working example of the
CanvasTextclass. - Working example of the
CanvasTextclass with word wrapping. - Complete working example of the space shooter game in this
article.
-
Working example of 50 ships
layered on top of one another.
- jQuery:
Learn more about this fast and concise JavaScript Library that simplifies
HTML document traversing, event handling, animating, and Ajax interactions
for rapid web development.
-
jQuery
Fundamentals (Rebecca Murphey, 2010): Read this comprehensive
overview of the jQuery JavaScript library.
-
jQuery Events API:
Learn more about methods used to register behaviors to take effect when
the user interacts with the browser.
- "Create great graphics with the HTML5 canvas" (developerWorks,
February 2011): Read how to enhance your web pages with canvas, a simple
HTML5 element that packs a punch.
-
HTML5 Canvas: View this demo that focuses on the use of the
canvas API and shows you how to paint a very simple animation.
- "HTML5 fundamentals, Part 4: The final touch - The Canvas"
(developerWorks, July 2011): Learn more about the changes in HTML5 and the
HTML5 canvas element.
-
Canvas
Pixel Manipulation: View this demo, from the Safari Dev Center, to
learn about managing the canvas to develop effective visual assets.
-
WHATWG: Explore this community of
developers working with the W3C to fine-tune HTML5.
-
Canvas
tutorial: Check out this tutorial from the Mozilla developers to
learn how to implement the canvas element in your HTML pages.
-
HTML5 Canvas
reference: Use the exercises in this reference from W3Schools.com
to help hone your canvas knowledge.
- developerWorks Web
development zone: Find articles covering various web-based
solutions. See the Web
development technical library for a wide range of technical
articles and tips, tutorials, standards, and IBM Redbooks.
- developerWorks
technical events and webcasts: Stay current with technology in
these sessions.
- developerWorks Live! briefings: Get up to speed quickly on IBM
products and tools as well as IT industry trends.
-
developerWorks on-demand demos: Watch demos ranging from product
installation and setup for beginners, to advanced functionality for
experienced developers.
- developerWorks on
Twitter: Join today to follow developerWorks tweets.
Get products and technologies
-
jQuery: Get the popular JavaScript Library
that simplifies HTML document traversing, event handling, animating, and
Ajax interactions for rapid web development.
-
Kibo: Another popular
library specifically written for speedy cross-browser keyboard event
handling.
-
IBM product
evaluation versions: Download or explore
the online trials in the IBM SOA Sandbox and get your hands on
application development tools and middleware products from DB2, Lotus,
Rational, Tivoli, and WebSphere.
Discuss
- developerWorks
community: Connect with other developerWorks users while exploring
the developer-driven blogs, forums, groups, and wikis.
- Find other developerWorks members interested in web development.

Ryan DeLuca began programming in 1998 and, after a few years, turned his hobby into a paying freelance career. He decided to formalize his training and graduated in 2011 from the University of Wisconsin-Superior with a bachelor of science degree. Shortly after graduation, Ryan joined The Nerdery as a software engineer specializing in PHP, JavaScript, CSS, and HTML. His many talents also include relational databases/SQL and graphics manipulation.

Kevin Moot has had an interest in computer graphics since creating games as a wee lad on his Apple IIe (with its vast array of six colors and a mind boggling 280x192 resolution). He has worked with HTML5's Canvas technology for several cutting-edge websites, and counts HTML/CSS, JavaScript, and .NET among his specialties. Kevin currently is an interactive software developer at The Nerdery.



