Posts Tagged ‘gfx’

June 19th, 2009 Comments Off
Tags: , ,

Category: Articles, News, Tips, Vetted Links

Dojo Tips and Tricks

Many months ago, I wrote an article for InsideRIA that provided a topical look at some of the more useful things that Dojo provides you entitled Dojo Tips and Tricks. I’d actually forgotten that I even wrote the article so it was a surprise when a colleague wrote me earlier this morning and mentioned it.

It’s a bit more diverse than some drive-by-tutorials in that it digs into DojoX just a little bit and mentions the build system, so even if you’ve been using some of the core tools for a while now, you may still learn a few things from a quick read.

In between the time when the article was finished and now, Dojo has since updated to version 1.3 so you’ll unfortunately see a few CDN URLs that point to version 1.2; however, upgrading the URLs shouldn’t break anything. The latest and greatest is usually a great place to start, so I always encourage folks to stay up to date.

If you’re coming to OSCON this summer, be sure to drop by my 3 hour tutorial for a closer look at many of the great tools that Dojo provides.

March 20th, 2009 6 Comments »
Tags: , ,

Category: Articles

Dojo, Now With Drawing Tools (Linux Journal Reprint)

This post is a “reprint” of a feature article I wrote for the February 2009 edition of Linux Journal, entitled “Dojo, Now With Drawing Tools.” The article text was minimally adapted from the printed form, so no extensive hyperlinking or web layout has been done. Per my contract with Linux Journal, the article must be published under a FSF Free Documentation License if I reproduce it, so I hereby republish it under the BSD Documentation License. (*Strikes Gavel*) Enjoy.

Internalizing Dojo’s “write once, deploy anywhere” philosophy, Dojo’s gfx (pronounced “g-f-x” or sometimes “graphics”) library packs a powerful 2-D drawing API that’s capable of plugging in to an arbitrary renderer. Out of the box, it works with Canvas, Silverlight, SVG and VML, so regardless of which browser your application is ultimately viewed within, gfx has you covered.

My article Dojo: the JavaScript Toolkit with Industrial-Strength Mojo in the July 2008 issue of Linux Journal illustrated how Dojo significantly lowers the amount of effort it takes to develop a cross-browser Web application by normalizing so many of the yucky aspects of Web programming, such as DOM manipulation, non-uniform aspects of the JavaScript across browsers, and repetitive tasks, such as styling nodes, performing AJAX requests and so forth. With that working knowledge, let’s turn to Dojo’s gfx library—a much more specialized aspect of the toolkit that’s expressly designed to give you 2-D drawing tools that can be used to do anything from producing a cool-looking reflection of an image to creating an animated game to rendering a drag-and-drop graph.
Figure 1. An example of a slick effect gfx can produce on an image.

So that you better understand exactly where gfx fits into the larger toolkit, recall that Dojo breaks down into roughly five components: Base, Core, Dijit, DojoX and Util. Base is the tiny dojo.js file that contains hard-live-without library code for common operations; Core includes most of the programmatic machinery for the toolkit; Dijit is an assortment of turnkey widgets; DojoX provides a collection of specialized subprojects; and Util provides a testing framework and scripts for tasks, such as minifying and consolidating JavaScript and CSS files. The gfx library is one of those many specialized subprojects that lives under the DojoX umbrella.

Figure 2. A Conceptual Portrayal of Dojo’s Functional Architecture

Note: A common misunderstanding is that everything within DojoX is experimental or necessarily unstable. Although there certainly are some alpha-quality subprojects within the DojoX namespace that you wouldn’t want to rely on for long-term production scenarios, several DojoX subprojects (including gfx) are quite ready for mainstream use. In general, you should be able to check a project’s README file to determine information about its status.

A Minimal Development Template

In order to demonstrate the various drawing concepts as clearly as possible, all of the examples you’re about to see will plug right in to the following minimal HTML page. Although you’re encouraged to download the entire toolkit eventually, so you have full access to the source code whenever you need it, let’s take advantage of the version that’s hosted on AOL’s Content Delivery Network, as it’s quicker to get up and running. The latest version of Dojo at the time of this writing is 1.2, so the minimal effort to put Dojo to work is the following page, which uses a script tag to cross-domain load the toolkit:

