Getting Started with the HTML 5 Canvas

Rendering complex graphs or designs to the Web has always been a challenge that has typically been solved by using images, server-side processes or plugins such as Silverlight or Flash. Although drawing charts with straight lines has never been a problem (with some creative CSS), rendering different types of shapes and gradients in the browser such as ellipses, bezier curves and other custom shapes has always been a problem. With the addition of the HTML 5 canvas in the latest version of all major browsers, a lot can be done with only JavaScript and HTML tags now.

So what is the canvas tag? Put simply, it’s a way to render pixels on the client-side using JavaScript. This includes rendering text, images, shapes, linear and radial gradients, performing animations, plus more. Unlike Scalable Vector Graphics (SVG) which is vector based (and also available in many browsers now), the canvas is pixel-based. This makes it more challenging in scenarios where a user can zoom in or out since code has to be written to re-render pixels based upon a specific zoom level. However, the canvas performs very well making it a good candidate for many types of complex rendering scenarios including graphs/charts, games and much more. The performance is especially good in Internet Explorer 9 due to the hardware acceleration it provides. Several great examples of using the canvas can be found on the web including Microsoft’s http://www.beautyoftheweb.com site. Check out Mike Tompkin’s excellent Firework musical demo at http://www.beautyoftheweb.com/firework/index.html (see Figure 1).


image

Figure 1. Putting the canvas into action with other HTML 5 features such as audio and video. Run it in IE9 to see how much hardware acceleration influences performance.

Before jumping into a discussion of using the canvas let’s take a moment to consider why you’d want to use it over something like Silverlight, Flash or server-side image generation processes. Determining when to use the canvas (or any HTML 5 feature in my mind) comes down to the target audience. For example, I’m a big fan of Silverlight in addition to Web technologies. If I’m building a Line of Business (LOB) application that will only be deployed on Windows or Macintosh machines using in-browser or out-of-browser techniques then I’ll generally look to Silverlight first since it works very well in that scenario and brings a lot of power and productivity to the table. However, if I’m writing an application that will be released on the Internet/Intranet and have the potential to be used by different devices such as iPads/iPhones, Android phones and tablets as well as others, then I’ll pick standard Web technologies. Every application is different and I don’t believe that one size or technology fits all. You’ll of course have to evaluate if your target users have browsers that support the canvas tag as well and plan an alternate strategy if they don’t. The canvas is definitely a new feature and not supported by a lot of older browsers including Internet Explorer prior to the release of IE9.

In this post I’ll provide an introduction to the canvas tag and demonstrate some of the fundamental tasks you can perform using it.  Let’s get started with an overview of how to define and interact with the canvas.

 

Putting the Canvas to Work

Canvas functionality is available in the latest browser versions such as IE9, Chrome, Safari, FireFox and Opera and can be defined using the <canvas> tag as shown next:

 

<canvas id="canvas" width="800" height="600"></canvas>

 

Once a canvas is defined (or dynamically added) in HTML you can interact with it using standard JavaScript. I generally prefer to use jQuery in any JavaScript-oriented page but you can use the standard document.getElementById() function to locate a canvas tag and then interact with it as well. The following code demonstrates how to locate a canvas tag defined in a page and get access to its 2D context for drawing:

 

<script type="text/javascript">
    window.onload = function () {
        var canvas = document.getElementById('canvas');
        var ctx = canvas.getContext("2d");
    };
</script>


If you’re using jQuery it would look something like the following:

 

<script type="text/javascript">
    $(document).ready(function () {
        var canvas = $('#canvas');
        var ctx = canvas[0].getContext("2d");
    });
</script>


