1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // demo parameters 6 // maybe overridden in test file 7 var numBalls = parseInt(getArgValue('ball_count')); 8 if (numBalls == 0 || isNaN(numBalls)) { 9 numBalls = 500; 10 } 11 var ballDiameter = 30; 12 var gravity = 0.5; //screen heights per second^2 13 var maxInitVelocity = 0.2; 14 var maxAngularVelocity = 5; // rad per second 15 var elasticity = 0.7; 16 var joltInterval = 1.5; 17 18 // globals 19 var balls = []; 20 var canvasWidth; 21 var canvasHeight; 22 var borderX; 23 var borderY; 24 var timeOfLastJolt = 0; 25 26 function include(filename) { 27 var head = document.getElementsByTagName('head')[0]; 28 var script = document.createElement('script'); 29 script.src = filename; 30 script.type = 'text/javascript'; 31 head.appendChild(script) 32 } 33 34 include("bouncing_balls_draw_ball_as_" + getArgValue('ball') + ".js"); 35 include("bouncing_balls_draw_back_as_" + getArgValue('back') + ".js"); 36 37 window.requestAnimFrame = (function(){ 38 return window.requestAnimationFrame || 39 window.webkitRequestAnimationFrame || 40 window.mozRequestAnimationFrame || 41 window.oRequestAnimationFrame || 42 window.msRequestAnimationFrame || 43 function( callback ){ 44 window.setTimeout(callback, 1000 / 60); 45 }; 46 })(); 47 48 window.onload = init; 49 50 function init(){ 51 handleResize(); 52 for (var i = 0; i < numBalls; i++) { 53 balls.push(new Ball()); 54 } 55 window.addEventListener("resize", handleResize, false); 56 drawBallInit(ballDiameter); // externally defined 57 window.requestAnimFrame(updateFrame); 58 } 59 60 function handleResize() { 61 canvasWidth = window.innerWidth; 62 canvasHeight = window.innerHeight; 63 canvas.setAttribute('width', canvasWidth); 64 canvas.setAttribute('height', canvasHeight); 65 borderX = ballDiameter/canvasWidth; 66 borderY = ballDiameter/canvasHeight; 67 prepareBackground(); // externally defined 68 } 69 70 function updateFrame() { 71 var now = new Date().getTime() / 1000; 72 var jolt = false; 73 if (now - timeOfLastJolt > joltInterval) { 74 jolt = true; 75 timeOfLastJolt = now; 76 } 77 drawBackground(); // externally defined 78 for (var i = 0; i < numBalls; i++) { 79 balls[i].step(jolt); 80 } 81 window.requestAnimFrame(updateFrame); 82 } 83 84 function Ball(){ 85 var x = borderX + Math.random()*(1-2*borderX); 86 var y = borderY + Math.random()*(1-2*borderY); 87 var angle = Math.PI * 2 * Math.random(); 88 var velocityY = Math.random()*maxInitVelocity*2 - maxInitVelocity; 89 var velocityX = Math.random()*maxInitVelocity*2 - maxInitVelocity; 90 var angularVelocity = Math.random()*maxAngularVelocity*2 - 91 maxAngularVelocity; 92 var previousFrameTime = new Date().getTime(); 93 var previousBounceTime = 0; 94 var alive = true; 95 function step(jolt) { 96 var curTime = new Date().getTime(); 97 var timeStep = (curTime - previousFrameTime) / 1000; 98 previousFrameTime = curTime; 99 100 // handle balls that are no longer bouncing 101 if (!alive) { 102 if (jolt) { 103 // If a jolt is applied, bump the rollong balls enough for them to 104 // reach between 0.75x and 1x the height of the window 105 velocityY = -Math.sqrt(2 * gravity * (1-2 * borderY) * (0.75 + 0.25 * 106 Math.random())) 107 velocityX = Math.random()*maxInitVelocity*2 - maxInitVelocity; 108 angularVelocity = Math.random()*maxAngularVelocity*2 - 109 maxAngularVelocity; 110 alive = true; 111 } else { 112 // rolling on the ground 113 angularVelocity = 2*velocityX*canvasWidth/ballDiameter; 114 } 115 } 116 117 // Compute angular motion 118 angle += timeStep*angularVelocity; 119 // Compute horizontal motion 120 var remainingTime = timeStep; 121 var deltaX = velocityX*remainingTime; 122 while ((x+deltaX) < borderX || (x+deltaX) > (1-borderX)){ 123 if ((x+deltaX) < borderX) { 124 // left side bounce 125 remainingTime -= (borderX - x)/velocityX; 126 x = borderX; 127 } else { 128 // right side bounce 129 remainingTime -= ((1-borderX) - x)/velocityX; 130 x = 1 - borderX; 131 } 132 velocityX = -elasticity*velocityX; 133 deltaX = velocityX*remainingTime; 134 } 135 x += deltaX; 136 137 // Compute vertical motion 138 remainingTime = timeStep; 139 var deltaY = alive ? velocityY*timeStep+gravity*timeStep*timeStep/2 : 0; 140 //Handle floor bounces 141 //To make sure the floor is air tight, we must be able to process multiple 142 //bounces per time step to avoid the "tunnel effect". 143 while ((y + deltaY) > (1 - borderY) && alive) { 144 // time to hit floor 145 var c = y-(1-borderY); 146 var b = velocityY; 147 var a = gravity/2; 148 // The greater root is always the right one 149 var subStep = (-b + Math.sqrt(b*b-4*a*c))/(2*a); 150 //velocity after floor hit 151 velocityY = -elasticity*(velocityY + gravity*subStep); 152 remainingTime -= subStep; 153 var bounceTime = curTime - remainingTime; 154 if (bounceTime - previousBounceTime < 0.005){ 155 // The number of iterations may not be finite within a timestep 156 // with elasticity < 1. This is due to power series convergence. 157 // To gard against hanging, we treat the ball as rolling on the ground 158 // once time between bounces is less than 5ms 159 alive = false; 160 deltaY = 0; 161 } else { 162 deltaY = velocityY*remainingTime+gravity*remainingTime*remainingTime/2; 163 } 164 previousBounceTime = bounceTime; 165 y = (1 - borderY); 166 } 167 y += deltaY; 168 velocityY += gravity*remainingTime; 169 170 drawBall(x * canvasWidth, y * canvasHeight, angle); // externally defined 171 } 172 173 return { 174 step: step 175 } 176 } 177