All Aboard AJAX, HTML Canvas, and the Supertrain
by Dave Hoover
|
Pages: 1, 2, 3, 4, 5, 6
Now when I point my browser to http://localhost:8053/train/line and repeatedly refresh, I see the data changing! It shows the apparent progress of a train heading south from Woodinville to Redmond, along with a train heading north from Redmond to Woodinville.

Figure 3.
Next I will AJAX-ificate the redwood.html page to save me from having to repeatedly click Refresh. To accomplish this, I'll use the insanely simple Prototype library.
docroot/redwood.html
<html>
<head>
<script type="text/javascript" src="prototype-1.4.0.js"></script>
</head>
<body>
<div id="status"></div>
<script type="text/javascript">
new Ajax.PeriodicalUpdater($("status"), "/train/line")
</script>
</body>
</html>
Pointing my browser to http://localhost:8053/redwood.html, I now see my trains' status updating every 2 seconds (the default polling period for Prototype's Ajax.PeriodicalUpdater). Cool! Unfortunately my customer won't be so easily impressed. It's time to turn that server-side state into dynamically updating client-side graphics. I'll continue taking small steps, so, like any good railroad project should, I'll start with some tracks:
docroot/redwood.html
...
<body>
<canvas
id="redwood"
width="500"
height="120"
style="border: 1px solid black">
</canvas>
<script type="text/javascript">
var tracks = {
north: new Track(30),
south: new Track(85)
}
var canvas = undefined
// IE will return false here
if ($("redwood").getContext) {
canvas = $("redwood").getContext("2d")
drawTracks()
}
function drawTracks() {
$H(tracks).values().each(function(track) {
track.draw()
})
}
function Track(y) {
this.y = y
this.startX = 10
this.endX = 490
this.tieSize = 3
this.tieGap = 5
this.draw = drawTrack
}
function drawTrack() {
canvas.moveTo(this.startX, this.y)
canvas.beginPath()
var x = this.startX
while (x < this.endX) {
canvas.lineTo(x, this.y)
canvas.lineTo(x, this.y + this.tieSize)
canvas.moveTo(x, this.y)
canvas.lineTo(x, this.y - this.tieSize)
canvas.moveTo(x, this.y)
x = x + this.tieGap
}
canvas.closePath()
canvas.stroke()
}
</script>
<div id="status"></div>
...

Figure 4.
Note that I'm not using the standard JavaScript for loops. Since I'm using the Prototype (1.4.0) JavaScript library for AJAX, I'm taking advantage of its Ruby-like collection iterators and syntactic sugar: $(), $H().values(), and each(). Now that my tracks are laid, I need to drop in some trains. First, I'll remove my Ajax.PeriodicalUpdater example, replacing it with a window.setInterval call (setInterval is an essential ingredient of creating a dynamic canvas). I'll also refactor drawTracks into a higher-level updateCanvas function:
docroot/redwood.html
...
if ($("redwood").getContext) {
canvas = $("redwood").getContext("2d")
window.setInterval(updateCanvas, 1000 * 2)
updateCanvas()
}
function updateCanvas() {
clearScreen()
drawTracks()
}
function clearScreen() {
canvas.clearRect(0, 0, $("redwood").width, $("redwood").height)
}
function drawTracks() {
...