<html>
  <head>
      <title>Minimal Development Template</title>
      <script
          type="text/javascript"
          src="http://o.aolcdn.com/dojo/1.2/dojo/dojo.xd.js">
      </script>
      <script type="text/javascript">
          dojo.addOnLoad(function() {
              /*Add Dojo-dependent logic
                here to avoid race conditions*/
          });
      </script>
  </head>
  <body>
  </body>
</html>

With the minimal template in place, it is trivial to load the gfx module and start drawing. The next section digs right in to various aspects of the API, but just so you can see where we’re heading, consider the modification to the template that instantiates a 600×600 pixel drawing surface and draws a line from the upper-left corner to the lower-right corner shown in Listing 1.

Listing 1: A Minimal Development Template

<html>
  <head>
      <title>Square with a Diagonal Line</title>
      <script
          type="text/javascript"
          src="http://o.aolcdn.com/dojo/1.2/dojo/dojo.xd.js">
      </script>
      <script type="text/javascript">
          dojo.require("dojox.gfx");
          dojo.addOnLoad(function() {
              var node = dojo.byId("surface");
              var surface = dojox.gfx.createSurface(node, 600, 600);

              surface.createLine({
                  x1 : 0,
                  y1 : 0,
                  x2 : 600,
                  y2 : 600
              })
              .setStroke("black")
              ;
          });
      </script>
  </head>
  <body>
      <div style="width:600;height:600;border:solid 1px"
       id="surface"></div>
  </body>
</html>

Figure 3. A 600×600 Drawing Surface with a Diagonal Line Drawn through It

Although quite simple, the previous example taught us that the origin of the drawing surface is the upper-left corner with positive axes extending down and to the right, and that you can place a drawing surface into an arbitrary page element. Although not directly stated, the latter implies that you can have multiple drawing surfaces on a single page.

It’s also worth noting that the style applied to the div element in no way applies to the gfx surface that is created. Internally, what happens is that the surface is created and placed inside of the div; thus, the containing div exhibits a 600×600 size with a visible border around it, and the surface that is placed into the div just so happened to be 600×600 pixels also. Without using Firebug to inspect the DOM, that may not have been obvious, so hopefully, mentioning it here avoids any confusion.

An additional aspect of this simple demonstration that’s important to note is that the browser was detected and a default drawing renderer was assigned automatically without any special intervention. In the case of a Gecko- or KHTML-based browser, like Firefox or Konqueror, SVG is used as the default renderer; Internet Explorer defaults to VML.

Figure 4. The gfx library’s flexible design provides a uniform API that supplies a uniform abstraction on top of the most common drawing engines in the mainstream. Because it internally detects the drawing engine that’s available, it works right out of the box.

Silverlight and Canvas can be configured to run on supported platforms via a gfxRenderer configuration switch supplied to djConfig via the script tag that loads Dojo into the page. For example, to instruct Firefox to use Canvas as the renderer you would provide the following script tag:

<script
    type="text/javascript"
    djConfig="gfxRenderer:'canvas'"
    src="http://o.aolcdn.com/dojo/1.2/dojo/dojo.xd.js">
</script>

All Shapes and Sizes

The gfx API exposes a number of intuitive functions for common operations, such as creating rectangles, circles, lines, polylines and paths that are loosely based on the SVG standard as well as a set of custom attributes, such as stroke, fill color, rounded corners and more. Most of the methods support “chaining syntax”, which allows you to operate on the results of the previous operation repeatedly, leading to crisp code and clean syntax, so long as you do not abuse the device (Listing 2).

Listing 2. An Assortment of Shapes

dojo.addOnLoad(function() {
    var node = dojo.byId("surface");
    var surface = dojox.gfx.createSurface(node, 600, 600);

    surface.createEllipse({
        cx : 300,
        cy : 300,
        rx  : 50,
        ry  : 100
    })
    .setFill("yellow")
    ;

    surface.createRect({
        x : 90,
        y : 90,
        width : 50,
        height : 170
    })
    .setFill([255,0,0,0.5])
    ;

    surface.createCircle({
        cx : 400,
        cy : 200,
        r  : 50
    })
    .setFill([255,0,0,0.5]);

    surface.createCircle({
        cx : 425,
        cy : 225,
        r  : 50
    })
    .setFill([0,255,0,0.5])
    ;

    surface.createCircle({
        cx : 425,
        cy : 175,
        r  : 50
    })
    .setFill([0,0,255,0.5])
    ;

    surface.createPolyline([
            100,400,
            200,300,
            350,350,
            500,350
    ])
    .setStroke({
        width : 10,
        join : "round",
        cap : "round"
    })
    ;

    surface.createCircle({
        r : 50,
        cx : 200,
        cy: 200
    })
    .setFill({
        type: "radial",
        cx : 200,
        cy: 200,
        r:50,
        colors: [
            {color:"white",offset:0},
            {color:"red",offset:1}]
    })
    ;
});

