Archive for the ‘JavaScript’ Tag

Return key binding for Knockout.js

I’ve become a big fan of Knockout.js for building dynamic web interfaces. It’s one of the most “magical” frameworks I’ve seen since jQuery and Node.js, in that it’s great at what it does and makes building complex model/view interactions a breeze. Knockout provides a clean and quick event binding scheme, which allows you to work faster and write less code. However, making things faster and cleaner leads to… wanting even more speed and cleanliness!

So it was that I got tired of writing clunky controller scripts for managing text field input submission when the RETURN key is pressed. This seemed like an excellent opportunity to expand upon the Knockout framework to simplify this redundant task.

Here it is, just paste this into your project script somewhere after the Knockout.js include:

/**
* RETURN key binding for Knockout.js
*/
ko.bindingHandlers.returnKey = {
 init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
 ko.utils.registerEventHandler(element, 'keydown', function(evt) {
 if (evt.keyCode === 13) {
 evt.preventDefault();
 evt.target.blur();
 valueAccessor().call(viewModel);
 }
 });
 }
};

Once you’ve included that extension in your page’s JavaScript, you’ll now have a “returnKey” data binding available. You can use it like this:

<input type="text" data-bind="value:message, returnKey:send"/>
<button data-bind="click:send">Send</button>
That's it!

Thematic Maps with Polygon Overlays

I’ve been building several data visualization maps recently for a variety of clients. In each case, the client has been looking for standard thematic choropleth maps where statistics are illustrated by shades of color among the map locations (ie: countries, states, etc). A few years ago we would have done this with Flash. Now though, there are several options for building JavaScript-based thematic maps using platforms like Google Maps or MapBox.

There are generally two approaches to creating thematic maps in HTML5: either create a pre-rendered tile set with all of your location data rendered into the map tiles and load that into a lightweight tile client (such as Modest Maps), or else use a simple base map with colored polygon shapes placed on top. Now, I would strongly encourage using pre-rendered tiles if possible. They’re becoming easy to make (thanks to applications like TileMill), and the performance of the pre-rendered graphics is superb. Polygon overlays are heavy and slow by comparison, although they do offer flexibility for rendering dynamic data sets on the fly using only front-end code. This is particularly useful when your data is frequently changing, or you if have limited access to a site’s technology infrastructure. I’ve ended up going with map polygons a few times recently… and they turned out to be trickier than I would have expected. First off, have a look at some polygon-based map demos that I put together, and feel free to grab their source code for your own use. Also, consider if your requirements are simple enough to just use Google Geocharts (which are easy and free, although not the most attractive or customizable solution).

The first thing you’ll need for a shape-based map is a client that supports polygon overlays, such as Google Maps, Leaflet, or another major platform. Realistically, the platform should natively support multi-polygons (ie: groups of polygons that behave as a single shape, for circumstances such as an archipelago), otherwise you’ll have to do grunt work to track multi-shape countries. Frameworks like Bing Maps and Mapstraction currently do NOT support multi-polygons, therefore they are probably not the best fit for polygon maps. Google Maps is outstanding with its canvas-based overlay renderer, although it now includes that pesky price tag which tarnishes its appeal. Therefore, I’ve found Leaflet to be a superb open-source alternative. While Open Layers is certainly an option, I’ve found its polygon features to be a bit clunky.

Next, you’re going to need polygon point data to draw onto your map. While there are countless sources of polygon data available online, most are formatted as Esri shape files. You’ll probably want Google-encoded polylines or raw point arrays if you’re adding your own shapes to a map using JavaScript. You can find point data for countries and U.S. states here. Note that there are both low and high-res country shapes available: you’ll probably want to detect a user’s browser and provide low-res shapes to older versions of IE so that they don’t choke.

To put the polygons on the map, you’ll need to decode the Google polylines and then add the point arrays to the map. Follow along with this script, and see its working demo here. For applying colors to your map, refer to this function:

// Gets a color for a specific value.
getColorForValue: function(value) {
    if (!isNaN(parseFloat(value)) && isFinite(value)) {
        if (value <= 20) {
            return "#aed0da";
        } else if (value <= 40) {
            return "#68c2e7";
        } else if (value <= 60) {
            return "#00a9df";
        } else if (value <= 80) {
            return "#00719f";
        } else if (value <= 100) {
            return "#003060";
        }
    }
    return "#ccc";
}

You’ll see that the above receives a location’s value and returns a color based on a pre-determined set of range breaks. You can customize those breaks, or hook them into a dynamic data source tailored to a specific data set.

This should get you up and running with a quick-and-dirty thematic map. Good luck and happy visualizing! You can see an implementation of a thematic map built using Google Maps at the Nuclear Materials Security Index. The custom map tooltip seen there is also available within the maps repo.

Snapping a Point to a Line Segment

Following up yesterday’s post on hit-testing a point within a polygon, there’s one other key geometric function that the Lassie engine’s motion grid would required in order to support polygonal motion areas: that’s the ability to snap stray (out-of-grid) clicks onto grid lines.

This took a bit of digging around to get working correctly… specifically, I wanted the point to snap to a line segment, therefore the snapped point’s position needs to be limited by the extent of the segment’s two end points. Ultimately, the operation turned out to be pretty compact:

// Snaps point P to the nearest position along line segment AB.
// @param p: The point to snap.
// @param a: First point of the line segment.
// @param b: Second point of the line segment.
// @return: A new point object with "x" and "y" coordinates.
function snapPointToLine(p, a, b) {
    var ap1 = p.x-a.x,
        ap2 = p.y-a.y,
        ab1 = b.x-a.x,
        ab2 = b.y-a.y,
        mag = ab1*ab1 + ab2*ab2,
        dot = ap1*ab1 + ap2*ab2,
        t = dot/mag;

    if (t < 0) {
        return {x: a.x, y: a.y};
    } else if (t > 1) {
        return {x: b.x, y: b.y};
    } else {
        return {x: a.x+ab1*t, y: a.y+ab2*t};
    }
}

Give it a try:

var a = {x:0, y:0};
var b = {x:50, y:50};
snapPointToLine({x:0, y:50}, a, b); // {x: 25, y: 25}
snapPointToLine({x:100, y:100}, a, b); // {x: 50, y: 50}

Polygon Hit Test with Ray Casting

Another old project that I’ve been digging back into with JavaScript recently has been the Lassie engine’s geometry system. Now, first and foremost, that’s NOT to say that the Lassie engine is in production for HTML5 (coding in my free time is not among my current priorities!). However, I’ve always found Lassie’s point-and-rectangle grid system to be archaic, and in all honesty, the only reason for that is because I’ve never taken the time to figure out polygon math. So, I’ve been tinkering with this…

The first thing that Lassie’s grid system would need to support polygons is a way to hit-test points against a polygonal area. A quick Google search on this led to an old (and shockingly simple) technique called ray casting.

The premise of ray casting involves extending a ray in one direction from the target point, and then counting how many times that ray intersects sides of the polygon in question. If the intersection count is an even number, then we can assume that the ray has both entered and exited the polygon’s boundaries an equal number of times, therefore placing the point outside of the polygon. However, if the intersection count is an odd number, then the point has entered but never left the bounds of the polygon and is therefore inside. Amazing that such a simple test holds up.

In JavaScript:

// Tests for counter-clockwise winding among three points.
// Specifically written for an intersection test:
// Uses ">=" (rather than ">") to cast equal points as valid CCW components.
function ccw(x, y, z) {
    return (z.y-x.y) * (y.x-x.x) >= (y.y-x.y) * (z.x-x.x);
}

// Tests for intersection between line segments AB and CD.
function intersection(a, b, c, d) {
    return ccw(a, c, d) !== ccw(b, c, d) && ccw(a, b, c) !== ccw(a, b, d);
}

