OLD | NEW |
| (Empty) |
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 } | |
OLD | NEW |