Figure 5. Dojo’s fairly intuitive gfx API makes drawing a variety of customized elements easy and fun.

Hopefully, the code mostly speaks for itself. The various types of objects that you can create are usually framed in the same way that they are presented in grade school. For example, a circle has a center point and a radius defined by cx, cy and r. Given a circle, you could set a fill color in a number of different ways: a string value, an rgb(a) tuple or even something more complex like a radial gradient with custom parameters of its own.

3×3 Matrix Transforms

Using a well-designed API with nice mnemonic devices is useful for much of the routine drawing you’ll be doing, but what about when you need to do something a lot more in depth? Although this is where a lot of JavaScript graphics libraries fall short, gfx absolutely shines here by equipping you with the ability to perform arbitrary 3×3 matrix transformations.

Just in case you don’t have a background with graphics, it may not be immediately apparent how 3×3 matrices and “all of that math” is useful. Basically, 3×3 matrices provide a compact way to express the three common operations that you do with objects all at the same time:

  • Translation: adjusting the position of an object in the x and y directions.
  • Rotation: adjusting the position of an object in the clockwise or counterclockwise directions usually (but not necessarily) around its center point.
  • Scaling: adjusting the size of an object by a scalar multiplier.

Don’t freak out quite yet if you’re not a math buff and don’t want to sink time into re-learning linear algebra just to get started with that great idea you had for a game or drawing application. Many of the common operations for manipulating shapes come with intuitive wrappers. To illustrate a trivial example, let’s assume that you want to draw a square but then rotate it around its center point so that it looks like a diamond:

dojo.addOnLoad(function() {
    var node = dojo.byId("surface");
    var surface = dojox.gfx.createSurface(node, 600, 600);

    rect1 = surface.createRect({
        x: 200,
        y: 200,
        width : 200,
        height:200
    })
    .setFill("red")
    .setTransform([dojox.gfx.matrix.rotategAt(45,300,300)])
    ;
});

With an upper-left corner at point (200,200) and a width and height of 200 pixels, the square originally was centered on the surface. Then, applying a 45-degree rotation around the square’s center point of (300,300) rotated it in place.

Figure 6. The effect of drawing a square and rotating 45 degrees around its center point.

To illustrate the effect of successively applying transformation matrices, let’s draw the very same diamond but rely on explicit translation to position it in the center of the surface before rotating it versus positioning it via the createRect function:

dojo.addOnLoad(function() {
    var node = dojo.byId("surface");
    var surface = dojox.gfx.createSurface(node, 600, 600);

    rect1 = surface.createRect({
        /* x and y default to (0,0) */
        width : 200,
        height:200
    })
    .setFill("red")
    .setTransform([
        dojox.gfx.matrix.translate(200,200),
        dojox.gfx.matrix.rotategAt(45,100,100)
    ])
    ;
});

In general, it is immensely more convenient to draw most shapes initially in a coordinate system with perpendicular x and y axes and then apply final positioning via translation and rotation. An important technicality to be aware of with successive transformations, however, is that the order in which the transforms are applied does matter, and the original position of the object is normally the point of reference. For instance, in the previous example, the shape explicitly was translated as 200 pixels in the x and y directions, but its original center point from before the translation is applied serves as the basis of rotation.

If you’re unconvinced that a shape as simple as a diamond would benefit much from the convenience of matrix transforms, just consider the extra work involved in calculating the exact coordinates for its corners, and you’ll quickly see that it’s easier to reason about “rotated squares” than it is about “native diamonds”.

Manipulating Groups

It won’t be long before you’ll find that it’s far more convenient to transform entire groups of objects instead of applying individual transforms to each object in the group. Let’s consider the task of drawing a simple arrow that is nothing more than a line with a triangle on the end of it. Although you could use a path to construct the entire arrow, take a look at how groups can be useful by combining the results from the createLine function and the createPath function (Listing 3).

Listing 3. Arrows in All Four Quadrants

dojo.addOnLoad(function() {
    var node = dojo.byId("surface");
    var surface = dojox.gfx.createSurface(node, 600, 600)

    function drawArrow(p) {
        /////////////////////////////////////////////////////
        //Create a group that can be manipulated as a whole
        /////////////////////////////////////////////////////
        var group = surface.createGroup();

        var x1 = p.start.x,
            y1=p.start.y,
            x2 = p.end.x,
            y2=p.end.y;

        var len = Math.sqrt(Math.pow(x2-x1,2) + Math.pow(y2-y1,2));

        var _defaultStroke = {
            color : "black",
            style : "solid",
            width : 1
        };

        ///////////////////////////
        //Add a line to the group
        ///////////////////////////
        group.createLine({
            x1 : 0,
            y1 : 0,
            x2 : 0+len,
            y2 : 0
        })
        .setStroke(p.stroke || _defaultStroke)
        ;

        var _arrowHeight = p.arrowHeight || 5;
        var _arrowWidth = p.arrowWidth || 3;

        /////////////////////////////////////////////////////
        //Add a custom path that is a triangle to the group
        /////////////////////////////////////////////////////
        group.createPath()
        .moveTo(len-_arrowHeight,0)
        .lineTo(len-_arrowHeight,-_arrowWidth)
        .lineTo(len,0)
        .lineTo(len-_arrowHeight,_arrowWidth)
        .lineTo(len-_arrowHeight,0)
        .setStroke(p.stroke || _defaultStroke)
        .setFill(p.stroke ? p.stroke.color : "black" )
        ;

        var _rot = Math.asin((y2-y1)/len)*180/Math.PI;
        if (x2 <= x1) {_rot = 180-_rot;}

        /////////////////////////////////////////////////////////////
        //Translate and rotate the entire group as a whole
        /////////////////////////////////////////////////////////////
        group.setTransform([
            dojox.gfx.matrix.translate(x1,y1),
            dojox.gfx.matrix.rotategAt(_rot,0,0)
        ]);
    }

    //diagonals
    drawArrow({start: {x:300,y:300}, end: {x : 435, y : 435}});
    drawArrow({start: {x:300,y:300}, end: {x : 165, y : 165}});
    drawArrow({start: {x:300,y:300}, end: {x : 435, y : 165}});
    drawArrow({start: {x:300,y:300}, end: {x : 165, y : 435}});

    //up, down, left, right
    drawArrow({start: {x:300,y:300}, end: {x : 300, y : 450}});
    drawArrow({start: {x:300,y:300}, end: {x : 300, y : 150}});
    drawArrow({start: {x:300,y:300}, end: {x : 150, y : 300}});
    drawArrow({start: {x:300,y:300}, end: {x : 450, y : 300}});
});

Attempting to calculate the three points for each of the arrows without the benefit of rotation quickly demonstrates just how laborious high-school geometry really can be; perhaps putting it to work with gfx makes it at least a little more interesting.

Figure 7. It’s generally a bit easier to think in terms of objects that have been rotated than trying to determine exact coordinates for shapes that don’t fit nicely into a simple perpendicular frame of reference.

Drag-and-Droppable Dominoes

Because it’s so common to want to interact with graphics, Dojo’s gfx library has gone a long way to do most of the legwork for you in this use case as well. To wrap up some aspects of drawing, let’s put together a little demonstration that draws a domino on the screen and then add drag-and-drop capabilities to it. As you’re about to see, the laborious part of the effort is actually drawing something interesting enough that you’d actually want to drag and drop it. The actual mechanics of making it drag-and-droppable amounts to one whole line of code.

Listing 4. Drag-and-Drappable Dominoes