// Performs ray casting, testing if point P falls within a polygonal region.
// @param p: The point to test.
// @param poly: An array of points forming a polygonal shape.
// @return: true if point falls within polygon.
function hitTestPolygon(p, poly) {
    var sides = poly.length,
        origin = {x:0, y:p.y},
        hits = 0,
        s1,
        s2,
        i;

    // Test intersection of an external ray against each polygon side.
    for (i = 0; i < sides; i++) {
        s1 = poly[i];
        s2 = poly[(i+1) % sides];
        origin.x = Math.min(origin.x, Math.min(s1.x, s2.x)-1);
        hits += (intersection(origin, p, s1, s2) ? 1 : 0);
    }

    // Return true if an odd number of hits were found.
    return hits % 2 > 0;
}

You’ll see our test uses three methods. First, ccw() tests for counter-clockwise winding among three points. Using that, we can test intersection() between two line segments by testing if the points of either segment wind into one another. Finally, we can perform ray casting by testing a target point’s line to an exterior point against each line segment in the polygon, and counting the number of intersections that are registered. Keep in mind that point order of your polygon array does matter (although the hit test still seems to work for oddly-wound polygons with intersecting sides). To see the hit test in action, try this:

var poly = [
    {x:10, y:10},
    {x:100, y:25},
    {x:125, y:125},
    {x:25, y:100}
];
hitTestPolygon({x:50, y:50}, poly); // true
hitTestPolygon({x:15, y:50}, poly); // false

Color Transform for HTML5 Canvas

Interest in more information on applying color transformations with HTML5 Canvas came from my last post. If you’re coming from Flash, the concept of color transforms should be pretty familiar. The canvas method works pretty much the same way that Flash’s ColorTransform object worked (or what the color tint interface configured within the Flash IDE). All code needed is below; this snippit is largely based on the operation from easel.js.

// Apply color transform.
var r = 115, // Red tint (0-255)
    g = 208, // Green tint (0-255)
    b = 189, // Blue tint (0-255)
    t = 0.6, // Tint strength (0-1)
    i,
    ctx = document.getElementById('myCanvas').getContext('2d'), // Get the drawing context of your canvas element.
    img = ctx.getImageData(0, 0, 100, 100), // Pull a rectangle of image data from context
    data = img.data, // Image image data array.
    len = data.length; // Length of data array.

// Loop through image data array.
// Apply color trasform to each block of RGBA values.
// Applied as: c = c * cmodifier + coffset.
for (i = 0; i < len;) {
    data[i] = data[i++] * (1-t) + (r*t);
    data[i] = data[i++] * (1-t) + (g*t);
    data[i] = data[i++] * (1-t) + (b*t);
    i++; //data[i] = data[i++] * 1 + 0; << skip alpha component. Adjust as needed.
}

// Restore image data within the canvas.
ctx.putImageData(img, 0, 0);

Once Flash, Now JavaScript

The fishing mini-game featured in “What Makes You Tick: A Stitch in Time” is back, and this time it’s not running in Flash. It’s running in HTML5 using the canvas tag… and it was a pretty easy migration. First, have a look at the Flash and HTML versions of the game; they’re at:

Flash: http://www.lassiegames.com/games/stitch_fishing/play_1024.php
HTML5 Canvas: http://www.lassiegames.com/tests/fishing/

Pretty cool, right? There’s no two ways about it, the canvas tag is quite capable with fast image compositing. So, let’s have a look at how this Flash game turned Canvas.

Interface graphics

There are several big graphics used in the game’s interface. The background image and the intro screen are both just big image plates applied to HTML elements. All interface elements (buttons, text displays) are just HTML. When the actual game starts running, a full-screen Canvas element is placed on top of the background image, and the game starts drawing itself at 30 frames a second. Once the game ends, the canvas is pulled from the DOM and we go back to simple HTML/CSS layout for the replay options.

Basic game graphics

