Home | History | Annotate | Download | only in canvas2d_balls_common
      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