Skip to content

Instantly share code, notes, and snippets.

@angus-c
Created September 25, 2014 05:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save angus-c/e7037a859f212859391f to your computer and use it in GitHub Desktop.
Save angus-c/e7037a859f212859391f to your computer and use it in GitHub Desktop.
A Bouncing Ball // source http://jsbin.com/hidori/75
<!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>
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