Bouncy2.js
April 28, 2016
This is the long awaited sequel to the classic game Bouncy.js.
Improvements include now rendering all animation on the canvas, as well as refined physics and collision detection. Mobile friendly with accelerometers as the primary user input.
The demo may take a few seconds to load. Control with arrow keys on a keyboard or accelerometers on a mobile device.
Live Demo (opens in new tab)
// Bouncy 2 : the House of Bounce, by Vlad Turda
var Bouncy = function( ID_bouncy ) {
// Bouncy : get PlayArea element : private
var PlayArea = document.getElementById( ID_bouncy );
// Bouncy : game objects : private
var PlayerOne = new this.Ball();
var VP = new this.ViewPort();
var Level = new this.Map();
// Bouncy : game properties : private
var animation_frame;
var wake_lock;
var config = {
map_name: 'map',
fps: 60,
gravity: { x: 0, y: 1 },
sweep_degrees: 5,
haptics: false
};
//+ Bouncy : initialization : method, public
this.init = function( init_config, init_ball ) {
// Initialize config object
config.map_name = init_config.map_name || config.map_name;
config.fps = init_config.fps || config.fps;
config.gravity = init_config.gravity || config.gravity;
config.sweep_degrees = init_config.sweep_degrees || config.sweep_degrees;
config.haptics = init_config.haptics || config.haptics;
// Set ViewPort dimensions
VP.setDimensions( PlayArea.clientWidth, PlayArea.clientHeight );
// Add ViewPort canvas to PlayArea DIV
PlayArea.appendChild( VP.getCanvas() );
// Initialize the PlayerOne
PlayerOne.pos = { x: 20, y: 20 };
PlayerOne.vel = { x: 0, y: 0, rot: 0 };
PlayerOne.acc = { x: 0, y: 0, jump: init_ball.jump_acc };
PlayerOne.diameter = init_ball.diameter;
PlayerOne.bounce = init_ball.bounce;
PlayerOne.friction = init_ball.friction;
PlayerOne.setImage( 'img/ball.png' );
// Set some event handlers
document.addEventListener( 'keydown', keyHandle );
document.addEventListener( 'keyup', keyHandle );
document.addEventListener( "fullscreenchange", fullScreenChange );
document.addEventListener( "webkitfullscreenchange", fullScreenChange );
document.addEventListener( "mozfullscreenchange", fullScreenChange );
// Start animation on mouse click
PlayArea.addEventListener( 'mousedown', requestStart );
// Mobile event handlers
if( /Mobi/.test( navigator.userAgent ) ) {
config.haptics = true;
window.addEventListener( 'devicemotion', deviceMotionHandle );
PlayArea.addEventListener( 'touchstart', requestFullScreen );
PlayArea.addEventListener( 'touchstart', requestWakeLock );
PlayArea.addEventListener( 'touchstart', requestStart );
}
};
var mapLoaded = function( MAP_DEF ) {
PlayerOne.pos.x = MAP_DEF.spawn.x;
PlayerOne.pos.y = MAP_DEF.spawn.y;
startAnimation();
};
//+ Bouncy : start animation method : private
var startAnimation = function() {
// Get animation frame if it doesn't already exist
if( !animation_frame ) {
animation_frame = requestAnimationFrame( gameLoop );
}
};
//+ Bouncy : game loop method : private
var gameLoop = function() {
// Resolve physics and check collision
PlayerOne.resolvePhysics( { gravity: config.gravity } );
PlayerOne.checkCollisionLayer( Level.layers[ 'collision' ], { sweep_degrees: config.sweep_degrees } );
// Request animation layer for each animated Level layer
//VP.clearBuffer();
Object.keys(Level.layers).forEach( function( layer ) {
if( Level.layers[ layer ].parameters.animate ) {
requestAnimationFrame( function() { Level.layers[ layer ].animate(); } );
}
} );
// Call haptics function if enabled and there was a collision
if( config.haptics && PlayerOne.cc.incident.total > 0 ) {
haptics( PlayerOne.cc.incident.coincidence.magnitude ); }
// Draw ViewPort
drawViewPort();
// Call for next animation frame
requestAnimationFrame( gameLoop );
};
//+ Bouncy : calculate ViewPort offset method : private
var calculateViewportOffset = function() {
// Check left edge
if( ( PlayerOne.pos.x - VP.offset.x < 100 ) && ( PlayerOne.pos.x > 100 ) ) {
VP.offset.x = parseInt( PlayerOne.pos.x - 100 ); }
// Check right edge
if( ( PlayerOne.pos.x + PlayerOne.diameter - VP.offset.x > PlayArea.clientWidth - 100 )
&& ( PlayerOne.pos.x + PlayerOne.diameter < Level.layers[ 'collision' ].canvas.width - 100 ) ) {
VP.offset.x = parseInt( PlayerOne.pos.x + PlayerOne.diameter - ( PlayArea.clientWidth - 100 ) ); }
// Check top edge
if( ( PlayerOne.pos.y - VP.offset.y < 100 ) && ( PlayerOne.pos.y > 100 ) ) {
VP.offset.y = parseInt( PlayerOne.pos.y - 100 ); }
// Check bottom edge
if( ( PlayerOne.pos.y + PlayerOne.diameter - VP.offset.y > PlayArea.clientHeight - 100 )
&& ( PlayerOne.pos.y + PlayerOne.diameter < Level.layers[ 'collision' ].canvas.height - 100 ) ) {
VP.offset.y = parseInt( ( PlayerOne.pos.y + PlayerOne.diameter ) - ( PlayArea.clientHeight - 100 ) ); }
};
//+ Bouncy : draw to buffer and update ViewPort method : private
var drawViewPort = function() {
calculateViewportOffset();
//VP.clearBuffer();
Object.keys(Level.layers).forEach( function( layer ) {
if(Level.layers[ layer ].parameters.drawable) {
VP.bufferImage(
(( Level.layers[ layer ].parameters.animate ) ? Level.layers[ layer ].animate_buffer : Level.layers[ layer ].canvas),
VP.offset.x * Level.layers[ layer ].parameters.parallax_ratio,
VP.offset.y * Level.layers[ layer ].parameters.parallax_ratio,
VP.getDimensions().x,
VP.getDimensions().y,
0,
0,
VP.getDimensions().x,
VP.getDimensions().y
);
if( layer === 'midground' ) {
VP.bufferImage( PlayerOne.getCanvas(), PlayerOne.pos.x, PlayerOne.pos.y );
}
}
});
VP.update();
};
//+ Bouncy : haptics method : private
var haptics = function( vib_strength ) {
var vibrate = navigator.vibrate || navigator.mozVibrate;
if( vib_strength > 5 && typeof vibrate === 'function' ) {
vibrate( parseInt( vib_strength * 1.5 ) );
}
};
//+ Bouncy : request full screen method : private
var requestFullScreen = function() {
// Remove this touch listener
PlayArea.removeEventListener( 'touchstart', requestFullScreen );
// Request full screen from ViewPort
VP.requestFullScreen();
// Set landscape orientation lock if possible
if( screen.orientation ) {
screen.orientation.lock( 'landscape-primary' );
window.screen.mozlockOrientation( 'landscape-primary' );
}
};
//+ Bouncy : request NoSleep "wake lock" method : private
var requestWakeLock = function() {
// Remove this listener
PlayArea.removeEventListener( 'touchstart', requestWakeLock );
// Request wake lock from NoSleep.js
wake_lock = new NoSleep();
wake_lock.enable();
};
//+ Bouncy : start game method : private
var requestStart = function() {
// Remove this listener
PlayArea.removeEventListener( 'mousedown', requestStart );
PlayArea.removeEventListener( 'touchstart', requestStart );
// Load map with startAnimation as a callback
Level.loadMap( config.map_name, mapLoaded );
};
//+ Bouncy : "fullScreenChange" event handler method : private
var fullScreenChange = function() {
// Reset the ViewPort dimensions
VP.setDimensions( PlayArea.clientWidth, PlayArea.clientHeight );
};
//+ Bouncy : mobile motion event handler method : private
var deviceMotionHandle = function( e ) {
// Calculate gravity vector
var device_gravity = {
x: ( e.acceleration.x - e.accelerationIncludingGravity.x ),
y: ( e.acceleration.y - e.accelerationIncludingGravity.y ) };
// Calculate gravity magnitude
var device_gravity_magnitude = calcMagnitude( device_gravity.x, device_gravity.y );
// Calculate gravity unit vector
var device_gravity_unit_vector = {
x: device_gravity.x / device_gravity_magnitude,
y: device_gravity.y / device_gravity_magnitude };
// Set global gravity vector
config.gravity.y = -device_gravity_unit_vector.x * 1;
config.gravity.x = -device_gravity_unit_vector.y * 1;
// Set PlayerOne's acceleration
PlayerOne.acc.x = ( Math.abs( PlayerOne.acc.x ) < Math.abs( e.acceleration.y ) ) ? e.acceleration.y * 0.5 : 0;
PlayerOne.acc.y = ( Math.abs( PlayerOne.acc.y ) < Math.abs( e.acceleration.x ) ) ? e.acceleration.x * 0.5 : 0;
};
//+ Bouncy : key handler for desktop method : private
var keyHandle = function( e ) {
switch( e.type ) {
case 'keydown':
if( e.keyCode === 37 ) { PlayerOne.acc.x = -1; }
if( e.keyCode === 39 ) { PlayerOne.acc.x = 1; }
if( e.keyCode === 38 ) { PlayerOne.vel.y -= PlayerOne.acc.jump; }
break;
case 'keyup':
if( e.keyCode === 37 ) { PlayerOne.acc.x = 0; }
if( e.keyCode === 39 ) { PlayerOne.acc.x = 0; }
break;
}
if( e.keyCode === 33 ) { PlayerOne.diameter += 5; }
if( e.keyCode === 34 ) { PlayerOne.diameter -= 5; }
};
//+ Bouncy : console data log method : private
var dataOut = function() {
console.clear();
console.log( 'PlayerOne Pos X: ' + PlayerOne.pos.x );
console.log( 'PlayerOne Pos Y: ' + PlayerOne.pos.y );
console.log( 'PlayerOne Vel X: ' + PlayerOne.vel.x );
console.log( 'PlayerOne Vel Y: ' + PlayerOne.vel.y );
console.log( 'Velocity Angle : ' + calcAngle( PlayerOne.vel.x, PlayerOne.vel.y ) );
};
};
//+ Bouncy : prototype : Ball object
Bouncy.prototype.Ball = function() {
// Every Ball object refers to its own functions
var Ball = this;
// Ball : properties : public
this.pos = { x: 0, y: 0 };
this.vel = { x: 0, y: 0, rot: 0 };
this.acc = { x: 0, y: 0, jump: 0 };
this.diameter;
this.bounce;
this.friction;
this.cc = {
next: { x: this.pos.x, y: this.pos.y},
incident: { x: 0, y: 0, angle: 0, total: 0 },
reflection: { x: 0, y: 0 },
surface_normal: { x: 0, y: 0 }
};
var image = new Image();
var canvas = document.createElement( 'canvas' );
var ctx;
this.setImage = function( image_path ) {
canvas.width = this.diameter;
canvas.height = this.diameter;
image.src = image_path;
image.onload = function() {
ctx = canvas.getContext( '2d' );
ctx.drawImage( this, 0, 0, canvas.width, canvas.height );
};
};
this.rotate = function() {
if( Math.abs(this.vel.rot) > 0.012 ) {
ctx.translate( this.diameter / 2, this.diameter / 2 );
ctx.rotate( this.vel.rot );
ctx.translate( -this.diameter / 2, -this.diameter / 2 );
ctx.drawImage( image, 0, 0, this.diameter, this.diameter );
}
};
this.drawTo = function( target_ctx, x_offset, y_offset ) {
target_ctx.drawImage( canvas, ( this.pos.x - x_offset ), ( this.pos.y - y_offset ), this.diameter, this.diameter );
};
this.getCanvas = function() { return canvas; };
this.getCanvasCTX = function() { return ctx; };
this.getImageData = function() {
return ctx.getImageData( 0, 0, this.diameter, this.diameter );
};
};
Bouncy.prototype.Ball.prototype.checkCollisionLayer = function( collision_layer, parameters ) {
// Initialize this's collision data
this.cc.next = { x: this.pos.x, y: this.pos.y };
this.cc.incident = { x: 0, y: 0, angle: 0, total: 0 };
this.cc.reflection = { x: 0, y: 0 };
this.cc.surface_normal = { x: 0, y: 0 };
// Calc variables
var check_angle;
var relative_coordinate;
var check = { x: 0, y: 0 };
var vf_i = 0;
// Check velocity vector for collision, one unit vector at a time
while( vf_i <= Math.ceil( this.vel.magnitude ) && this.cc.incident.total === 0 ) {
// First advance the the sweep coordinates by 1 velocity unit
if( vf_i > 0 ) {
this.cc.next.x += this.vel.unit_vector.x;
this.cc.next.y += this.vel.unit_vector.y; }
// Sweep collision edge normal to velocity vector
for( var degrees = -90; degrees <= 90; degrees += parameters.sweep_degrees ) {
// Get relative relative_coordinate coordinates
check_angle = this.vel.angle + degrees;
relative_coordinate = getCircleCoordAtAngle( check_angle, this.diameter / 2, 1, 1 );
// Get absolute coordinates
check = {
x: Math.round( this.cc.next.x + ( this.diameter / 2 ) + relative_coordinate.x ),
y: Math.round( this.cc.next.y + ( this.diameter / 2 ) + relative_coordinate.y ) };
// Check absolute coordinate in collision array for 255
if( collision_layer.array[check.y][check.x] === 255 ) {
this.cc.incident.angle += check_angle;
this.cc.incident.total++; }
} // End of sweep
// Reflect velocity vector if collisions were detected
if( this.cc.incident.total > 0 ) {
// Calculate indcident reflection vector
this.cc.incident.angle = this.cc.incident.angle / this.cc.incident.total;
this.cc.incident.surface_normal = getCircleCoordAtAngle( this.cc.incident.angle, this.diameter / 2, 1, 1 );
this.cc.incident.reflection = calcReflection( this.vel.unit_vector, this.cc.incident.surface_normal );
// Calculate reflected velocity vector
this.vel.x = this.cc.incident.reflection.x * this.vel.magnitude;
this.vel.y = this.cc.incident.reflection.y * this.vel.magnitude;
this.cc.incident.surface_normal.magnitude = calcMagnitude( this.cc.incident.surface_normal.x, this.cc.incident.surface_normal.y );
this.vel.x -= ( ( 1 - this.bounce ) * this.vel.x ) * Math.abs( this.cc.incident.surface_normal.x / this.cc.incident.surface_normal.magnitude );
this.vel.y -= ( ( 1 - this.bounce ) * this.vel.y ) * Math.abs( this.cc.incident.surface_normal.y / this.cc.incident.surface_normal.magnitude );
// Subtract 1 velocity unit back from the current coordinates
this.cc.next.x -= this.vel.unit_vector.x;
this.cc.next.y -= this.vel.unit_vector.y;
// Add back left over velocity as reflected vector
if( vf_i < this.vel.magnitude ) {
this.vel.magnitude = calcMagnitude( this.vel.x, this.vel.y );
this.cc.next.x += ( this.cc.incident.reflection.x * ( this.vel.magnitude - vf_i ) );
this.cc.next.y += ( this.cc.incident.reflection.y * ( this.vel.magnitude - vf_i ) );
}
// Calculate reflection to surface coincidence vector and magnitude
this.cc.incident.coincidence = {
x: ( this.cc.incident.surface_normal.x / this.cc.incident.surface_normal.magnitude * this.vel.x ),
y: ( this.cc.incident.surface_normal.y / this.cc.incident.surface_normal.magnitude * this.vel.y ) };
this.cc.incident.coincidence.magnitude = calcMagnitude( this.cc.incident.coincidence.x, this.cc.incident.coincidence.y );
}
vf_i++; // Increment velocity factor itterator\
} // End of velocity check, while
// Set final positions
this.pos.x = Math.round( this.cc.next.x );
this.pos.y = Math.round( this.cc.next.y );
};
Bouncy.prototype.Ball.prototype.resolvePhysics = function( state ) {
// Add acceleration and gravity to velocity
this.vel.y += this.acc.y + state.gravity.y;
this.vel.x += this.acc.x + state.gravity.x;
// Calculate velocity magnitude and angle
this.vel.magnitude = calcMagnitude( this.vel.x, this.vel.y );
this.vel.angle = calcAngle( this.vel.x, this.vel.y );
// Calculate the velocity unit vector
this.vel.unit_vector = {
x: ( ( this.vel.magnitude !== 0 ) ? this.vel.x / this.vel.magnitude : 0 ),
y: ( ( this.vel.magnitude !== 0 ) ? this.vel.y / this.vel.magnitude : 0 ) };
// Calculate rotational velocity
this.vel.rot = 0;
if( calcMagnitude(this.vel.x, this.vel.y) >= 1 ) {
this.vel.rot = 2 * ((this.vel.x*state.gravity.y)-(this.vel.y*state.gravity.x)) / this.diameter;
}
// Call this to redraw its identity canvas
this.rotate();
};
//+ Bouncy : prototype : ViewPort object
Bouncy.prototype.ViewPort = function() {
var canvas = document.createElement( 'canvas' );
var buffer = document.createElement( 'canvas' );
canvas.style.width = '100%';
canvas.style.height = '100%';
var ctx = canvas.getContext( '2d' );
var buffer_ctx = buffer.getContext( '2d' );
this.offset = { x: 0, y: 0 };
this.setDimensions = function( d_x, d_y ) {
canvas.width = d_x;
canvas.height = d_y;
buffer.width = d_x;
buffer.height = d_y;
};
this.getDimensions = function() {
return { x: canvas.width, y: canvas.height };
};
this.clear = function() {
ctx.clearRect( 0, 0, canvas.width, canvas.height );
};
this.clearBuffer = function() {
buffer_ctx.clearRect( 0, 0, buffer.width, buffer.height );
};
this.whiteBuffer = function() {
buffer_ctx.fillStyle = 'white';
buffer_ctx.fillRect( 0, 0, buffer.width, buffer.height );
};
this.bufferImageData = function( image_data, x, y ) {
buffer_ctx.putImageData( image_data, x - this.offset.x, y - this.offset.y );
};
this.bufferImage = function( image, x, y ) {
buffer_ctx.drawImage( image, x - this.offset.x, y - this.offset.y );
};
this.update = function() {
this.clear();
ctx.drawImage( buffer, 0, 0 );
};
this.requestFullScreen = function() {
if( canvas.requestFullscreen ) { canvas.requestFullscreen(); }
else if( canvas.msRequestFullscreen ) { canvas.msRequestFullscreen(); }
else if( canvas.mozRequestFullScreen ) { canvas.mozRequestFullScreen(); }
else if( canvas.webkitRequestFullscreen ) { canvas.webkitRequestFullscreen(); }
};
this.getCanvas = function() { return canvas; };
};
//+ Bouncy : prototype : Map object
Bouncy.prototype.Map = function( ) {
// Each Map object may refer to its own Map functions
var Map = this;
// These are the available layers
this.layers = [];
this.layers_loaded = 0;
//+ Map : load map method : method, public
this.loadMap = function( map_name, callback ) {
var map_parameters = document.createElement('script');
map_parameters.onload = function() { loadLayers( BOUNCY_MAP_DEF, callback ); };
map_parameters.src = 'maps/' + map_name + '/def_map.js';
document.getElementsByTagName( 'head' )[0].appendChild(map_parameters);
};
var loadLayers = function( MAP_DEF, callback ) {
MAP_DEF.layers.forEach( function( layer ) {
// Create new layer object
Map.layers[ layer.name ] = new Map.Layer( layer );
Map.layers[ layer.name ].vp_dimensions = MAP_DEF.vp_dimensions;
switch( layer.type ) {
case 'collision':
Map.layers[ layer.name ].image.onload = function() {
Map.layers[ layer.name ].ctx = Map.layers[ layer.name ].canvas.getContext( '2d' );
Map.layers[ layer.name ].ctx.drawImage( this, 0, 0 );
Map.layers[ layer.name ].imageData = Map.layers[ layer.name ].ctx.getImageData( 0, 0, this.width, this.height );
Map.layers[ layer.name ].array = [ ];
for( var i_y = 0; i_y < this.height; i_y++ ) {
Map.layers[ layer.name ].array[i_y] = [];
for( var i_x = 0; i_x < this.width; i_x++ ) {
Map.layers[ layer.name ].array[ i_y ][ i_x ] = Map.layers[ layer.name ].imageData.data[ ( ( i_y * this.width + i_x ) * 4 ) + 3 ];
}
}
Map.layers_loaded++;
if( Map.layers_loaded === MAP_DEF.layers.length ) { callback( MAP_DEF ); }
};
// Set each layer's image source
Map.layers[ layer.name ].image.src = 'maps/' + MAP_DEF.name + '/' + layer.name + '.png';
break;
case 'raster':
Map.layers[ layer.name ].image.onload = function() {
Map.layers[ layer.name ].ctx = Map.layers[ layer.name ].canvas.getContext( '2d' );
Map.layers[ layer.name ].ctx.drawImage( this, 0, 0 );
// If this is the collision layer, generate imageData array
Map.layers_loaded++;
if( Map.layers_loaded === MAP_DEF.layers.length ) { callback( MAP_DEF ); }
};
// Set each layer's image source
Map.layers[ layer.name ].image.src = 'maps/' + MAP_DEF.name + '/' + layer.name + '.png';
break;
case 'generate':
Map.layers[ layer.name ].ctx = Map.layers[ layer.name ].canvas.getContext('2d');
Map.layers[ layer.name ].animate_buffer.width = Map.layers[ layer.name ].parameters.dimensions.x;
Map.layers[ layer.name ].animate_buffer.height = Map.layers[ layer.name ].parameters.dimensions.y;
Map.layers[ layer.name ].animate_buffer_ctx = Map.layers[ layer.name ].animate_buffer.getContext('2d');
Map.layers[ layer.name ].generate( );
Map.layers_loaded++;
if( Map.layers_loaded === MAP_DEF.layers.length ) { callback( MAP_DEF ); }
break;
}
if( layer.animate ) {
Map.layers[ layer.name ].animate();
}
} );
};
};
Bouncy.prototype.Map.prototype.Layer = function( parameters ) {
this.parameters = parameters;
this.image = document.createElement( 'img' );
this.canvas = document.createElement( 'canvas' );
this.canvas.width = this.parameters.dimensions.x;
this.canvas.height = this.parameters.dimensions.y;
this.animate_buffer = document.createElement( 'canvas' );
this.animate_buffer.width = this.parameters.dimensions.x;
this.animate_buffer.height = this.parameters.dimensions.y;
this.animate_float = { x: 0, y: 0 };
};
Bouncy.prototype.Map.prototype.Layer.prototype.generate = function( ) {
switch( this.parameters.objects.type ) {
case 'clouds':
for( var g_i = 0; g_i < this.parameters.objects.count; g_i++ ) {
var random_start = {
x: (Math.random() * (this.parameters.dimensions.x)),
y: (Math.random() * (this.parameters.dimensions.y - this.parameters.objects.dimensions.y)) };
var delta = {
x: (Math.random()*this.parameters.objects.dimensions.variation.x) + this.parameters.objects.dimensions.x,
y: (Math.random()*this.parameters.objects.dimensions.variation.y) + this.parameters.objects.dimensions.y };
//var red = parseInt(Math.random()*255);
//var green = parseInt(Math.random()*255);
//var blue = parseInt(Math.random()*255);
this.ctx.fillStyle = this.parameters.objects.color;
if( random_start.x + delta.x < this.parameters.dimensions.x ) {
this.ctx.fillRect( random_start.x, random_start.y, delta.x, delta.y );
}
else {
this.ctx.fillRect( random_start.x, random_start.y, this.parameters.dimensions.x-random_start.x, delta.y );
this.ctx.fillRect( 0, random_start.y, (random_start.x+delta.x)-this.parameters.dimensions.x, delta.y );
}
}
break;
}
};
Bouncy.prototype.Map.prototype.Layer.prototype.animate = function( ) {
switch( this.parameters.animate.type ) {
case 'translate':
this.animate_float.x += this.parameters.animate.speed.x;
this.animate_float.y += this.parameters.animate.speed.y;
if ( this.animate_float.x >= 1 || this.animate_float.y >= 1 ) {
this.animate_buffer_ctx.clearRect(0, 0, this.parameters.dimensions.x, this.parameters.dimensions.y);
this.animate_buffer_ctx.drawImage(
this.canvas,
this.animate_float.x,
this.animate_float.y,
this.parameters.dimensions.x - this.animate_float.x,
this.parameters.dimensions.y - this.animate_float.y,
0,
0,
this.parameters.dimensions.x - this.animate_float.x,
this.parameters.dimensions.y - this.animate_float.y
);
this.animate_buffer_ctx.drawImage(
this.canvas,
0,
0,
this.animate_float.x,
this.parameters.dimensions.y,
this.parameters.dimensions.x - this.animate_float.x,
this.animate_float.y,
this.animate_float.x,
this.parameters.dimensions.y
);
this.animate_float.x = 0;
this.animate_float.y = 0;
this.ctx.clearRect(0, 0, this.parameters.dimensions.x, this.parameters.dimensions.y);
this.ctx.drawImage(this.animate_buffer, 0, 0);
}
break;
}
};