Notice that once the canvas object is located you must access it’s 2D drawing context. The W3C defines the 2D context in the following way: “The 2D context represents a flat Cartesian surface whose origin (0,0) is at the top left corner, with the coordinate space having x values increasing when going right, and y values increasing when going down.” (http://dev.w3.org/html5/2dcontext/). You can think of it as the drawing surface that you’ll programmatically interact with using JavaScript. Once you have a reference to the 2D context object you can use methods such as lineTo(), fillText() and fillRect() to perform drawing operations. Let’s take a look at a few of the drawing features available.

 

Drawing Shapes, Lines and Text

If you’ve ever used GDI+ (Graphics Device Interface) in the .NET framework (System.Drawing namespace) then you’ll feel right at home using the canvas since it’s similar to GDI+ drawing in many ways. If you’ve never touched GDI+ then don’t worry about it; it’s simple to get started using the canvas once you know a few fundamentals. Drawing is accomplished by calling JavaScript functions that handle rendering lines, shapes, colors, text, images, styles and more. Several of the key functions you can use are shown in Listing 1.

Function Description
arc() Used to render an arc by defining a center x/y coordinate, a radius, a starting angle and an ending angle
beginPath() Signals the start of a new path
bezierCurveTo() Handles drawing a Bezier curve to the canvas
closePath() Signals the end of a path
createLinearGradient() Used to create a linear gradient with multiple gradient stops
drawImage() Used to render an Image object to the canvas
fill() Fills a shape such as a rect or arc
fillText() Renders text to the screen
fillRect() Fills a rectangle with a defined color
lineTo() Draws a line from the current position to a defined x/y coordinate
moveTo() Moves the “pen” to a specific position on the 2D drawing surface
stroke() Used to render a line


Listing 1. Key canvas functions.


Let’s look at a few things you can do with the canvas starting with rendering rectangles. To draw rectangles you can use the fillRect(topLeftCornerX,topLeftCornerY,width,height) function. It accepts the x/y coordinates of where to start the shape as well as its height and width. An example of defining a rectangle is shown in Listing 2. In this example, the 2D context has its fillStyle set to a color of Green. The square that’s rendered by calling fillRect() will be displayed in the upper-left of the screen (0,0 point) and have a width of 200 and a height of 100.

 

<script type="text/javascript">
    window.onload = function () {
        var canvas = document.getElementById('canvas');
        var ctx = canvas.getContext("2d");

        //Render a rectangle
        ctx.fillStyle = 'Green';
        ctx.fillRect(0, 0, 200, 100);
    };
</script>


Listing 2
. Rendering a rectangle using the fillRect() function.

 

If you’d like to render an arc or circle, the arc() function can be used. It has the following signature:

arc(centerX,centerY,radius,startAngle,endAngle,antiClockwise);

The centerX and center Y parameters define where the middle of the ellipse will be, the radius defines the size of the ellipse, and the startAngle and endAngle parameters control the start and end points of the ellipse (note that the start and end angle parameters are defined using radians rather than degrees). The antiClockwise parameter will draw an arc (part of a circle) in an anticlockwise direction when set to true. An example of using the arc() function is shown next:



//Render a circle
ctx.arc(100, 200, 50, 0, 2 * Math.PI, false);
ctx.fillStyle = 'Navy';
ctx.fill();


Passing a value of 0 and 2 * Math.PI for the start and end angle parameters will result in a complete circle being rendered. To render part of a circle simply supply a different value for the startAngle or endAngle parameter as shown next. This example will result in 1/2 of a circle being rendered. Figure 2 shows the rectangle, circle and arc that are rendered to the canvas.


ctx.beginPath(); 
ctx.arc(100, 300, 50, 0, Math.PI, false); ctx.fillStyle = 'Navy'; ctx.fill();
image

Figure 2
. The results of rendering a rectangle, circle and arc.


It’s important to note that a call to beginPath() was performed before the arc() function call so that each circle/arc shape stayed distinct and didn’t merge into the following one as shown in Figure 3. The beginPath() function tells the canvas to start rendering a new path while fill() ends the path (you can also add a call to
closePath() after each call to fill() if desired although it’s not required in this case).

image

Figure 3
. The result of two shapes combining as a result of not making a call to beingPath().


Lines can also be drawn quite easily. This is accomplished by using moveTo() and lineTo() along with the stroke() or fill() functions. The moveTo() function positions the virtual pen at a specific location which can then draw a line to a specific
x/y coordinate by using lineTo(). An example of drawing lines is shown in Listing 3.



ctx.beginPath();
ctx.moveTo(100, 400);
ctx.lineTo(50, 500);
ctx.lineTo(150, 500);
ctx.lineTo(100, 400);
ctx.strokeStyle = 'Red';
ctx.lineWidth = 4;
ctx.stroke();
ctx.fillStyle = 'Yellow';
ctx.fill();

Listing 3
. Drawing lines and filling an area.



The complete code for the shapes and lines rendered to this point is shown in Listing 4.

<!DOCTYPE>
<html>
<head>
    <title>Canvas Fundamentals</title>
    <script type="text/javascript">
        window.onload = function () {
            var canvas = document.getElementById('canvas');
            var ctx = canvas.getContext("2d");

            //Draw a rectangle
            ctx.fillStyle = 'Green';
            ctx.fillRect(0, 0, 200, 100);

            //Draw a circle
            ctx.arc(100, 200, 50, 0, 2 * Math.PI, false);
            ctx.fillStyle = 'Navy';
            ctx.fill();

            //Draw an arc
            ctx.beginPath();
            ctx.arc(100, 300, 50, 0, Math.PI, false);
            ctx.fillStyle = 'Navy';
            ctx.fill();

            //Draw lines
            ctx.beginPath();
            ctx.moveTo(100, 400);
            ctx.lineTo(50, 500);
            ctx.lineTo(150, 500);
            ctx.lineTo(100, 400);
            ctx.strokeStyle = 'Red';
            ctx.lineWidth = 3;
            ctx.stroke();
            ctx.fillStyle = 'Yellow';
            ctx.fill();
        };
    </script>
</head>
<body>
    <canvas id="canvas" width="800" height="600"></canvas>
</body>
</html>

Listing 4
. Rendering shapes and lines using the canvas.


There are certainly other types of shapes you can draw with the canvas including bezier and quadratic curves. They’re useful when you need to draw non-standard shapes or add rounded corners to something like a rectangle. The Mozilla canvas documentation available at https://developer.mozilla.org/en/Canvas_tutorial/Drawing_shapes provides an example of using lines and quadratic curves to render a rectangle with rounded corners (see Listing 5).


function roundedRect(ctx, x, y, width, height, radius) {
    ctx.beginPath();
    ctx.moveTo(x, y + radius);
    ctx.lineTo(x, y + height - radius);
    ctx.quadraticCurveTo(x, y + height, x + radius, y + height);
    ctx.lineTo(x + width - radius, y + height);
    ctx.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);
    ctx.lineTo(x + width, y + radius);
    ctx.quadraticCurveTo(x + width, y, x + width - radius, y);
    ctx.lineTo(x + radius, y);
    ctx.quadraticCurveTo(x, y, x, y + radius);
    ctx.strokeStyle = 'Black';
    ctx.lineWidth = 10;
    ctx.stroke();
    ctx.fillStyle = 'Lime';
    ctx.fill();
}