<html>
  <head>
    <title>Dominoes!</title>
    <script type="text/javascript"
        src="http://o.aolcdn.com/dojo/1.2/dojo/dojo.xd.js">
    </script>
    <script type="text/javascript">
           dojo.require("dojox.gfx");
            dojo.require("dojox.gfx.move");

            dojo.addOnLoad(function() {
                var node = dojo.byId("surface");
                var surface = dojox.gfx.createSurface(node, 600, 300);

                /* Using some sane ratios for layout, construct a domino */
                function drawDomino(surface,x,y,num1,num2,_width) {

                    var surface = surface.createGroup();

                    var _width = _width || 200;

                    _height = 2*_width, _r = _width/20;

                    //draw an empty domino...
                    var rect1 = surface.createRect({
                        x : x,
                        y : y,
                        width : _width,
                        height : _height,
                        r : _r
                    })
                    .setStroke("black")
                    .setFill("black")
                    ;
                    var rect2 = surface.createRect({
                        x : x+ _r/2,
                        y : y+ _r/2,
                        width : _width -_r,
                        height :_height -_r,
                        r : _r
                    })
                    .setStroke({width: _r/4, color: "white"})
                    ;

                    var line = surface.createLine({
                        x1 : x + _r/2,
                        y1 : y+_height/2,
                        x2 : x+ _width - _r/2,
                        y2 : y+_height/2,
                    })
                    .setStroke({width: _r/4, color: "white"})
                    ;

                    //now, fill in the dots

                    //numbers 1,3,5 have dots in the center
                    if (num1 == 1 || num1 == 3 || num1 == 5) {
                        surface.createCircle({
                            cx : x+_width/2,
                            cy : y+_height/4,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                    }
                    if (num2 == 1 || num2 == 3 || num2 == 5) {
                        surface.createCircle({
                            cx : x+_width/2,
                            cy : y+_height/4*3,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                    }

                    //numbers >= 2 have two of the corners filled in
                    if (num1 >= 2) {
                        surface.createCircle({
                            cx : x+_width/6*5,
                            cy : y+_height/12,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                        surface.createCircle({
                            cx : x+_width/6,
                            cy : y+_height/12*5,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;

                    }
                    if (num2 >= 2) {
                        surface.createCircle({
                            cx : x+_width/6*5,
                            cy : y+_height/12*7,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                        surface.createCircle({
                            cx : x+_width/6,
                            cy : y+_height/12*11,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                    }

                    //numbers >=4 have the other two corners filled in
                    if (num1 >= 4) {
                        surface.createCircle({
                            cx : x+_width/6,
                            cy : y+_height/12,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                        surface.createCircle({
                            cx : x+_width/6*5,
                            cy : y+_height/12*5,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                    }
                    if (num2 >= 4) {
                        surface.createCircle({
                            cx : x+_width/6,
                            cy : y+_height/12*7,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                        surface.createCircle({
                            cx : x+_width/6*5,
                            cy : y+_height/12*11,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                    }

                    //number 6 has the sides filled in
                    if (num1 == 6) {
                        surface.createCircle({
                            cx : x+_width/6,
                            cy : y+_height/4,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                        surface.createCircle({
                            cx : x+_width/6*5,
                            cy : y+_height/4,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                    }
                    if (num2 == 6) {
                        surface.createCircle({
                            cx : x+_width/6,
                            cy : y+_height/4*3,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                        surface.createCircle({
                            cx : x+_width/6*5,
                            cy : y+_height/4*3,
                            r : _width/10
                        })
                        .setStroke("white")
                        .setFill("white")
                        ;
                    }

                    return surface;
                }

                var width=50,
                    padding=50;

                for (var i=0; i <= 6; i++) {
                    var d = drawDomino(
                        surface,
                        i*75+padding,
                        2*padding,
                        i,
                        Math.floor(Math.random()*7),
                        width
                    );

                    ////////////////////////////////////////////
                    //This is all it takes to make the group
                    //drag-and-droppable!
                    ////////////////////////////////////////////
                    new dojox.gfx.Moveable(d);
                }

                ////////////////////////////////////////////
                //Ensure that the last domino moved is always on top
                //by subscribing to move notifications and
                //adjusting the z-index
                ////////////////////////////////////////////
                dojo.subscribe("/gfx/move/start", function(m) {
                    m.shape.moveToFront();
                });

            });

    </script>
  </head>
  <body>
    <div id="surface"
     style="position:absolute;width:600;height:300;border:solid1px;">
    </div>
  </body>
</html>

Figure 8. With the logic to draw the drag-and-droppable dominoes in place, now all that’s left is to write some game logic. (An exercise for the most interested of readers.)

Charting: gfx on Steroids

Perhaps the ultimate test of an API is a few good examples of what you can build with it. One of the ultimate demonstrations of gfx’s flexibility and power is Dojo’s charting library, another DojoX subproject. A comprehensive introduction of the charting library would entail an article of its own, so until that time comes, you can find some great documentation on Dojo charting from the Dojo Key Links page. And, of course, you always can read over the source, which is located in the dojox.charting module of the toolkit’s source code, if you want to get an idea of how much work goes into aligning labels, drawing tick marks and so on.

In addition to equipping you with many of the basic charts you’d want to use in a Web application, charting recently got a boost with a number of cool new features, including event support so that custom tooltips and animations can occur within charts—that kind of visual flair makes all the difference. To give you an idea of just how easy the charting API is to get up and running, consider the code blurb in Listing 5 that shows how to create a chart.

Listing 5. Preview of Dojo’s Charting API

//////////////////////////////////////////////////////
// This function demonstrates the general form of
// putting Dojo's charting API built on top of
// gfx to use. Pass in a node, customize the chart,
// and let Dojo take care of the rest
//////////////////////////////////////////////////////
new dojox.charting.Chart2D(node))
    .setTheme(dojox.charting.themes.PlotKit.blue)
    .addPlot("default", {
        type: "Default",
        lines: true,
        markers: true,
        tension: 2
    })
    .addAxis("x", {
        min: 0,
        max: 6,
        majorTick: { stroke: "black", length: 3 },
        minorTick: { stroke: "gray", length: 3 }
    })
    .addAxis("y", {
        vertical: true,
        min: 0,
        max: 10,
        majorTick: { stroke: "black", length: 3 },
        minorTick: { stroke: "gray", length: 3 }
    })
    .addSeries("Series A", [
        { x: 0.5, y: 5 },
        { x: 1.5, y: 1.5 },
        { x: 2, y: 9 },
        { x: 5, y: 0.3 }
    ])
    .addSeries("Series B", [
        { x: 0.3, y: 8 },
        { x: 4, y: 6 },
        { x: 5.5, y: 2 }
    ])
    .render()
    ;

Figure 9. An example of the charts you can draw with Dojo—no Flash required!

Figure 10. Another Example Chart Drawn with Dojo

Although only a teaser, it’s worthwhile to note the charting API focus on charting—not on raw drawing operations—so you can focus on the semantics of the task at hand instead of the implementation details associated with lower-level operations. In general, you simply provide some data that says what kind of chart you’d like, how to customize the axes and pass in the series data. Setting up event handlers, legends and other related things all work much the same way.

There’s Plenty More Where That Came From

2-D drawing is an enormous topic in and of itself, and no single article could cover all the nooks and crannies adequately. This article is designed to give you an idea of just how easy Dojo makes 2-D for the Web, which hopefully motivates you to start experimenting with the examples and check out the API docs.

Resources

January 17th, 2009 Comments Off
Tags: ,

Category: News

Dojo, Now with Drawing Tools! (Linux Journal, Feb 09)

I was really excited when I got the Feb 09 issue of Linux Journal in the mail yesterday, because they did such a fantastic on laying out the article I put together on gfx, a module for cross-browser 2D drawing. It’s a fairly meaty nine page tutorial, there’s a ton of code examples in it (each with accompanying images that show you what the code examples produce) and overall, it’s clear that they really put some thought into making it look so nice in print.

Per my contracts with Linux Journal, I’m allowed to republish articles I write under an OSS license 30 days after they’ve been on newsstands, so once that time elapses, I’ll republish it here, just like I did for Using Dojo: The JavaScript Toolkit with Industrial Strength Mojo. But seriously — don’t let that dissuage you from paying the six bucks to get this February issue. It’s packed with great articles on web dev, and is one of my favorite issues in a long time.

November 8th, 2008 Comments Off
Tags: , ,

Category: Vetted Links

Fat Dan’s (Dojo-powered) Casino

Not too long ago, I helped O’Reilly Media with a little project involving the creation of a roulette game and a slot machine to support one of their Head First books. You can check out the hosted version of these games over at Fat Dan’s Casino.

Looking back, I feel like using sprites for the slot machine icons as opposed to using separate z-axis layers would have been a lot cleaner, but this didn’t occur to me until after the fact. This implementation detail is somewhat of a moot point since it doesn’t affect the UX, but I thought I’d mention it in case you decide to hack on the code a bit.

Also, if you’re running IE, be warned that if you don’t have Silverlight installed, the roulette wheel isn’t quite as nice as it could be because VML is the default renderer and it — well, the performance characteristics of IE’s VML implementation could be a lot better. Comparing a simple operation like spinning an image using IE’s VML to Safari’s SVG is just mind-blowing.

Have fun over at the casino.

September 15th, 2008 8 Comments »
Tags: , ,

Category: Screencasts

A Simple, Degradable Reflection Widget

A nice, shiny apple

Dojo makes creating a reflection of a nice, shiny apple like this one quite easy!

This post is the first of many screencasts for this site, so please bear with me as I get more skilled at producing video, developing my big boy voice, and those sorts of things. I’ve chosen QuickTime’s H.264 format as my default for screencasts, but if there are other (or better formats), I can always re-encode or provide alternatives. Just let me know!

Viewer discretion is advised.

Reflection has been in vogue for a while now in user interfaces, and it wasn’t long ago that I saw a headline about a JavaScript library that streamlined a number of useful effects such as reflection — but alas, without the benefit of Dojo. Well, being the Dojo fanboy that I am, I immediately started to ponder how to accomplish some of these same effects using the dojox.gfx module, and a couple of hours later, I had prototyped a nice, degradable widget that seemed to do the job. By the way, that’s one of the things I love so much about Dojo: once you have a reasonable grip on it, you seldom, if ever, have to look somewhere else. More times than not, a great deal of your work is already done for you, and you just have to connect some of the dots. Remember: it’s all about the frameworks

But there’s always a catch: in this case it’s that IE users must have Silverlight installed for the widget to work; otherwise, it degrades back to the plain image. The reason is that VML, IE’s default gfx renderer, doesn’t support transparency in gradients. No transparent gradient, no nice fade out effect. Likewise, if JavaScript is not available (honestly, who does that?), it degrades down to the bare image. If you didn’t already know, the gfx module exposes an API that works across browsers by using whatever drawing engine is available. At the moment, renderers are available for canvas, VML, SVG, and Silverlight. That pretty much covers it. Adding support for another one should be straightforward: follow the API guidlines and write it.

Watch the Screencast

I’ll briefly step through some of the interesting slivers of the widget in the next section for those readers who want to skim a written synopsis and put it to work right away, but those of you who have a the time and inclination to have me talk you through it should watch the screencast (~26M, mov file.) I don’t particularly cover basic aspects of the widget lifecycle or anything like that in this post, so if you’re interested in some screencasts on those kinds of basics, please leave a comment and let me know.

Skim the Synopsis

So you’re still reading…alright, once you download the example code, you should be able to unzip the archive, open up the html file, and everything should just work. Well, unless of course you’re using Internet Explorer and don’t have Silverlight installed. Unfortunately, it doesn’t appear that VML supports transparency in gradients (the very mechanism which gets us the faded out reflective effect), so there’s not a lot that we can do there. But the good news is that it’s degradable, so the image will appear as usual on IE.

The markup required to put it to work is simply something to this effect:

<img id="apple" dojoType="dtdg.ImageReflector" src="apple.png" />

And just like any other Dojo widget, you could opt to create it programmatically with something to this effect:

var niceShinyApple = new dtdg.ImageReflector({/* props could go here */}, "apple");

If you take a look under the hood, you’ll see that it’s surprisingly simple to pull this off. First, you create a surface to draw on that is some fraction of the image’s height and place this surface just below the image itself. Then, just draw a reflection of the (clipped) image on the surface using the standard 2D matrix transform for reflection and apply a linear gradient that becomes more transparent as it approaches the bottom. That’s really it. The rest is just a little bit of software engineering.

But do note that this would be a lot harder if the dojox.gfx API (yes, it says that it’s version 0.9, but it’s up to date as far as I can tell) wasn’t so fricken’ awesome. Serious kudos to Eugene Lazutkin and all of the committers who have worked so hard on making it this good. Having full-blown 2D matrix operations at your fingertips in such an accessible way for arbitrary backends is so powerful.

Download the Goods

Just in case you weren’t paying attention, you can use the links in this very sentence to download the example code and download a copy of the screencast (to watch in the privacy of your own home.)

Leave Feedback

Again, this is my first screencast, so if there are things that you particularly liked or things that I can do to make the next one a lot better, please leave a comment and let me know!