The game graphics all draw from a single sprite sheet, which you can see here. You’ll see that there’s one of each of the fish (green and yellow), multi-frame animation states for the animating jellyfish and the fishing net, and then some assorted interface elements. In most cases, rectangular regions are pulled from this sprite sheet and composited directly to the canvas on the fly. The big exception here are the green fish…

As the game runs, notice that the green fish have an implied visual depth: they get smaller and tinted with a pale shade of green further down in the layer stack. This effect is achieved with scale and pixel color transformations, both of which are expensive operations to be running every frame. However, these effects can be pre-rendered. Scroll down and look below the fishing game on the page. You should see a line of 15 green fish images, each getting slightly bigger and darker from left-to-right. These are the actual fish images being used within the game. As the game launches, the original green fish image is pulled from the sprite sheet and rendered with transformations onto these 15 individual canvas elements which are created on the fly. Then as the game runs, the pre-rendered fish images can be quickly composited into the game canvas without re-processing these additional transformations. Keep in mind that those individual fish images technically do NOT need to be displayed within the DOM for this to work (they’re just being displayed for demonstration purposes). Normally, you’d just store these pre-rendered elements within memory.

As for the fishing line, that’s just a simple vector arc drawn from a fixed point at the top down to the dynamically plotted mouse position. A fixed control point for the line’s arc is set a few hundred pixels below the origin to create a curve.

Animations

Animations are certainly the trickiest part of post-Flash graphics. Again, if you refer to the game’s sprite sheet you’ll see multiple graphical states drawn for the undulating jellyfish and the lifting net. The concept of animation is pretty simple: we’re just going to change the rectangular region pulled from the sprite sheet every few frames. The implementation is a bit trickier though. Animations generally require an independent interval which runs at a fraction of the main framerate; this allows objects to move around the screen at 30fps while only changing visual state, say, every three frames (10fps). One trick here is to use a modulus (division remainder) of the main clock to maintain a subset interval. Mind you, I was experimenting with some home brew animation techniques while building the Fishing game so reinvented a few wheels. There are plenty of JavaScript-based animation frameworks (like Cocos 2D) which will take care of this grunt work for you.

Sound

Now, I will admit that I cheated on the audio. I was underwhelmed by the capabilities of the HTML5 audio tag for playing sound effects in rapid succession, so I just set up a Flash player that plays effects at the command of JavaScript.

Program

All told, I ended up keeping the JavaScript architecture very similar to the original Flash architecture. In Flash, there’s a HookableObject abstract that extends into several implementations (Fish, Jellyfish, Boot, etc). In JavaScript, this same mapping was used with HookableObject serving as the prototype for Fish and Jellyfish. Beyond that, most object relationships between, say, Hook > Fish > Net didn’t really need to change. In fact, the single biggest change required involved the display list. In Flash, it was easy to reconfigure/reorder the pond as needed during an update pass, and then see all graphics magically update in accordance during Flash’s next render pass. This was useful when a fish swam off screen… anytime a fish left play and was ready for reset, there was a small probability offered for it to upgrade itself to “gold” status and swim the next pass as a bonus fish at the top of the stack. Unfortunately, this was a lot more complicated in JavaScript where I was trying to perform application update and rendering in a single pass, and therefore update order and rendering order were dependent upon one another. Rather than splitting rendering into a second pass, I opted to just change the delegation of bonus fish so that only fish at the top of the stack were eligible for upgrade to gold status.

So, that’s the gist of the HTML5 fishing game. Fun stuff. Enjoy the game, and feel free to download the complete project code from its GitHub repo.

SWFAddress revisited

It’s been a year since I’ve done anything with SWFAddress, and in that time I’ve received many support questions in response to my last blog post on the subject. So, now that I’m back into SWFAddress on a new project, I’ve finally gotten around to posting a sample implementation of a SWFAddress proxy class in my previous post, available here:

https://lassieadventurestudio.wordpress.com/2008/10/09/better-swfaddress/

I hope it’s useful!