I’m attempting to write one of the Dart demos in Javascript/Coffeescript. Everything seems to be working just fine as far as the code goes, but nothing ever appears on the canvas. I’ve tested this in Firefox and Chrome and I’m not getting any console errors or anything. I can’t quite figure this one out. Any ideas why my canvas stays blank?
Here’s my code at http://jsbin.com/orazag/1/edit
And for posterity, here it is, too.
HTML:
<!DOCTYPE html>
<!-- Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
for details. All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file. -->
<html>
<head>
<meta charset="utf-8">
<title>Solar System Demo</title>
<link type="text/css" rel="stylesheet" href="solar.css">
</head>
<body>
<h1>Solar System</h1>
<p>A solar system visualization using requestAnimationFrame.</p>
<div>
<canvas id="container" width="500px" height="400px"></canvas>
</div>
<footer>
<p id="summary"> </p>
<p id="notes"> </p>
</footer>
<script type="text/javascript" src="solar.js"></script>
</body>
</html>
CSS:
/* Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file */
/* for details. All rights reserved. Use of this source code is governed by a */
/* BSD-style license that can be found in the LICENSE file. */
body {
background-color: #F8F8F8;
font-family: 'Open Sans', sans-serif;
font-size: 14px;
font-weight: normal;
line-height: 1.2em;
margin: 15px;
}
p {
color: #333;
}
#container {
border: 1px solid #ccc;
background-color: #fff;
}
#summary {
float: left;
}
#notes {
float: right;
width: 120px;
text-align: right;
}
.error {
font-style: italic;
color: red;
}
Coffeescript:
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame
main = ->
solarSystem = new SolarSystem document.getElementById 'container'
solarSystem.start()
return
fpsAverage = null
showFps = (fps) ->
fpsAverage ?= fps
fpsAverage = fps * 0.05 + fpsAverage * 0.95
document.getElementById('notes').textContent = Math.round(fpsAverage) + ' fps'
return
class Point
constructor: (@x, @y) ->
class SolarSystem
constructor: (@canvas) ->
@renderTime = null
start: ->
@width = @canvas.parentNode.clientWidth
@height = @canvas.parentNode.clientHeight
@canvas.width = @width
@_start()
return
_start: ->
# Create the Sun.
@sun = new PlanetaryBody @, 'Sun', '#ff2', 14.0
# Add planets.
@sun.addPlanet new PlanetaryBody @, 'Mercury', 'orange', 0.382, 0.387, 0.241
@sun.addPlanet new PlanetaryBody @, 'Venus', 'green', 0.949, 0.723, 0.615
earth = new PlanetaryBody @, 'Earth', '#33f', 1.0, 1.0, 1.0
@sun.addPlanet earth
earth.addPlanet new PlanetaryBody @, 'Moon', 'gray', 0.2, 0.14, 0.075
@sun.addPlanet new PlanetaryBody @, 'Mars', 'red', 0.532, 1.524, 1.88
@addAsteroidBelt @sun, 150
f = 0.1
h = 1 / 1500.0
g = 1 / 72.0
jupiter = new PlanetaryBody @, 'Jupiter', 'gray', 4.0, 5.203, 11.86
@sun.addPlanet jupiter
jupiter.addPlanet new PlanetaryBody @, 'Io', 'gray', 3.6 * f, 421 * h, 1.769 * g
jupiter.addPlanet new PlanetaryBody @, 'Europa', 'gray', 3.1 * f, 671 * h, 3.551 * g
jupiter.addPlanet new PlanetaryBody @, 'Ganymede', 'gray', 5.3 * f, 1070 * h, 7.154 * g
jupiter.addPlanet new PlanetaryBody @, 'Callisto', 'gray', 4.8 * f, 1882 * h, 16.689 * g
@requestRedraw()
return
draw: ->
@requestRedraw()
time = Date.now()
if @renderTime?
showFps Math.round 1000 / (time - @renderTime)
@renderTime = time
context = @canvas.getContext '2d'
@drawBackground context
@drawPlanets context
return
drawBackground: (context) ->
context.fillStyle = 'white'
context.rect 0, 0, @width, @height
context.fill()
return
drawPlanets: (context) ->
@sun.draw context, @width / 2, @height /2
return
requestRedraw: ->
window.requestAnimationFrame => @draw()
return
addAsteroidBelt: (body, count) ->
# Asteroids are generally between 2.06 and 3.27 AUs.
for [0...count]
radius = 2.06 + Math.random() * (3.27 - 2.06)
body.addPlanet new PlanetaryBody @, 'asteroid', '#777', 0.1 * Math.random(), radius, radius * 2
return
normalizeOrbitRadius: (r) ->
r * (@width / 10.0)
normalizePlanetSize: (r) ->
Math.log(r + 1) * (@width / 100.0)
class PlanetaryBody
constructor: (@solarSystem, @name, @color, bodySize, orbitRadius = 0.0, @orbitPeriod = 0.0) ->
@bodySize = solarSystem.normalizePlanetSize bodySize
@orbitRadius = solarSystem.normalizeOrbitRadius orbitRadius
@orbitSpeed = @_calculateSpeed orbitPeriod
@planets = []
addPlanet: (planet) ->
@planets.push planet
draw: (context, x, y) ->
pos = @_calculatePos x, y
@drawSelf context, pos.x, pos.y
@drawChildren context, pos.x, pos.y
return
drawSelf: (context, x, y) ->
context.save()
try
context.lineWidth = 0.5
context.fillStyle = @color
context.strokeStyle = @color
if @bodySize >= 2.0
context.shadowOffsetX = 2
context.shadowOffsetY = 2
context.shadowBlur = 2
context.shadowColor = '#ddd'
context.beginPath()
context.arc x, y, @bodySize, 0, Math.PI * 2, false
context.fill()
context.closePath()
context.stroke()
context.shadowOffsetX = 0
context.shadowOffsetY = 0
context.shadowBlur = 0
context.beginPath()
context.arc x, y, @bodySize, 0, Math.PI * 2, false
context.fill()
context.closePath()
context.stroke()
finally
context.restore()
return
drawChildren: (context, x, y) ->
for planet in @planets
planet.draw context, x, y
return
_calculateSpeed: (period) ->
if period == 0.0
0.0
else
1 / (60.0 * 24.0 * 2 * period)
_calculatePos: (x, y) ->
if @orbitSpeed == 0.0
new Point x, y
else
angle = @solarSystem.renderTime * @orbitSpeed
new Point @orbitRadius * Math.cos(angle) + x, @orbitRadius * Math.sin(angle) + y
window.onload = main
Or, if you prefer, here’s the equivalent Javascript:
// Generated by CoffeeScript 1.4.0
(function() {
var PlanetaryBody, Point, SolarSystem, fpsAverage, main, showFps;
window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
main = function() {
var solarSystem;
solarSystem = new SolarSystem(document.getElementById('container'));
solarSystem.start();
};
fpsAverage = null;
showFps = function(fps) {
if (fpsAverage == null) {
fpsAverage = fps;
}
fpsAverage = fps * 0.05 + fpsAverage * 0.95;
document.getElementById('notes').textContent = Math.round(fpsAverage) + ' fps';
};
Point = (function() {
function Point(x, y) {
this.x = x;
this.y = y;
}
return Point;
})();
SolarSystem = (function() {
function SolarSystem(canvas) {
this.canvas = canvas;
this.renderTime = null;
}
SolarSystem.prototype.start = function() {
this.width = this.canvas.parentNode.clientWidth;
this.height = this.canvas.parentNode.clientHeight;
this.canvas.width = this.width;
this._start();
};
SolarSystem.prototype._start = function() {
var earth, f, g, h, jupiter;
this.sun = new PlanetaryBody(this, 'Sun', '#ff2', 14.0);
this.sun.addPlanet(new PlanetaryBody(this, 'Mercury', 'orange', 0.382, 0.387, 0.241));
this.sun.addPlanet(new PlanetaryBody(this, 'Venus', 'green', 0.949, 0.723, 0.615));
earth = new PlanetaryBody(this, 'Earth', '#33f', 1.0, 1.0, 1.0);
this.sun.addPlanet(earth);
earth.addPlanet(new PlanetaryBody(this, 'Moon', 'gray', 0.2, 0.14, 0.075));
this.sun.addPlanet(new PlanetaryBody(this, 'Mars', 'red', 0.532, 1.524, 1.88));
this.addAsteroidBelt(this.sun, 150);
f = 0.1;
h = 1 / 1500.0;
g = 1 / 72.0;
jupiter = new PlanetaryBody(this, 'Jupiter', 'gray', 4.0, 5.203, 11.86);
this.sun.addPlanet(jupiter);
jupiter.addPlanet(new PlanetaryBody(this, 'Io', 'gray', 3.6 * f, 421 * h, 1.769 * g));
jupiter.addPlanet(new PlanetaryBody(this, 'Europa', 'gray', 3.1 * f, 671 * h, 3.551 * g));
jupiter.addPlanet(new PlanetaryBody(this, 'Ganymede', 'gray', 5.3 * f, 1070 * h, 7.154 * g));
jupiter.addPlanet(new PlanetaryBody(this, 'Callisto', 'gray', 4.8 * f, 1882 * h, 16.689 * g));
this.requestRedraw();
};
SolarSystem.prototype.draw = function() {
var context, time;
this.requestRedraw();
time = Date.now();
if (this.renderTime != null) {
showFps(Math.round(1000 / (time - this.renderTime)));
}
this.renderTime = time;
context = this.canvas.getContext('2d');
this.drawBackground(context);
this.drawPlanets(context);
};
SolarSystem.prototype.drawBackground = function(context) {
context.fillStyle = 'white';
context.rect(0, 0, this.width, this.height);
context.fill();
};
SolarSystem.prototype.drawPlanets = function(context) {
this.sun.draw(context, this.width / 2, this.height / 2);
};
SolarSystem.prototype.requestRedraw = function() {
var _this = this;
window.requestAnimationFrame(function() {
return _this.draw();
});
};
SolarSystem.prototype.addAsteroidBelt = function(body, count) {
var radius, _i;
for (_i = 0; 0 <= count ? _i < count : _i > count; 0 <= count ? _i++ : _i--) {
radius = 2.06 + Math.random() * (3.27 - 2.06);
body.addPlanet(new PlanetaryBody(this, 'asteroid', '#777', 0.1 * Math.random(), radius, radius * 2));
}
};
SolarSystem.prototype.normalizeOrbitRadius = function(r) {
return r * (this.width / 10.0);
};
SolarSystem.prototype.normalizePlanetSize = function(r) {
return Math.log(r + 1) * (this.width / 100.0);
};
return SolarSystem;
})();
PlanetaryBody = (function() {
function PlanetaryBody(solarSystem, name, color, bodySize, orbitRadius, orbitPeriod) {
this.solarSystem = solarSystem;
this.name = name;
this.color = color;
if (orbitRadius == null) {
orbitRadius = 0.0;
}
this.orbitPeriod = orbitPeriod != null ? orbitPeriod : 0.0;
this.bodySize = solarSystem.normalizePlanetSize(bodySize);
this.orbitRadius = solarSystem.normalizeOrbitRadius(orbitRadius);
this.orbitSpeed = this._calculateSpeed(orbitPeriod);
this.planets = [];
}
PlanetaryBody.prototype.addPlanet = function(planet) {
return this.planets.push(planet);
};
PlanetaryBody.prototype.draw = function(context, x, y) {
var pos;
pos = this._calculatePos(x, y);
this.drawSelf(context, pos.x, pos.y);
this.drawChildren(context, pos.x, pos.y);
};
PlanetaryBody.prototype.drawSelf = function(context, x, y) {
context.save();
try {
context.lineWidth = 0.5;
context.fillStyle = this.color;
context.strokeStyle = this.color;
if (this.bodySize >= 2.0) {
context.shadowOffsetX = 2;
context.shadowOffsetY = 2;
context.shadowBlur = 2;
context.shadowColor = '#ddd';
}
context.beginPath();
context.arc(x, y, this.bodySize, 0, Math.PI * 2, false);
context.fill();
context.closePath();
context.stroke();
context.shadowOffsetX = 0;
context.shadowOffsetY = 0;
context.shadowBlur = 0;
context.beginPath();
context.arc(x, y, this.bodySize, 0, Math.PI * 2, false);
context.fill();
context.closePath();
context.stroke();
} finally {
context.restore();
}
};
PlanetaryBody.prototype.drawChildren = function(context, x, y) {
var planet, _i, _len, _ref;
_ref = this.planets;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
planet = _ref[_i];
planet.draw(context, x, y);
}
};
PlanetaryBody.prototype._calculateSpeed = function(period) {
if (period === 0.0) {
return 0.0;
} else {
return 1 / (60.0 * 24.0 * 2 * period);
}
};
PlanetaryBody.prototype._calculatePos = function(x, y) {
var angle;
if (this.orbitSpeed === 0.0) {
return new Point(x, y);
} else {
angle = this.solarSystem.renderTime * this.orbitSpeed;
return new Point(this.orbitRadius * Math.cos(angle) + x, this.orbitRadius * Math.sin(angle) + y);
}
};
return PlanetaryBody;
})();
window.onload = main;
}).call(this);
My basic debugging process here was:
console.log('drawing')in the draw function told me, yes they are.console.log(x, y)in the draw function told me both wereNaN.So with some
console.log‘s sprinkled around I found that the X and Y being sent to your draw functions wereNaN. So somewhere your doing some bad math. I traced it back to the_calculateSpeedmethod in the constructor where you passed inorbitPeriodbut the construct accepts that as@orbitPeriod, which of course is a totally different thing.console.logis your friend, add it where you think your data is messed up to debug like a pro.