229 lines
4.9 KiB
JavaScript
229 lines
4.9 KiB
JavaScript
import { Animation } from './Animation.js';
|
|
|
|
class Confetti {
|
|
|
|
constructor( game ) {
|
|
|
|
this.game = game;
|
|
this.started = 0;
|
|
|
|
this.options = {
|
|
speed: { min: 0.0011, max: 0.0022 },
|
|
revolution: { min: 0.01, max: 0.05 },
|
|
size: { min: 0.1, max: 0.15 },
|
|
colors: [ 0x41aac8, 0x82ca38, 0xffef48, 0xef3923, 0xff8c0a ],
|
|
}
|
|
|
|
this.geometry = new THREE.PlaneGeometry( 1, 1 );
|
|
this.material = new THREE.MeshLambertMaterial( { side: THREE.DoubleSide } );
|
|
|
|
this.holders = [
|
|
new ConfettiStage( this.game, this, 1, 20 ),
|
|
new ConfettiStage( this.game, this, -1, 30 ),
|
|
];
|
|
|
|
}
|
|
|
|
start() {
|
|
|
|
if ( this.started > 0 ) return;
|
|
|
|
this.holders.forEach( holder => {
|
|
|
|
this.game.world.scene.add( holder.holder );
|
|
holder.start();
|
|
this.started ++;
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
stop() {
|
|
|
|
if ( this.started == 0 ) return;
|
|
|
|
this.holders.forEach( holder => {
|
|
|
|
holder.stop( () => {
|
|
|
|
this.game.world.scene.remove( holder.holder );
|
|
this.started --;
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
updateColors( colors ) {
|
|
|
|
this.holders.forEach( holder => {
|
|
|
|
holder.options.colors.forEach( ( color, index ) => {
|
|
|
|
holder.options.colors[ index ] = colors[ [ 'D', 'F', 'R', 'B', 'L' ][ index ] ];
|
|
|
|
} );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class ConfettiStage extends Animation {
|
|
|
|
constructor( game, parent, distance, count ) {
|
|
|
|
super( false );
|
|
|
|
this.game = game;
|
|
this.parent = parent;
|
|
|
|
this.distanceFromCube = distance;
|
|
|
|
this.count = count;
|
|
this.particles = [];
|
|
|
|
this.holder = new THREE.Object3D();
|
|
this.holder.rotation.copy( this.game.world.camera.rotation );
|
|
|
|
this.object = new THREE.Object3D();
|
|
this.holder.add( this.object );
|
|
|
|
this.resizeViewport = this.resizeViewport.bind( this );
|
|
this.game.world.onResize.push( this.resizeViewport )
|
|
this.resizeViewport();
|
|
|
|
this.geometry = this.parent.geometry;
|
|
this.material = this.parent.material;
|
|
|
|
this.options = this.parent.options;
|
|
|
|
let i = this.count;
|
|
while ( i-- ) this.particles.push( new Particle( this ) );
|
|
|
|
}
|
|
|
|
start() {
|
|
|
|
this.time = performance.now();
|
|
this.playing = true;
|
|
|
|
let i = this.count;
|
|
while ( i-- ) this.particles[ i ].reset();
|
|
|
|
super.start();
|
|
|
|
}
|
|
|
|
stop( callback ) {
|
|
|
|
this.playing = false;
|
|
this.completed = 0;
|
|
this.callback = callback;
|
|
|
|
}
|
|
|
|
reset() {
|
|
|
|
super.stop();
|
|
|
|
this.callback();
|
|
|
|
}
|
|
|
|
update() {
|
|
|
|
const now = performance.now();
|
|
const delta = now - this.time;
|
|
this.time = now;
|
|
|
|
let i = this.count;
|
|
|
|
while ( i-- )
|
|
if ( ! this.particles[ i ].completed ) this.particles[ i ].update( delta );
|
|
|
|
if ( ! this.playing && this.completed == this.count ) this.reset();
|
|
|
|
}
|
|
|
|
resizeViewport() {
|
|
|
|
const fovRad = this.game.world.camera.fov * THREE.Math.DEG2RAD;
|
|
|
|
this.height = 2 * Math.tan( fovRad / 2 ) * ( this.game.world.camera.position.length() - this.distanceFromCube );
|
|
this.width = this.height * this.game.world.camera.aspect;
|
|
|
|
const scale = 1 / this.game.transition.data.cameraZoom;
|
|
|
|
this.width *= scale;
|
|
this.height *= scale;
|
|
|
|
this.object.position.z = this.distanceFromCube;
|
|
this.object.position.y = this.height / 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class Particle {
|
|
|
|
constructor( confetti ) {
|
|
|
|
this.confetti = confetti;
|
|
this.options = this.confetti.options;
|
|
|
|
this.velocity = new THREE.Vector3();
|
|
this.force = new THREE.Vector3();
|
|
|
|
this.mesh = new THREE.Mesh( this.confetti.geometry, this.confetti.material.clone() );
|
|
this.confetti.object.add( this.mesh );
|
|
|
|
this.size = THREE.Math.randFloat( this.options.size.min, this.options.size.max );
|
|
this.mesh.scale.set( this.size, this.size, this.size );
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
reset( randomHeight = true ) {
|
|
|
|
this.completed = false;
|
|
|
|
this.color = new THREE.Color( this.options.colors[ Math.floor( Math.random() * this.options.colors.length ) ] );
|
|
this.mesh.material.color.set( this.color );
|
|
|
|
this.speed = THREE.Math.randFloat( this.options.speed.min, this.options.speed.max ) * - 1;
|
|
this.mesh.position.x = THREE.Math.randFloat( - this.confetti.width / 2, this.confetti.width / 2 );
|
|
this.mesh.position.y = ( randomHeight )
|
|
? THREE.Math.randFloat( this.size, this.confetti.height + this.size )
|
|
: this.size;
|
|
|
|
this.revolutionSpeed = THREE.Math.randFloat( this.options.revolution.min, this.options.revolution.max );
|
|
this.revolutionAxis = [ 'x', 'y', 'z' ][ Math.floor( Math.random() * 3 ) ];
|
|
this.mesh.rotation.set( Math.random() * Math.PI / 3, Math.random() * Math.PI / 3, Math.random() * Math.PI / 3 );
|
|
|
|
}
|
|
|
|
stop() {
|
|
|
|
this.completed = true;
|
|
this.confetti.completed ++;
|
|
|
|
}
|
|
|
|
update( delta ) {
|
|
|
|
this.mesh.position.y += this.speed * delta;
|
|
this.mesh.rotation[ this.revolutionAxis ] += this.revolutionSpeed;
|
|
|
|
if ( this.mesh.position.y < - this.confetti.height - this.size )
|
|
( this.confetti.playing ) ? this.reset( false ) : this.stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export { Confetti };
|