Classic Pong [ADVANCED]
An advanced example of using the canvas to create a game. This was featured by Bing on the anniversary of the release of PONG. Click to start and click to pause, very simple and first to 5 wins.
<div class="content"> <div> <div id="container"> <canvas id="baseLayer"></canvas> <canvas id="dynamicLayer"></canvas> </div> </div> </div> <script type="text/javascript"> var Pong; (function(Pong) { Pong.Consts = { "initMessage": "Click to start", "winMessage": "You win!", "loseMessage": "You lose!", "continueMessage": "Click to play again", "pausedMessage": "Paused" }; })(Pong || (Pong = {})); var __extends = this.__extends || function(n, t) { function r() { this.constructor = n } for (var i in t) t.hasOwnProperty(i) && (n[i] = t[i]); r.prototype = t.prototype; n.prototype = new r }, Pong; (function(n) { var e, t, i, r, u, f; (function(n) { n[n.Init = 0] = "Init"; n[n.Game = 1] = "Game"; n[n.Paused = 2] = "Paused"; n[n.Win = 3] = "Win"; n[n.Lose = 4] = "Lose" })(n.State || (n.State = {})); e = n.State; t = function() { function n(n, t) { this.x = 0; this.y = 0; this.x = n; this.y = t } return n.prototype.clone = function() { return new n(this.x, this.y) }, n.prototype.getRaw = function() { return { x: this.x, y: this.y } }, n }(); n.Vector = t; i = function() { function n() { this.position = new t(0, 0); this.size = new t(0, 0) } return n }(); n.Box = i; r = function(n) { function t() { n.apply(this, arguments); this.score = 0; this.hasTurn = !1 } return __extends(t, n), t }(i); n.Player = r; u = function(n) { function t() { n.apply(this, arguments); this.direction = null } return __extends(t, n), t }(i); n.Ball = u; f = function() { function n() { this.maxScore = 5; this.redrawBaseLayer = !1; this.width = 0; this.height = 0; this.state = 0; this.tick = 0; this.delayTick = 0 } return n.prototype.reset = function(n, t) { this.width = n; this.height = t; this.redrawBaseLayer = !0; this.assignConstants(); this.ball = new u; this.ball.size.x = this.ballSize; this.ball.size.y = this.ballSize; this.ball.direction = null; this.player = new r; this.player.size.x = this.padWidth; this.player.size.y = this.padHeight; this.cpu = new r; this.cpu.size.x = this.padWidth; this.cpu.size.y = this.padHeight; this.resetPositions() }, n.prototype.checkRedrawBaseLayer = function() { var n = this.redrawBaseLayer; return this.redrawBaseLayer = !1, n }, n.prototype.scheduleRedraw = function() { this.redrawBaseLayer = !0 }, n.prototype.getWidth = function() { return this.width }, n.prototype.getHeight = function() { return this.height }, n.prototype.getPlayerScore = function() { return this.player.score }, n.prototype.getCpuScore = function() { return this.cpu.score }, n.prototype.getBall = function() { return this.ball }, n.prototype.getPlayer = function() { return this.player }, n.prototype.getCpu = function() { return this.cpu }, n.prototype.getState = function() { return this.state }, n.prototype.canStartGame = function() { return this.state !== 1 }, n.prototype.isPaused = function() { return this.state === 2 }, n.prototype.pause = function() { this.state === 1 && this.setState(2) }, n.prototype.resume = function() { this.state === 2 && this.setState(1) }, n.prototype.getBallSpeed = function() { return this.ballSpeed }, n.prototype.getCpuSpeed = function() { return this.aiSpeed }, n.prototype.getTick = function() { return this.tick }, n.prototype.setTick = function(n) { this.tick = n }, n.prototype.getDelayTick = function() { return this.delayTick }, n.prototype.startGame = function() { this.canStartGame() && (this.resetPositions(), this.setState(1)) }, n.prototype.cpuMiss = function() { ++this.player.score; this.processMissedBall(!0) }, n.prototype.playerMiss = function() { ++this.cpu.score; this.processMissedBall(!1) }, n.prototype.resetPositions = function() { this.player.position.x = this.width - this.padOffset; this.player.position.y = this.height / 2; this.cpu.position.x = this.padOffset; this.cpu.position.y = this.height / 2; this.player.hasTurn = !0; this.player.score = 0; this.cpu.hasTurn = !1; this.cpu.score = 0; this.placeBallToCenter(); this.tick = 0 }, n.prototype.assignConstants = function() { var n = this.height / 128, t = this.width / 256; this.ballSize = ~~(6 * n); this.padWidth = ~~(4 * n); this.padHeight = ~~(24 * n); this.padOffset = ~~(15 * n); this.ballSpeed = ~~(165 * Math.max(n, t)); this.aiSpeed = ~~(62.5 * Math.max(n, t)) }, n.prototype.setState = function(n) { this.state !== n && (this.redrawBaseLayer = !0, this.state = n) }, n.prototype.placeBallToCenter = function() { this.ball.position.x = this.width / 2; this.ball.position.y = this.height / 2 }, n.prototype.delay = function() { this.delayTick = this.tick + 1e3 }, n.prototype.processMissedBall = function(n) { this.player.hasTurn = !n; this.cpu.hasTurn = n; this.ball.direction = null; this.scheduleRedraw(); this.checkGameOver() || this.delay() }, n.prototype.checkGameOver = function() { return this.state !== 1 ? !1 : (this.player.score >= this.maxScore ? this.setState(3) : this.cpu.score >= this.maxScore && this.setState(4), this.state !== 1) }, n }(); n.GameState = f })(Pong || (Pong = {})), function(n) { var t = function() { function t() {} return t.prototype.simulate = function(n, t) { var i, r, u, f; t.getState() === 1 && ((i = this.updateTick(n, t), t.getTick() < t.getDelayTick()) || (r = t.getBall(), u = r.direction == null, u && (f = t.getPlayer().hasTurn, r.direction = this.throwBall(f), t.placeBallToCenter()), this.advanceBall(t, i), this.advanceCpu(t, i))) }, t.prototype.advanceBall = function(n, t) { var i = n.getBall(), s = n.getBallSpeed(), u = i.position.x, r = i.position.y, a = i.direction.x, v = i.direction.y, e = i.size.x / 2, f = i.size.y / 2, h = n.getWidth(), o = n.getHeight(), c, l; u += a * s * t; r += v * s * t; r < f && (i.direction.y = -i.direction.y, c = f - r, r = f + c); r > o - f && (i.direction.y = -i.direction.y, l = r - (o - f), r = o - f - l); this.isLeftBorderCrossed(i.position.x, u, e, n.getCpu()) && this.isBallDeflected(r, f, n.getCpu()) && (i.direction = this.getDeflectionAngle(r, n.getCpu(), !1)); this.isRightBorderCrossed(i.position.x, u, e, n.getPlayer()) && this.isBallDeflected(r, f, n.getPlayer()) && (i.direction = this.getDeflectionAngle(r, n.getPlayer(), !0)); u < e && (u = e, n.cpuMiss()); u > h - e && (u = h - e, n.playerMiss()); i.position.x = u; i.position.y = r }, t.prototype.isLeftBorderCrossed = function(n, t, i, r) { var u = r.position.x + r.size.x; return n >= u && t <= u }, t.prototype.isRightBorderCrossed = function(n, t, i, r) { var u = r.position.x; return n + i <= u && t + i >= u }, t.prototype.isBallDeflected = function(n, t, i) { var r = i.size.y / 2, u = i.position.y - r, f = i.position.y + r, e = n - t, o = n + t; return o >= u && e <= f }, t.prototype.getDeflectionAngle = function(n, t, i) { var e = t.size.y / 2, u = (n - t.position.y) / e, f, r; return u = Math.max(-1, Math.min(u, 1)), f = Math.asin(u / Math.sqrt(2)), r = this.getVectorFromAngle(f), i && (r.x = -r.x), r }, t.prototype.advanceCpu = function(n, t) { var h = n.getBall(), r = n.getCpu(), e = n.getCpuSpeed(), c = h.position.y, i = r.position.y, u = r.size.y / 2, o = n.getHeight(), f = i - c, s; Math.abs(f) < r.size.y / 4 && (f = 0); s = f < 0 ? e : f > 0 ? -e : 0; i += s * t; i < u && (i = u); i > o - u && (i = o - u); r.position.y = i }, t.prototype.updateTick = function(n, t) { var i = t.getTick(), r, u; return i || (i = n), r = n - i, u = r / 1e3, t.setTick(n), u }, t.prototype.getVectorFromAngle = function(t) { var i = Math.cos(t), r = Math.sin(t); return new n.Vector(i, r) }, t.prototype.getRandomVectorFromAngle = function(n, t) { var i = t - n, r = Math.random() * i + n; return this.getVectorFromAngle(r) }, t.prototype.throwBall = function(n) { var i = -Math.PI / 4, r = Math.PI / 4, t = this.getRandomVectorFromAngle(i, r); return n && (t.x = -t.x), t }, t }(); n.Physics = t }(Pong || (Pong = {})), function(n) { var t = function() { function n(n) { this.canvas = n; this.ctx = this.canvas.getContext("2d") } return n.prototype.getContext = function() { return this.ctx }, n.prototype.getElement = function() { return this.canvas }, n }(), i = function() { function i(n, i) { this.backgroundColor = "#000"; this.foregroundColor = "#fff"; this.fontHeightMeasured = 0; this.lastBallPosition = null; this.lastPlayerPosition = null; this.lastCpuPosition = null; this.baseLayer = new t(n); this.dynamicLayer = new t(i) } return i.prototype.reset = function() { this.lastBallPosition = null; this.lastPlayerPosition = null; this.lastCpuPosition = null }, i.prototype.render = function(n) { n.checkRedrawBaseLayer() && this.renderBaseLayer(n); this.renderDynamicLayer(n) }, i.prototype.renderBaseLayer = function(t) { var i = this.baseLayer.getContext(), r, u, f; i.fillStyle = this.backgroundColor; i.fillRect(0, 0, t.getWidth(), t.getHeight()); this.dashedLine(i, ~~(t.getWidth() / 2), 0, ~~(t.getWidth() / 2), t.getHeight(), [6, 3]); i.strokeStyle = this.foregroundColor; i.lineWidth = 2; i.stroke(); i.fillStyle = this.foregroundColor; this.fontHeightMeasured !== t.getHeight() && (this.fontHeightMeasured = t.getHeight(), this.fontHeight = ~~(this.fontHeightMeasured * 14 / 128), this.font = this.fontHeight + "px Arial", i.font = this.font, i.textBaseline = "middle", i.textAlign = "center", this.fontWidth = i.measureText("0").width); i.fillText(t.getPlayerScore().toString(), ~~(t.getWidth() / 2 + this.fontWidth * 2), this.fontHeight); i.fillText(t.getCpuScore().toString(), ~~(t.getWidth() / 2 - this.fontWidth * 2), this.fontHeight); r = t.getState(); r !== 1 && (i.fillStyle = this.backgroundColor, i.fillRect(t.getWidth() / 4, t.getHeight() / 4, t.getWidth() / 2, t.getHeight() / 2), i.fillStyle = this.foregroundColor, r === 0 ? i.fillText(n.Consts.initMessage, t.getWidth() / 2, t.getHeight() / 2) : r === 2 ? i.fillText(n.Consts.pausedMessage, t.getWidth() / 2, t.getHeight() / 2) : (r === 3 || r === 4) && (u = r === 3 ? n.Consts.winMessage : n.Consts.loseMessage, f = n.Consts.continueMessage, i.fillText(u, t.getWidth() / 2, t.getHeight() / 2 - this.fontHeight), i.fillText(f, t.getWidth() / 2, t.getHeight() / 2 + this.fontHeight))) }, i.prototype.renderDynamicLayer = function(n) { var t = this.dynamicLayer.getContext(), i; this.lastBallPosition == null || this.lastCpuPosition == null || this.lastPlayerPosition == null ? t.clearRect(0, 0, n.getWidth(), n.getHeight()) : (i = 10, this.clearBox(t, n.getBall(), this.lastBallPosition, i), this.clearBox(t, n.getPlayer(), this.lastPlayerPosition, i), this.clearBox(t, n.getCpu(), this.lastCpuPosition, i)); t.fillStyle = this.foregroundColor; (n.getState() === 1 || n.getState() === 2) && this.renderBox(t, n.getBall()); this.renderBox(t, n.getPlayer()); this.renderBox(t, n.getCpu()); this.lastBallPosition = n.getBall().position.clone(); this.lastPlayerPosition = n.getPlayer().position.clone(); this.lastCpuPosition = n.getCpu().position.clone() }, i.prototype.clearBox = function(n, t, i, r) { var u = ~~(t.size.x / 2), f = ~~(t.size.y / 2); n.clearRect(i.x - u - r, i.y - f - r, t.size.x + r * 2, t.size.y + r * 2) }, i.prototype.renderBox = function(n, t) { var i = ~~(t.size.x / 2), r = ~~(t.size.y / 2); n.fillRect(t.position.x - i, t.position.y - r, t.size.x, t.size.y) }, i.prototype.dashedLine = function(n, t, i, r, u, f) { f || (f = [10, 5]); n.save(); var e = r - t, o = u - i, s = Math.sqrt(e * e + o * o), c = Math.atan2(o, e); n.translate(t, i); n.moveTo(0, 0); n.rotate(c); var l = f.length, a = 0, h = !0; for (t = 0; s > t;) t += f[a++ % l], t > s && (t = s), h ? n.lineTo(t, 0) : n.moveTo(t, 0), h = !h; n.restore() }, i }(); n.Renderer = i }(Pong || (Pong = {})), function(n) { var i = function() { function n() {} return n }(), t = function() { function t() { this.lastPlayingStatus = !1; this.fieldSize = null; this.dragSuppressor = null; this.state = new n.GameState; this.physics = new n.Physics; this.attachToDom(); this.bindEvents(); this.defineAnimation() } return t.prototype.run = function() { this.initialize(); this.loop() }, t.prototype.getState = function() { return this.state }, t.prototype.initialize = function() { this.state.reset(this.fieldSize.width, this.fieldSize.height); this.renderer.reset() }, t.prototype.loop = function() { var t = this, n; this.requestAnimFrame.call(window, function() { return t.loop() }); n = (new Date).getTime(); this.physics.simulate(n, this.state); this.renderer.render(this.state); this.update() }, t.prototype.update = function() { var n = this.state.getState() === 1; n !== this.lastPlayingStatus && (this.lastPlayingStatus = n, this.lastPlayingStatus ? ThirdParty.addClass(this.container, "playing") : ThirdParty.removeClass(this.container, "playing"), this.state.getState() === 3 ? ThirdParty.logShow("win", 1) : this.state.getState() === 4 && ThirdParty.logShow("lose", 1)) }, t.prototype.attachToDom = function() { var u = ThirdParty._ge; this.container = u("container"); var i = u("baseLayer"), r = u("dynamicLayer"), f = this.container.offsetWidth, e = this.container.offsetHeight, t = this.calculateDimentions(f, e); this.fieldSize = t; i.style.width = t.actualWidth + "px"; i.style.height = t.actualHeight + "px"; r.style.width = t.actualWidth + "px"; r.style.height = t.actualHeight + "px"; i.width = r.width = t.width; i.height = r.height = t.height; this.renderer = new n.Renderer(i, r) }, t.prototype.calculateDimentions = function(n, t) { var i = { width: n, height: t, actualWidth: n, actualHeight: t }; return n > t ? i.height = n / 2 : i.width = t * 2, i }, t.prototype.onClick = function() { if (this.state.canStartGame()) if (this.state.isPaused()) { var n = (new Date).getTime(); this.state.setTick(n); this.state.resume() } else this.state.startGame(), this.renderer.reset(), ThirdParty.logClick("start"); else this.state.pause() }, t.prototype.onDoubleClick = function() { ThirdParty.toggleFullscreen() }, t.prototype.onMouseMove = function(n) { var r, e, t, u, i, f; this.lastPlayingStatus && (r = 0, n.touches && n.touches.length ? (e = n.touches[0], r = e.pageY) : r = n.pageY || 0, t = r - this.container.offsetTop, u = this.state.getPlayer(), t = t * this.fieldSize.height / this.fieldSize.actualHeight, i = u.size.y / 2, f = this.fieldSize.height, t < i && (t = i), t > f - i && (t = f - i), u.position.y = t) }, t.prototype.onMouseDown = function() { this.dragSuppressor && (this.dragSuppressor(), this.dragSuppressor = null); this.dragSuppressor = ThirdParty.dragSuppress() }, t.prototype.onMouseUp = function() { this.dragSuppressor && (this.dragSuppressor(), this.dragSuppressor = null) }, t.prototype.bindEvents = function() { var n = this, t = ThirdParty.bindEvent; t(this.container, "click", function() { return n.onClick() }); t(this.container, "dblclick", function() { return n.onDoubleClick() }); t(_d, "mousemove", function(t) { return n.onMouseMove(t) }); t(_d, "touchmove", function(t) { return n.onMouseMove(t) }); t(this.container, "mousedown", function(t) { return n.onMouseDown(t) }); t(_d, "mouseup", function(t) { return n.onMouseUp(t) }); t(this.container, "touchstart", function(t) { return n.onMouseDown(t) }); t(_d, "touchend", function(t) { return n.onMouseUp(t) }) }, t.prototype.defineAnimation = function() { this.requestAnimFrame = function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(n) { window.setTimeout(n, 1e3 / 60) } }() }, t }(); n.PongGame = t }(Pong || (Pong = {})), function(n) { var t = new n.PongGame; t.run() }(Pong || (Pong = {})); var ThirdParty; (function(n) { function i() { var i = t.performance, r = i && i.timing, e = i && i.navigation, o, u, f; if (i && r && e && i.now) { o = r.navigationStart + Math.ceil(i.now()); u = {}; for (f in r) typeof r[f] == "number" && (u[f] = r[f]); u.loadEventEnd = o; n.sendMessage("Perf", { timing: u, navigation: e, now: i.now() }) } } function r() { document.readyState === "complete" ? setTimeout(i, 1) : n.bindEvent(t, "load", i) } var t = window; r() })(ThirdParty || (ThirdParty = {})); </script>DEMO | DOWNLOAD