Created
September 25, 2014 05:05
-
-
Save angus-c/e7037a859f212859391f to your computer and use it in GitHub Desktop.
A Bouncing Ball // source http://jsbin.com/hidori/75
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>A Bouncing Ball</title> | |
</head> | |
<body> | |
<canvas id="canvas" width="300" height="300"></canvas> | |
<input id="delay" type="range" max="2000" value="10"/> | |
<script id="jsbin-javascript"> | |
var size; | |
var ballCount = 18; | |
var ballSize = 10; | |
var delay = 10; | |
var interval; | |
// var colors = ['black', 'blue', 'fuschia', 'green', 'coral', 'gray', 'navy', 'purple', 'olive', 'red']; | |
var map; | |
var collisionCount = 0; | |
window.onload = function() { | |
document.getElementById('delay').addEventListener( | |
'change', | |
function(e) { | |
clearInterval(interval); | |
interval = setInterval(renderBalls, e.target.value); | |
} | |
); | |
} | |
var Ball = function (xSpeed, ySpeed, magic, color) { | |
this.x = size*Math.random(); | |
this.y = size*Math.random(); | |
this.xSpeed = xSpeed; | |
this.ySpeed = ySpeed; | |
this.magic = magic; | |
this.movesSinceCollision = 0; | |
this.color = color; | |
}; | |
var circle = function (x, y, radius, fillCircle, color) { | |
ctx.beginPath(); | |
ctx.arc(x, y, radius, 0, Math.PI * 2, false); | |
if (fillCircle) { | |
ctx.fillStyle=color; | |
ctx.fill(); | |
} else { | |
ctx.stroke(); | |
} | |
}; | |
Ball.prototype.draw = function () { | |
circle(this.x, this.y, ballSize, true, this.color); | |
}; | |
Ball.prototype.move = function () { | |
this.x += this.xSpeed; | |
this.y += this.ySpeed; | |
this.movesSinceCollision++; | |
}; | |
Ball.prototype.checkCollision = function () { | |
var xE = this.x + ballSize + 2; | |
var xxE = this.x + Math.pow((ballSize*ballSize/2), 0.5); | |
var xW = this.x - ballSize - 2; | |
var xxW = this.x - Math.pow((ballSize*ballSize/2), 0.5); | |
var yN = this.y - ballSize - 2; | |
var yyN = this.y - Math.pow((ballSize*ballSize/2), 0.5); | |
var yS = this.y + ballSize + 2; | |
var yyS = this.y + Math.pow((ballSize*ballSize/2), 0.5); | |
var collidingBallIndex; | |
var probe = { | |
e: getOpacityAt(xE, this.y), | |
w: getOpacityAt(xW, this.y), | |
n: getOpacityAt(this.x, yN), | |
s: getOpacityAt(this.x, yS), | |
ne: getOpacityAt(xxE + 2, yyN - 2), | |
se: getOpacityAt(xxE + 2, yyS + 2), | |
nw: getOpacityAt(xxW - 2, yyN - 2), | |
sw: getOpacityAt(xxW - 2, yyS + 2) | |
} | |
function getOpacityAt(x, y) { | |
var opacityIndex = (Math.round(x) + Math.round(y)*300)*4 -1 | |
var opacity = map[opacityIndex]; | |
if (opacity) { | |
// blue value is the id | |
collidingBallIndex = map[opacityIndex-1]; | |
if (collidingBallIndex) { | |
console.log(collisionCount++); | |
if (collidingBallIndex > ballCount) | |
console.log('*' + collidingBallIndex); | |
} | |
} | |
return opacity; | |
} | |
function getBlueAt(x, y) { | |
if (map[(Math.round(x) + Math.round(y)*300)*4 -2] > 5) { | |
console.log(map[(Math.round(x) + Math.round(y)*300)*4 -2]) | |
} | |
return map[(Math.round(x) + Math.round(y)*300)*4 -2]; | |
} | |
if (probe.e) { | |
this.xSpeed = -Math.abs(this.xSpeed); | |
this.afterCollision(probe); | |
} else if (probe.w) { | |
this.xSpeed = Math.abs(this.xSpeed); | |
this.afterCollision(probe); | |
} else if (probe.n) { | |
this.ySpeed = Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.s) { | |
this.ySpeed = -Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.ne) { | |
this.xSpeed = -Math.abs(this.xSpeed); | |
this.ySpeed = Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.se) { | |
this.xSpeed = -Math.abs(this.xSpeed); | |
this.ySpeed = -Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.nw) { | |
this.xSpeed = Math.abs(this.xSpeed); | |
this.ySpeed = Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.sw) { | |
this.xSpeed = Math.abs(this.xSpeed); | |
this.ySpeed = -Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} | |
// if (collidingBallIndex) { | |
// balls[collidingBallIndex].color = "#ff" + balls[collidingBallIndex].color.slice(-4); | |
// } | |
// ctx.fillStyle="red"; | |
// ctx.fillRect(xE, this.y, 1, 1); | |
// ctx.fillRect(xW, this.y, 1, 1); | |
// ctx.fillRect(this.x, yN, 1, 1); | |
// ctx.fillRect(this.x, yS, 1, 1); | |
// ctx.fillRect(xxE+1, yyN-1, 1, 1); | |
// ctx.fillRect(xxE+1, yyS+1, 1, 1); | |
// ctx.fillRect(xxW-1, yyN-1, 1, 1); | |
// ctx.fillRect(xxW-1, yyS+1, 1, 1); | |
}; | |
Ball.prototype.afterCollision = function(probe) { | |
if (!this.movesSinceCollision) { | |
console.log('pos:', this.x, this.y); | |
console.log( | |
Object.keys(probe).filter( | |
function(k) {return Number(probe[k])} | |
) | |
); | |
} | |
if (this.magic) { | |
// console.log(Object.keys(probe).filter( | |
// function(k) {return Number(probe[k])} | |
// )); | |
// console.log('sX', this.xSpeed, 'sY', this.ySpeed); | |
} | |
this.movesSinceCollision = 0; | |
} | |
var canvas = document.getElementById("canvas"); | |
var ctx = canvas.getContext("2d"); | |
var size = canvas.width; | |
var balls = []; | |
for (var i = 1; i <= ballCount; i++) { | |
balls[i] = new Ball( | |
1 - 2*Math.random(), | |
1 - 2*Math.random(), | |
i == 0, | |
//'#0000' + ('00'+i).slice(-2) //set blue to id | |
'#' + ('00' + Math.floor(i*255/(ballCount-1)).toString(16)).slice(-2) + '00' + ('00'+i).slice(-2) //set blue to id | |
); | |
console.log('#0000' + ('00'+i).slice(-2)); | |
} | |
interval = setInterval(renderBalls, delay); | |
function renderBalls() { | |
ctx.clearRect(0, 0, size, size); | |
ctx.strokeRect(0, 0, size, size); | |
for (var i = 1; i <= ballCount; i++) | |
balls[i].draw(); | |
var id = ctx.getImageData(0, 0, size, size); | |
map = ctx.getImageData(0, 0, size, size).data; | |
// for (var j = 0; j < map.length; j = j + 4) { | |
// if (map[j+2]>5) { | |
// //console.log(j, map[j+2]); | |
// id.data[j+2] = 255; | |
// } | |
// } | |
// ctx.putImageData(id, 0, 0); | |
for (var i = 1; i <= ballCount; i++) | |
balls[i].checkCollision(); | |
for (var i = 1; i <= ballCount; i++) | |
balls[i].move(); | |
} | |
</script> | |
<script id="jsbin-source-javascript" type="text/javascript"> var size; | |
var ballCount = 18; | |
var ballSize = 10; | |
var delay = 10; | |
var interval; | |
// var colors = ['black', 'blue', 'fuschia', 'green', 'coral', 'gray', 'navy', 'purple', 'olive', 'red']; | |
var map; | |
var collisionCount = 0; | |
window.onload = function() { | |
document.getElementById('delay').addEventListener( | |
'change', | |
function(e) { | |
clearInterval(interval); | |
interval = setInterval(renderBalls, e.target.value); | |
} | |
); | |
} | |
var Ball = function (xSpeed, ySpeed, magic, color) { | |
this.x = size*Math.random(); | |
this.y = size*Math.random(); | |
this.xSpeed = xSpeed; | |
this.ySpeed = ySpeed; | |
this.magic = magic; | |
this.movesSinceCollision = 0; | |
this.color = color; | |
}; | |
var circle = function (x, y, radius, fillCircle, color) { | |
ctx.beginPath(); | |
ctx.arc(x, y, radius, 0, Math.PI * 2, false); | |
if (fillCircle) { | |
ctx.fillStyle=color; | |
ctx.fill(); | |
} else { | |
ctx.stroke(); | |
} | |
}; | |
Ball.prototype.draw = function () { | |
circle(this.x, this.y, ballSize, true, this.color); | |
}; | |
Ball.prototype.move = function () { | |
this.x += this.xSpeed; | |
this.y += this.ySpeed; | |
this.movesSinceCollision++; | |
}; | |
Ball.prototype.checkCollision = function () { | |
var xE = this.x + ballSize + 2; | |
var xxE = this.x + Math.pow((ballSize*ballSize/2), 0.5); | |
var xW = this.x - ballSize - 2; | |
var xxW = this.x - Math.pow((ballSize*ballSize/2), 0.5); | |
var yN = this.y - ballSize - 2; | |
var yyN = this.y - Math.pow((ballSize*ballSize/2), 0.5); | |
var yS = this.y + ballSize + 2; | |
var yyS = this.y + Math.pow((ballSize*ballSize/2), 0.5); | |
var collidingBallIndex; | |
var probe = { | |
e: getOpacityAt(xE, this.y), | |
w: getOpacityAt(xW, this.y), | |
n: getOpacityAt(this.x, yN), | |
s: getOpacityAt(this.x, yS), | |
ne: getOpacityAt(xxE + 2, yyN - 2), | |
se: getOpacityAt(xxE + 2, yyS + 2), | |
nw: getOpacityAt(xxW - 2, yyN - 2), | |
sw: getOpacityAt(xxW - 2, yyS + 2) | |
} | |
function getOpacityAt(x, y) { | |
var opacityIndex = (Math.round(x) + Math.round(y)*300)*4 -1 | |
var opacity = map[opacityIndex]; | |
if (opacity) { | |
// blue value is the id | |
collidingBallIndex = map[opacityIndex-1]; | |
if (collidingBallIndex) { | |
console.log(collisionCount++); | |
if (collidingBallIndex > ballCount) | |
console.log('*' + collidingBallIndex); | |
} | |
} | |
return opacity; | |
} | |
function getBlueAt(x, y) { | |
if (map[(Math.round(x) + Math.round(y)*300)*4 -2] > 5) { | |
console.log(map[(Math.round(x) + Math.round(y)*300)*4 -2]) | |
} | |
return map[(Math.round(x) + Math.round(y)*300)*4 -2]; | |
} | |
if (probe.e) { | |
this.xSpeed = -Math.abs(this.xSpeed); | |
this.afterCollision(probe); | |
} else if (probe.w) { | |
this.xSpeed = Math.abs(this.xSpeed); | |
this.afterCollision(probe); | |
} else if (probe.n) { | |
this.ySpeed = Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.s) { | |
this.ySpeed = -Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.ne) { | |
this.xSpeed = -Math.abs(this.xSpeed); | |
this.ySpeed = Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.se) { | |
this.xSpeed = -Math.abs(this.xSpeed); | |
this.ySpeed = -Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.nw) { | |
this.xSpeed = Math.abs(this.xSpeed); | |
this.ySpeed = Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.sw) { | |
this.xSpeed = Math.abs(this.xSpeed); | |
this.ySpeed = -Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} | |
// if (collidingBallIndex) { | |
// balls[collidingBallIndex].color = "#ff" + balls[collidingBallIndex].color.slice(-4); | |
// } | |
// ctx.fillStyle="red"; | |
// ctx.fillRect(xE, this.y, 1, 1); | |
// ctx.fillRect(xW, this.y, 1, 1); | |
// ctx.fillRect(this.x, yN, 1, 1); | |
// ctx.fillRect(this.x, yS, 1, 1); | |
// ctx.fillRect(xxE+1, yyN-1, 1, 1); | |
// ctx.fillRect(xxE+1, yyS+1, 1, 1); | |
// ctx.fillRect(xxW-1, yyN-1, 1, 1); | |
// ctx.fillRect(xxW-1, yyS+1, 1, 1); | |
}; | |
Ball.prototype.afterCollision = function(probe) { | |
if (!this.movesSinceCollision) { | |
console.log('pos:', this.x, this.y); | |
console.log( | |
Object.keys(probe).filter( | |
function(k) {return Number(probe[k])} | |
) | |
); | |
} | |
if (this.magic) { | |
// console.log(Object.keys(probe).filter( | |
// function(k) {return Number(probe[k])} | |
// )); | |
// console.log('sX', this.xSpeed, 'sY', this.ySpeed); | |
} | |
this.movesSinceCollision = 0; | |
} | |
var canvas = document.getElementById("canvas"); | |
var ctx = canvas.getContext("2d"); | |
var size = canvas.width; | |
var balls = []; | |
for (var i = 1; i <= ballCount; i++) { | |
balls[i] = new Ball( | |
1 - 2*Math.random(), | |
1 - 2*Math.random(), | |
i == 0, | |
//'#0000' + ('00'+i).slice(-2) //set blue to id | |
'#' + ('00' + Math.floor(i*255/(ballCount-1)).toString(16)).slice(-2) + '00' + ('00'+i).slice(-2) //set blue to id | |
); | |
console.log('#0000' + ('00'+i).slice(-2)); | |
} | |
interval = setInterval(renderBalls, delay); | |
function renderBalls() { | |
ctx.clearRect(0, 0, size, size); | |
ctx.strokeRect(0, 0, size, size); | |
for (var i = 1; i <= ballCount; i++) | |
balls[i].draw(); | |
var id = ctx.getImageData(0, 0, size, size); | |
map = ctx.getImageData(0, 0, size, size).data; | |
// for (var j = 0; j < map.length; j = j + 4) { | |
// if (map[j+2]>5) { | |
// //console.log(j, map[j+2]); | |
// id.data[j+2] = 255; | |
// } | |
// } | |
// ctx.putImageData(id, 0, 0); | |
for (var i = 1; i <= ballCount; i++) | |
balls[i].checkCollision(); | |
for (var i = 1; i <= ballCount; i++) | |
balls[i].move(); | |
} | |
</script></body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var size; | |
var ballCount = 18; | |
var ballSize = 10; | |
var delay = 10; | |
var interval; | |
// var colors = ['black', 'blue', 'fuschia', 'green', 'coral', 'gray', 'navy', 'purple', 'olive', 'red']; | |
var map; | |
var collisionCount = 0; | |
window.onload = function() { | |
document.getElementById('delay').addEventListener( | |
'change', | |
function(e) { | |
clearInterval(interval); | |
interval = setInterval(renderBalls, e.target.value); | |
} | |
); | |
} | |
var Ball = function (xSpeed, ySpeed, magic, color) { | |
this.x = size*Math.random(); | |
this.y = size*Math.random(); | |
this.xSpeed = xSpeed; | |
this.ySpeed = ySpeed; | |
this.magic = magic; | |
this.movesSinceCollision = 0; | |
this.color = color; | |
}; | |
var circle = function (x, y, radius, fillCircle, color) { | |
ctx.beginPath(); | |
ctx.arc(x, y, radius, 0, Math.PI * 2, false); | |
if (fillCircle) { | |
ctx.fillStyle=color; | |
ctx.fill(); | |
} else { | |
ctx.stroke(); | |
} | |
}; | |
Ball.prototype.draw = function () { | |
circle(this.x, this.y, ballSize, true, this.color); | |
}; | |
Ball.prototype.move = function () { | |
this.x += this.xSpeed; | |
this.y += this.ySpeed; | |
this.movesSinceCollision++; | |
}; | |
Ball.prototype.checkCollision = function () { | |
var xE = this.x + ballSize + 2; | |
var xxE = this.x + Math.pow((ballSize*ballSize/2), 0.5); | |
var xW = this.x - ballSize - 2; | |
var xxW = this.x - Math.pow((ballSize*ballSize/2), 0.5); | |
var yN = this.y - ballSize - 2; | |
var yyN = this.y - Math.pow((ballSize*ballSize/2), 0.5); | |
var yS = this.y + ballSize + 2; | |
var yyS = this.y + Math.pow((ballSize*ballSize/2), 0.5); | |
var collidingBallIndex; | |
var probe = { | |
e: getOpacityAt(xE, this.y), | |
w: getOpacityAt(xW, this.y), | |
n: getOpacityAt(this.x, yN), | |
s: getOpacityAt(this.x, yS), | |
ne: getOpacityAt(xxE + 2, yyN - 2), | |
se: getOpacityAt(xxE + 2, yyS + 2), | |
nw: getOpacityAt(xxW - 2, yyN - 2), | |
sw: getOpacityAt(xxW - 2, yyS + 2) | |
} | |
function getOpacityAt(x, y) { | |
var opacityIndex = (Math.round(x) + Math.round(y)*300)*4 -1 | |
var opacity = map[opacityIndex]; | |
if (opacity) { | |
// blue value is the id | |
collidingBallIndex = map[opacityIndex-1]; | |
if (collidingBallIndex) { | |
console.log(collisionCount++); | |
if (collidingBallIndex > ballCount) | |
console.log('*' + collidingBallIndex); | |
} | |
} | |
return opacity; | |
} | |
function getBlueAt(x, y) { | |
if (map[(Math.round(x) + Math.round(y)*300)*4 -2] > 5) { | |
console.log(map[(Math.round(x) + Math.round(y)*300)*4 -2]) | |
} | |
return map[(Math.round(x) + Math.round(y)*300)*4 -2]; | |
} | |
if (probe.e) { | |
this.xSpeed = -Math.abs(this.xSpeed); | |
this.afterCollision(probe); | |
} else if (probe.w) { | |
this.xSpeed = Math.abs(this.xSpeed); | |
this.afterCollision(probe); | |
} else if (probe.n) { | |
this.ySpeed = Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.s) { | |
this.ySpeed = -Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.ne) { | |
this.xSpeed = -Math.abs(this.xSpeed); | |
this.ySpeed = Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.se) { | |
this.xSpeed = -Math.abs(this.xSpeed); | |
this.ySpeed = -Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.nw) { | |
this.xSpeed = Math.abs(this.xSpeed); | |
this.ySpeed = Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} else if (probe.sw) { | |
this.xSpeed = Math.abs(this.xSpeed); | |
this.ySpeed = -Math.abs(this.ySpeed); | |
this.afterCollision(probe); | |
} | |
// if (collidingBallIndex) { | |
// balls[collidingBallIndex].color = "#ff" + balls[collidingBallIndex].color.slice(-4); | |
// } | |
// ctx.fillStyle="red"; | |
// ctx.fillRect(xE, this.y, 1, 1); | |
// ctx.fillRect(xW, this.y, 1, 1); | |
// ctx.fillRect(this.x, yN, 1, 1); | |
// ctx.fillRect(this.x, yS, 1, 1); | |
// ctx.fillRect(xxE+1, yyN-1, 1, 1); | |
// ctx.fillRect(xxE+1, yyS+1, 1, 1); | |
// ctx.fillRect(xxW-1, yyN-1, 1, 1); | |
// ctx.fillRect(xxW-1, yyS+1, 1, 1); | |
}; | |
Ball.prototype.afterCollision = function(probe) { | |
if (!this.movesSinceCollision) { | |
console.log('pos:', this.x, this.y); | |
console.log( | |
Object.keys(probe).filter( | |
function(k) {return Number(probe[k])} | |
) | |
); | |
} | |
if (this.magic) { | |
// console.log(Object.keys(probe).filter( | |
// function(k) {return Number(probe[k])} | |
// )); | |
// console.log('sX', this.xSpeed, 'sY', this.ySpeed); | |
} | |
this.movesSinceCollision = 0; | |
} | |
var canvas = document.getElementById("canvas"); | |
var ctx = canvas.getContext("2d"); | |
var size = canvas.width; | |
var balls = []; | |
for (var i = 1; i <= ballCount; i++) { | |
balls[i] = new Ball( | |
1 - 2*Math.random(), | |
1 - 2*Math.random(), | |
i == 0, | |
//'#0000' + ('00'+i).slice(-2) //set blue to id | |
'#' + ('00' + Math.floor(i*255/(ballCount-1)).toString(16)).slice(-2) + '00' + ('00'+i).slice(-2) //set blue to id | |
); | |
console.log('#0000' + ('00'+i).slice(-2)); | |
} | |
interval = setInterval(renderBalls, delay); | |
function renderBalls() { | |
ctx.clearRect(0, 0, size, size); | |
ctx.strokeRect(0, 0, size, size); | |
for (var i = 1; i <= ballCount; i++) | |
balls[i].draw(); | |
var id = ctx.getImageData(0, 0, size, size); | |
map = ctx.getImageData(0, 0, size, size).data; | |
// for (var j = 0; j < map.length; j = j + 4) { | |
// if (map[j+2]>5) { | |
// //console.log(j, map[j+2]); | |
// id.data[j+2] = 255; | |
// } | |
// } | |
// ctx.putImageData(id, 0, 0); | |
for (var i = 1; i <= ballCount; i++) | |
balls[i].checkCollision(); | |
for (var i = 1; i <= ballCount; i++) | |
balls[i].move(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment