Skip to content
Browse files

Add animation helpers

  • Loading branch information...
1 parent 7a91b8a commit db387bbd375955a560b8e875fcdce9c950382ab7 @karenc committed Jul 28, 2012
Showing with 235 additions and 41 deletions.
  1. +75 −41 app/assets/javascripts/fakesnladders.js
  2. +159 −0 app/assets/javascripts/utils.js
  3. +1 −0 app/views/home/index.html.erb
View
116 app/assets/javascripts/fakesnladders.js
@@ -1,85 +1,119 @@
-function FakesNLadders(context, gridSize) {
+var tickManager;
+var allowClick = true;
+
+function FakesNLadders(layer, gridSize) {
this.gridSize = gridSize;
- this.context = context;
+ this.layer = layer;
+ this.layer.addGameObject(this);
}
-FakesNLadders.prototype.init = function() {
- this.context.fillStyle = 'black';
- this.context.strokeRect(0, 0, this.gridSize, this.gridSize);
+FakesNLadders.prototype.draw = function(context) {
+ context.fillStyle = 'black';
+ context.strokeRect(0, 0, this.gridSize, this.gridSize);
// grid squares
var squaresToSide = 10;
var nSquares = squaresToSide*squaresToSide;
var squareSize = this.gridSize/squaresToSide;
for (var x = 0.5; x < this.gridSize; x += squareSize) {
- this.context.moveTo(x, 0);
- this.context.lineTo(x, this.gridSize);
+ context.moveTo(x, 0);
+ context.lineTo(x, this.gridSize);
}
for (var y = 0.5; y < this.gridSize; y += squareSize) {
- this.context.moveTo(0, y);
- this.context.lineTo(this.gridSize, y);
+ context.moveTo(0, y);
+ context.lineTo(this.gridSize, y);
}
- this.context.strokeStyle = 'rgba(70, 70, 70, 1)';
- this.context.stroke();
+ context.strokeStyle = 'rgba(70, 70, 70, 1)';
+ context.stroke();
// grid numbering (centered, alternating direction)
- this.context.font = "bold 16px sans-serif";
- this.context.textBaseline = 'bottom';
- this.context.textAlign = 'middle'; // left is the same??
+ context.font = "bold 16px sans-serif";
+ context.textBaseline = 'bottom';
+ context.textAlign = 'middle'; // left is the same??
var sqNo = 0;
var centring = squareSize/2;
for(var y = squaresToSide-1; y >= 0; y--) {
for(var x = squaresToSide-1; x >= 0; x--) {
- this.context.fillText(sqNo++, x*squareSize + centring, y*squareSize + centring);
+ context.fillText(sqNo++, x*squareSize + centring, y*squareSize + centring);
}
y--;
for(var x = 0; x < squaresToSide; x++) {
- this.context.fillText(sqNo++, x*squareSize + centring, y*squareSize + centring);
+ context.fillText(sqNo++, x*squareSize + centring, y*squareSize + centring);
}
}
// borders and placeholders for dash elements
// marker text style
- this.context.font = "bold 24px sans-serif"; // marker text
- this.context.textBaseline = 'middle';
- this.context.textAlign = 'middle'; // left is the same??
+ context.font = "bold 24px sans-serif"; // marker text
+ context.textBaseline = 'middle';
+ context.textAlign = 'middle'; // left is the same??
// board end landing zone
- this.context.beginPath();
- this.context.moveTo(this.gridSize, squareSize*2 + 0.5);
- this.context.lineTo(WIDTH, squareSize*2 + 0.5);
- this.context.stroke();
- this.context.fillText("END", this.gridSize - (this.gridSize - WIDTH)/2, squareSize);
+ context.beginPath();
+ context.moveTo(this.gridSize, squareSize*2 + 0.5);
+ context.lineTo(WIDTH, squareSize*2 + 0.5);
+ context.stroke();
+ context.fillText("END", this.gridSize - (this.gridSize - WIDTH)/2, squareSize);
// 'A' text option
- this.context.beginPath();
- this.context.moveTo(this.gridSize, squareSize*5 + 0.5);
- this.context.lineTo(WIDTH, squareSize*5 + 0.5);
- this.context.stroke();
- this.context.fillText("Option A", this.gridSize - (this.gridSize - WIDTH)/2, squareSize*3.5);
+ context.beginPath();
+ context.moveTo(this.gridSize, squareSize*5 + 0.5);
+ context.lineTo(WIDTH, squareSize*5 + 0.5);
+ context.stroke();
+ context.fillText("Option A", this.gridSize - (this.gridSize - WIDTH)/2, squareSize*3.5);
// 'B' text option
- this.context.beginPath();
- this.context.moveTo(this.gridSize, squareSize*8 + 0.5);
- this.context.lineTo(WIDTH, squareSize*8 + 0.5);
- this.context.stroke();
- this.context.fillText("Option B", this.gridSize - (this.gridSize - WIDTH)/2, squareSize*6.5);
+ context.beginPath();
+ context.moveTo(this.gridSize, squareSize*8 + 0.5);
+ context.lineTo(WIDTH, squareSize*8 + 0.5);
+ context.stroke();
+ context.fillText("Option B", this.gridSize - (this.gridSize - WIDTH)/2, squareSize*6.5);
// status/help text area
- this.context.fillText("Status", this.gridSize - (this.gridSize - WIDTH)/2, squareSize*9);
+ context.fillText("Status", this.gridSize - (this.gridSize - WIDTH)/2, squareSize*9);
+}
+
+FakesNLadders.prototype.hit = function(p) {
+ alert(p);
}
-function init() {
+function init(e) {
+ e.stopPropagation();
var game = document.getElementById('game');
- var context = game.getContext('2d');
+ var gameContext = game.getContext('2d');
+ var backBuffer = document.getElementById('back-buffer');
+ var context = backBuffer.getContext('2d');
+
var background = document.getElementById('background');
- context.drawImage(background, 0, 0);
+ var gameLayer = new Layer();
+
+ function redraw() {
+ context.drawImage(background, 0, 0);
+
+ context.save();
+ gameLayer.draw(context);
+ context.restore();
+
+ gameContext.drawImage(backBuffer, 0, 0);
+ }
+
+ tickManager = new TickManager(redraw);
+
+ document.addEventListener('mousedown',
+ function(mouseEvent) {
+ mouseEvent.stopPropagation();
+ var point = new Point(
+ mouseEvent.pageX - game.offsetLeft,
+ mouseEvent.pageY - game.offsetTop);
+ gameLayer.hit(point);
+ redraw();
+ }, false);
- var fakesNLadders = new FakesNLadders(context, HEIGHT);
- fakesNLadders.init();
+ var fakesNLadders = new FakesNLadders(gameLayer, HEIGHT);
+ redraw();
}
-document.addEventListener('load', init, true);
+addEventListener('load', init, false);
View
159 app/assets/javascripts/utils.js
@@ -0,0 +1,159 @@
+function Point(x, y) {
+ this.x = x;
+ this.y = y;
+}
+
+Point.prototype.vectorTo = function(destination, duration) {
+ if (duration === undefined) {
+ duration = 1;
+ }
+ return new Point(
+ (destination.x - this.x) / duration,
+ (destination.y - this.y) / duration);
+};
+
+
+function Rect(x, y, width, height) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+}
+
+Rect.prototype.containsPoint = function(point) {
+ return point.x >= this.x && point.x < this.x + this.width &&
+ point.y >= this.y && point.y < this.y + this.height;
+};
+
+Rect.prototype.topLeft = function() {
+ return new Point(this.x, this.y);
+};
+
+
+function Layer() {
+ this.gameObjects = [];
+}
+
+Layer.prototype.addGameObject = function(obj) {
+ this.gameObjects.push(obj);
+};
+
+Layer.prototype.removeObject = function(obj) {
+ var idx = this.gameObjects.indexOf(obj);
+ if (idx !== -1) {
+ this.gameObjects.splice(idx, 1);
+ }
+};
+
+Layer.prototype.removeObjects = function(objs) {
+ var i;
+ for (i = 0; i < objs.length; i++) {
+ this.removeObject(objs[i]);
+ }
+};
+
+Layer.prototype.draw = function(ctx) {
+ var i;
+ for (i = 0; i < this.gameObjects.length; i++) {
+ this.gameObjects[i].draw(ctx);
+ }
+};
+
+Layer.prototype.hit = function(point) {
+ var i;
+ for (i = 0; i < this.gameObjects.length; i++) {
+ var hitObj = this.gameObjects[i].hit(point);
+ if (hitObj) {
+ return hitObj;
+ }
+ }
+ return null;
+};
+
+
+function TickManager(redraw) {
+ this.animations = [];
+ this.interval = null;
+ this.redraw = redraw;
+}
+
+TickManager.prototype.addAnimation = function(anim, completion) {
+ if (completion === undefined) {
+ completion = function() {};
+ }
+ this.animations.push({
+ anim: anim,
+ completion: completion
+ });
+
+ // Nothing to do if the interval timer has been started
+ if (this.interval !== null) {
+ return;
+ }
+
+ // Start interval timer
+ var this_ = this;
+ this.lastTick = new Date().getTime();
+ this.interval = setInterval(function() {
+ var now = new Date().getTime();
+ var dt = (now - this_.lastTick) / 1000;
+ this_.tick(dt);
+ this_.lastTick = now;
+
+ // Stop interval timer when there are no more animations
+ if (this_.animations.length === 0) {
+ clearInterval(this_.interval);
+ this_.interval = null;
+ }
+ }, TICK_INTERVAL);
+};
+
+TickManager.prototype.tick = function(dt) {
+ var deadAnimations = [];
+ var i;
+ for (i = 0; i < this.animations.length; i++) {
+ if (!this.animations[i].anim.tick(dt)) {
+ deadAnimations.push(i);
+ this.animations[i].completion();
+ }
+ }
+
+ while (deadAnimations.length > 0) {
+ i = deadAnimations.pop();
+ this.animations.splice(i, 1);
+ }
+
+ this.redraw();
+};
+
+
+function MoveAnimation(obj, destination, duration) {
+ this.obj = obj;
+ this.destination = destination;
+ this.duration = duration;
+ this.direction = this.obj.rect.topLeft().vectorTo(destination, duration);
+}
+
+MoveAnimation.prototype.tick = function(dt) {
+ this.obj.rect.x += this.direction.x * dt;
+ this.obj.rect.y += this.direction.y * dt;
+
+ this.duration -= dt;
+ if (this.duration <= 0) {
+ this.obj.rect.x = this.destination.x;
+ this.obj.rect.y = this.destination.y;
+ return false;
+ }
+ return true;
+};
+
+function multiCompletion(count, completion) {
+ if (completion === undefined) {
+ completion = function() {};
+ }
+ return function() {
+ if (--count === 0) {
+ completion();
+ }
+ };
+}
View
1 app/views/home/index.html.erb
@@ -16,6 +16,7 @@
var WIDTH = 929;
var HEIGHT = 600;
</script>
+ <%= javascript_include_tag "utils.js" %>
<%= javascript_include_tag "fakesnladders.js" %>
<%- end -%>

0 comments on commit db387bb

Please sign in to comment.
Something went wrong with that request. Please try again.