Visualizing Lord Stanley's Cup: an HTML5 experiment.

Here's a little something I whipped up over the past week. It's an interactive visualization of the winners and losers of the Stanley Cup finals since 1927. I was trying to accomplish two things: 1. learn how to use some new stuff in HTML5 (canvas, css transistions, etc), and 2. teach myself a little bit about hockey. I'm pretty psyched at how it turned out.
By the way, Internet Explorer completely barfs on this, so it's really only worth looking at if you're using Chrome, Safari, or Firefox.
How I did it
1. Start with simple HTML markup.
It seemed like the right way to start was to put all the data into something simple that would display fine on it's own, in any browser, then transform it into something more interesting with javascript and css. I came up with a simple HTML structure that showed the wins and losses for each team, and allowed for some notes for context.
<section id="sc-timeline">
<ul id="timeline-data">
<li id="calgary">
<h3 class="team-name">Calgary Flames</h3>
<div class="timeline">
<h4>Stanley Cup Wins:</h4>
<span class="win" title="1989/05/30">1989</span>,
<h4>Stanley Cup Losses:</h4>
<span class="lose" title="1986/05/30">1986</span>,
<span class="lose" title="2004/05/30">2004</span>
<h4>Notes:</h4>
<span class="note" title="1972/10/01">Founded in 1972</span>
</div>
</li>
</ul>
</section>
... then repeat the list-items for each team in the data set.
2. Build a timeline with Javascript.
Now that the raw data is on the page, I need to add some stuff to it so it will actually be interesting. I stored all the javascript to control the visualization in an class called 'timeline'
var timeline = function() {
this.init = function(el) { ... setup stuff goes here ... }
... more logic goes here ...
}
Then I created an instance of the class, and run an initialize function that adds in the extra HTML we need for the visualization.
var sc_timeline = new timeline();
sc_timeline.init($('SECTION#sc-timeline'));
Rather than paste in all the code here, which would be overwhelming, have a look at the source.
In the init function, three bits of extra markup are added to the existing html, so the structure now looks like this:
<section style="width: 1010px;">
<ul id="timeline-data">
<li>...</li>
</ul>
<!-- this stuff below is added by the timeline.init() function. -->
<canvas class="timeline-canvas" height="270" width="1010">
<ul class="timeline-bar">
<li>1927</li>
<li>1928</li>
... etc.
</ul>
<div class="timeline-titles"></div>
<div class="timeline-notes"></div>
<section>
All of that is super easy with jQuery.
3. Add some CSS
Now, all the markup is there, but it's pretty ugly. In a CSS file, I floated all the list items so they displayed horizontally, hid the raw data (wins, losses, notes), positioned the new timeline-title and timeline-notes DIVs on top of the CANVAS element, and added team logos to each LI in the timeline-data list (using the team_logo_nav.jpg sprite)
It's all really straightforward CSS, but there's one thing I want to point out. When you hover over the team logo, I used a CSS transition to give some animation to the background position. It's the first time I've ever used that, and I like it a lot. I can imagine some really cool stuff that can be done with it. (too bad it only works in Safari and Chrome, currently)
-webkit-transition: background-position 0.3s linear;
4. Add interactivity
There are two bits of interactivity I want on this thing. First, you should be able to mouse over the team logos and display the years they were in the finals, along with any notes. Second, and inversely, you should be able to mouse over the years in the timeline and see which teams played on any given year.
In the timeline setup, I attach the actions to the list items:
this.setup_timeline_actions = function() {
public_instance = this.public_instance; // this is a reference to this instance that we can attach to HTML elements that need to refer back to it.
this.teams.find('LI').hover(function() {
public_instance.draw_timeline_links(this);
});
this.years.find('LI').hover(function() {
public_instance.draw_matchup(this);
});
}
I won't go through the ins and outs of how I did the highlighting of the corresponding list items. It's basically finding the matching years or teams by filtering the list for an ID or title attribute, then adding a 'selected' class to them and writing the team name to the timeline-titles DIV element.
Oh, and then I got to the fun part...
5. Drawing with CANVAS.
I think the most interesting part of the visualization is the bars that link the teams to their win or loss years. I did it with the HTML5 CANVAS element, which is basically a scriptable image tag that you draw on programmatically. It's not difficult, but it's way different than basic DOM scripting like I'm used to.
To make it work the way I wanted, I sized the canvas to be exactly as wide as the list items. Then I could easily to draw links between the two rows, just by finding how far the corresponding elements were from the left edge.
this.draw_connection = function(team,year,color) {
// this.ctx() is a reference to the context object of the canvas.
this.ctx().globalAlpha = 0.2;
this.ctx().fillStyle = color;
// get the coordinates
var l1 = team.position().left;
var l2 = year.position().left
var r1 = l1 + team.width();
var r2 = year.width() + year.position().left;
var h = $(this.stage.canvas).height();
// draw the connection
this.ctx().beginPath();
this.ctx().moveTo(l1,0);
this.ctx().lineTo(r1,0);
this.ctx().lineTo(r2,h);
this.ctx().lineTo(l2,h);
this.ctx().fill();
}
When I loop through all the teams and draw their connections, with blue bars for wins, and red bars for losses, it creates a pretty neat effect:

Drawing notes is pretty simlar, I drew a line on the canvas, then positioned the note text on top of it, left-aligned to the line:
var year_pos = this.stage.years.find('LI#y' + year).position().left;
var h = $(this.stage.canvas).height();
this.ctx().strokeStyle = '#00ff00';
this.ctx().globalAlpha = 1;
this.ctx().beginPath();
this.ctx().moveTo(year_pos, 0);
this.ctx().lineTo(year_pos,h);
this.ctx().stroke();
this.stage.notes.append(''+ note.note + '');
Creating a note layout that looks like:

From there, it's just a matter of clearing the canvas whenever a year or team is hovered, then drawing the connections all over again, but just with the selected year.
Again, here's the finished product. View the source to see the details of the javascript.
Learn more about working with the CANVAS tag.
Learn more about CSS transitions.
Update: The guys at FanSnap had me build a timeline for the NBA finals. I made some design refinements with this one and made it work in IE. By "made it work in IE", I mean "made myself feel really dirty while making it look only halfway-passable in IE".