var body = document.body; // container var canvas = document.createElement("canvas"); canvas.width = body.clientWidth; canvas.height = body.clientHeight; body.appendChild(canvas); var ctx = canvas.getContext("2d"); ctx.fillStyle = "#0099CC"; ctx.fillRect(0, 0, canvas.width, canvas.height); //window.onresize = function(e) { // canvas.width = body.clientWidth; // canvas.height = body.clientHeight; //} // //window.onresize(); // initial var spring = 0.99; var a_x = 0; var a_y = 1000; var Ball = function(x, y) { this.position_x = x || Math.random() * canvas.width; this.position_y = y || Math.random() * canvas.height; var dir = Math.random() * Math.PI * 2; var vel = Math.random() * 200; this.velocity_x = vel * Math.cos(dir); this.velocity_y = vel * Math.sin(dir); this.size = 10; this.collided = false; this.update = function(dt) { this.velocity_y += a_y * dt; this.position_x += this.velocity_x * dt; this.position_y += this.velocity_y * dt + 0.5 * a_y * dt * dt; if (this.position_x < this.size / 2) { this.position_x = this.size / 2; this.velocity_x *= -1; } else if (this.position_x > canvas.width - this.size / 2) { this.position_x = canvas.width - this.size / 2; this.velocity_x *= -1; } if (this.position_y < this.size / 2) { this.position_y = this.size / 2; this.velocity_y *= -1; } else if (this.position_y > canvas.height - this.size / 2) { this.position_y = canvas.height - this.size / 2; this.velocity_y *= -1; } }; this.render = function() { ctx.beginPath(); if (this.position_y < -this.size / 2) { ctx.moveTo(this.position_x, 0); ctx.lineTo(this.position_x + this.size / 2, Math.sqrt(3) * this.size / 2); ctx.lineTo(this.position_x - this.size / 2, Math.sqrt(3) * this.size / 2); ctx.lineTo(this.position_x, 0); } else ctx.arc(this.position_x, this.position_y, this.size / 2, 0, Math.PI * 2, false); ctx.closePath(); ctx.fillStyle = this.collided ? "orange" : "white"; ctx.fill(); }; this.collide = function(target) { var toTarget_x = target.position_x - this.position_x; var toTarget_y = target.position_y - this.position_y; var len = Math.sqrt(toTarget_x * toTarget_x + toTarget_y * toTarget_y); var rr = (this.size + target.size) / 2; // nomalize toTarget_x /= len; toTarget_y /= len; if (len < rr) { this.position_x -= (rr - len) * toTarget_x; this.position_y -= (rr - len) * toTarget_y; target.position_x += (rr - len) * toTarget_x; target.position_y += (rr - len) * toTarget_y; } var projToTarget = this.velocity_x * toTarget_x + this.velocity_y * toTarget_y; var projFromTarget = target.velocity_x * toTarget_x + target.velocity_y * toTarget_y; this.velocity_x -= (projToTarget - projFromTarget) * toTarget_x * spring; this.velocity_y -= (projToTarget - projFromTarget) * toTarget_y * spring; target.velocity_x -= (projFromTarget - projToTarget) * toTarget_x * spring; target.velocity_y -= (projFromTarget - projToTarget) * toTarget_y * spring; } }; var balls = []; for (var i = 0; i < 50; i++) { balls.push(new Ball()); } var isMouseDown = false; canvas.onmousedown = function() { isMouseDown = true; } canvas.onmousemove = function(event) { if (isMouseDown) { var x = event.pageX - canvas.offsetLeft; var y = event.pageY - canvas.offsetTop; balls.push(new Ball(x, y)); } }; canvas.onmouseup = function() { isMouseDown = false; } var checkCollision = function(ball1, ball2) { var dx = ball2.position_x - ball1.position_x; var dy = ball2.position_y - ball1.position_y; var distSq = dx * dx + dy * dy; var d = (ball1.size + ball2.size) / 2; if (distSq < d * d) { ball1.collide(ball2); ball1.collided = ball2.collided = true; } }; var checkCollisionInCell = function(cell) { for (var i = 0, len = cell.length; i < len; i++) { for (var j = i + 1; j < len; j++) { checkCollision(cell[i], cell[j]); } } } var checkCollisionInTwoCells = function(cell1, cell2) { for (var i = 0, leni = cell1.length, lenj = cell2.length; i < leni; i++) { for (var j = 0; j < lenj; j++) { checkCollision(cell1[i], cell2[j]); } } } var maxsize = 50; var columns = Math.floor(canvas.width / maxsize); var rows = Math.floor(canvas.height / maxsize); var cell_width = canvas.width / columns; var cell_height = canvas.height / rows; var cells; var handle, last_time = +new Date(); var loop = function() { var now = +new Date(); var dt = Math.min((now - last_time) / 1000, 0.1); ctx.fillStyle = "#0099CC"; ctx.fillRect(0, 0, canvas.width, canvas.height); // build cells cells = [] for (var r = 0; r < rows; r++) { cells[r] = []; for (var c = 0; c < columns; c++) { cells[r][c] = []; } } // put balls in cells for (var i = 0, len = balls.length; i < len; i++) { if (balls[i].position_y > 0) { var c = Math.floor(balls[i].position_x / cell_width); var r = Math.floor(balls[i].position_y / cell_height); cells[r][c].push(balls[i]); } balls[i].collided = false; } // check collision for (var r = 0; r < rows; r++) { for (var c = 0; c < columns; c++) { checkCollisionInCell(cells[r][c]); if (c < columns - 1) checkCollisionInTwoCells(cells[r][c], cells[r][c + 1]); if (c > 0 && r < rows - 1) checkCollisionInTwoCells(cells[r][c], cells[r + 1][c - 1]); if (r < rows - 1) checkCollisionInTwoCells(cells[r][c], cells[r + 1][c]); if (c < columns - 1 && r < rows - 1) checkCollisionInTwoCells(cells[r][c], cells[r + 1][c + 1]); } } // update balls for (var i = 0, len = balls.length; i < len; i++) { balls[i].update(dt); balls[i].render(); } last_time = now; handle = requestAnimationFrame(loop); }; loop(); // run