Listing 5
. Drawing a rectangle with rounded corners.

The roundedRect() function can be called as shown next and will render the rectangle shown in Figure 4.
roundedRect(ctx, 250, 5, 150, 150, 15);
image 
Figure 4. Using lineTo() and quadraticCurveTo() to draw a rounded rectangle.

 
The final topic that I’ll cover is text rendering. Although rendering shapes can be good for charts and a variety of other tasks, at some point you’ll want to render text to a canvas. Fortunately, that’s an easy proposition and something that’s also quite flexible since the canvas supports different types of transforms as well (scaling and rotating objects for example). To render text to the canvas you can use the fillText() method which accepts the text to add as well as the x and y coordinates of where to add it. An example of using fillText() is shown next:

ctx.fillStyle = 'Black';
ctx.font = '30pt Arial';
ctx.fillText('Drawing with the Canvas', 0, 550);
 


Summary

Although I’ve only scratched the surface of what can be done with the canvas, at this point you can see that using it to perform basic drawing functions is pretty straightforward (and much like GDI+ if you’ve worked with that). Using the canvas definitely involves a fair amount of JavaScript though. You may wonder (like I did) if there’s not an easier way of of rendering shapes especially given that you have to figure out the coordinates yourself. While the tooling story for the canvas isn’t great quite yet, there are plugins for tools such as Adobe Illustrator that can be used to export shapes and even animations to JavaScript. One example is Ai -- Canvas which is available at http://visitmix.com/labs/ai2canvas/. A project that provides a library of shapes and other samples for the canvas can be found at http://code.google.com/p/html5-canvas-graphics/. Several open source script libraries such as Flot (http://code.google.com/p/flot/) are available as well.

In future posts I’ll provide additional examples of using the canvas to generate more useful graphics that can be displayed in a web application. An example of the types of topics that I’ll cover to help teach canvas topics is shown next (download an early version of the chart here) :

image



NOTE: I’ve had a few people ask if I’m covering HTML 5 features because I’m moving away from Silverlight. The answer is, “Absolutely not!”. I still use Silverlight with a lot of customers and plan to continue using it for Line of Business applications and Windows Phone 7 applications. At the same time, my company, The Wahlin Group, does a lot of web-based projects as well (including one that’ll be released soon from Microsoft). I’m a big believer in staying up-to-speed on all the latest technologies as much as possible and personally enjoy researching and writing about the latest trends.

comments powered by Disqus

7 Comments

  • Nice write-up Dan...

    BTW, what does this mean?
    "The Wahlin Group, does a lot of web-based projects as well (including one that’ll be released soon from Microsoft)."

    The part the says " (including one that’ll be released soon from Microsoft)"

    Thanks!
    ..Ben

  • We built an HTML 5 project that demonstrates several of the new features available (code first, MVC 3, canvas, SVG, jQuery templates, CSS3, etc.). It'll be released soon I hope...they're still finalizing a few other related things. :-) You can catch it at the end of this talk at MIX: http://channel9.msdn.com/events/MIX/MIX11/HTM14

    Dan

  • I just watched the video; It's nice to see all the new stuff in HTML5, SVG, JS and etc., but it sure hurts to see four years of my life invested in SL that is slowly getting blown away. I know, I've been in IT for 3 decades and things change, but now it looks like the changes are becoming counter productive. You're no longer an expert in anything, because by the time you're, it's already obsolete and changed...

    Now, we need more tooling from MSFT for this new direction. Thanks Dan for the nice Blog!
    ..Ben

  • Ben: Yeah, the original vision of Silverlight everywhere is definitely gone it looks like. But, I still think Silverlight is excellent for LOB and media applications as well as (of course) WP7 apps. I'm hoping that XAML will continue to play a large role going into the future....we'll see.

    The new stuff (canvas and SVG) definitely has a ways to go to be widely supported (I don't think most people upgrade their browsers as often as we do :-)). And, as you mention, the tooling stinks right now for the most part so hopefully we'll see more there soon.

    Dan

  • Dan;
    <>
    That is EXACTLY what I'm betting on and tell people where SL fits perfectly. The problem that I see in MSFT, is that (unlike many other companies), the management or marketing (or whoever) make sharp turning decisions that are uncalled for and throw everything off balance. Just look at Adobe, for years they have kept their products and improve on them. People TRUST flash will be there tomorrow, but many people don't TRUST MSFT with holding on to their products. I hear this all the time from seasoned developers. For years ASP.Net WebForm was called to be the best thing, but suddenly they make MVC look like the WebForms is dead. MSFT is already killing WPF (in replacement with what?), and many companies are afraid to jump on WPF, now lost trust in SL. As a consultant, I don't know what to offer that is not killed in six months.
    I'm very concern about these sudden changes that is sending the wrong message to the corporate world. Many people thought that Scott Guthrie was the main voice behind holding the .Net development platform, and now that he has been moved, it's almost like who is connecting with developers now?
    As a consultant, I'm sure you either directly or subtly face the same concerns too.

  • Ben:

    I agree with you. The way things have been presented/marketed definitely hasn't helped consultants like you and I. That's exactly why I (and I'm guessing you as well) try to stay diverse in many different technologies. I'm still loving Silverlight as mentioned, but I'm also a big fan of ASP.NET MVC, jQuery, etc.

    Dan

  • Dan:
    I'm with you 100%. How do you deal with MVC still missing lots of components for LOB apps and maturity? I use Telerik controls (SL, ASP.Net Webforms and etc.) and they have some of the most complete and mature controls for ASP.Net Webforms that I wouldn't know what to do without them in MVC world. Do you use MVC to deliver LoB apps (for customers who don't want to be dependent on plugin like SL), instead of WebForms?
    I'm designing/developing a three tier app (DAL talking to EF, BLL talking to DAL and ObjectDataSource talking to BLL) and having everything separated and I can mock the data and do testing, all done in WebForms and I feel very comfortable with it. But when I look at MVC, I feel like throwing all that away.
    That's my hangup on MVC right now for LoB apps. What's your advice & direction on MVC and LoB apps with lots of data entry forms?

    Secondly, how do you formulate the front end using jQuery/Html/CSS to work with backend system i.e. WCF Ria Services (Yes, they just released a RIA/JS). Do foresee building complex apps with jQuery/CSS/HTML as your frontend?

Comments have been disabled for this content.