File

src/app/models/babylonjs5-confetti.ts

Constructor

constructor(babylonIntegration: BabylonIntegration, amountParticles: number, animationDuration: number, animationName: ParticleAnimName, startPosition: Vector3)
Parameters :
  • babylonIntegration

    BabylonIntegration

  • startPosition

    Vectors of starting position

  • amountParticles

    amount of particles into the scene

  • animationDuration

    duration in miliseconds

Methods

Private init
init()
Returns: void
Public runParticles
runParticles()

Start animation

Returns: void
Private recycleParticle
recycleParticle(particle: SolidParticle)

Particles during the animation

Returns: void
explosionAnim
explosionAnim(particle: SolidParticle)

Confetti explosion anim

Parameters :
  • particle

    SolidParticle

Returns: void

Properties

amountParticles
amountParticles: number
animationDuration
animationDuration: number
animationName
animationName: ParticleAnimName
babylonIntegration
babylonIntegration: BabylonIntegration
currentSPS
currentSPS: SolidParticleSystem
mesh
mesh: Mesh
observer
observer: Observer<any>
position
position: Vector3
import { Mesh, Vector3, SolidParticleSystem,
		 SolidParticle, Observer,MeshBuilder, CubicEase,
		 Animation, EasingFunction, Scalar,Color4 } from "@babylonjs/core";
import { AppUtils } from "../app-utils";
import { BabylonIntegration } from "./babylon-integration";

export enum ParticleAnimName {
	explosion,
	basket
}
export class BabylonJs5Confetti {
	babylonIntegration: BabylonIntegration;
	mesh: Mesh;
	position: Vector3;
	amountParticles: number;
	currentSPS: SolidParticleSystem;
	observer: Observer<any>;
	animationDuration: number;
	animationName: ParticleAnimName;

	/**
	 * https://doc.babylonjs.com/features/featuresDeepDive/particles/solid_particle_system/sps_intro
	 *
	 * @param babylonIntegration BabylonIntegration
	 * @param startPosition Vectors of starting position
	 * @param amountParticles amount of particles into the scene
	 * @param animationDuration duration in miliseconds
	 */
	constructor(
		babylonIntegration: BabylonIntegration,
		amountParticles: number,
		animationDuration: number,
		animationName: ParticleAnimName,
		startPosition?: Vector3
	) {
		this.babylonIntegration = babylonIntegration;
		if (startPosition) {
			this.position = startPosition;
		} else {
			this.position = new Vector3(0, 0, 0);
		}
		this.animationName = animationName;
		this.animationDuration = animationDuration;
		this.amountParticles = amountParticles;
		this.currentSPS = this.init();
		this.currentSPS.setParticles();
	}

	private init() {
		const SPS = new SolidParticleSystem("SPS", this.babylonIntegration.scene) as SolidParticleSystem;
		const tetra = MeshBuilder.CreatePlane("tetra", { size: 0.18 });
		SPS.addShape(tetra, this.amountParticles);
		tetra.dispose();
		this.mesh = SPS.buildMesh();
		this.mesh.layerMask = this.babylonIntegration.camera.layerMask;
		SPS.initParticles = () => {
			for (let p = 0; p < SPS.nbParticles; p++) {
				this.recycleParticle(SPS.particles[p]);
			}
		};
		SPS.initParticles();
		return SPS;
	}

	/**
	 * Start animation
	 */
	public async runParticles() {
		let gravity: number;
		this.currentSPS.updateParticle = particle => {
			gravity = this.animationName === ParticleAnimName.basket ? -0.005 : -0.0003;
			particle.velocity.y += gravity;
			particle.position.addInPlace(particle.velocity); // update particle new position
			particle.rotation.addInPlace(particle.velocity);
			return particle;
		};
		this.observer = this.babylonIntegration.scene.onAfterRenderObservable.add(async () => {
			this.currentSPS.setParticles();
		});

		await AppUtils.timeOut(this.animationDuration);

		const ease = new CubicEase();
		ease.setEasingMode(EasingFunction.EASINGMODE_EASEINOUT);

		const fade = Animation.CreateAndStartAnimation("fm1", this.mesh, "visibility", 60, 60, this.mesh.visibility, 0, 0, ease);
		fade.onAnimationEnd = () => {
			if (this.babylonIntegration.scene) {
				// verify if scene existe maybe we already leave the page at the end of the activity
				this.babylonIntegration.scene.onAfterRenderObservable.remove(this.observer);
			}
			this.currentSPS.dispose();
		};
		fade.disposeOnEnd = true;
	}

	/**
	 * Particles during the animation
	 */
	private recycleParticle(particle: SolidParticle) {
		if (this.animationName === ParticleAnimName.explosion) {
			this.explosionAnim(particle);
		}
	}

	/**
	 * Confetti explosion anim
	 *
	 * @param particle SolidParticle
	 */
	explosionAnim(particle: SolidParticle) {
		const speed = 0.3;
		particle.position.x = this.position.x;
		particle.position.y = this.position.y;
		particle.position.z = this.position.z;
		particle.rotation.x = Scalar.RandomRange(-Math.PI, Math.PI);
		particle.rotation.y = Scalar.RandomRange(-Math.PI, Math.PI);
		particle.rotation.z = Scalar.RandomRange(-Math.PI, Math.PI);
		const hexColorsAccepted = ["#a864fd", "#29cdff", "#78ff44", "#ff718d", "#fdff6a", "#02FEFF", "#6D2EF1"];
		particle.color = Color4.FromHexString(hexColorsAccepted[Math.floor(Math.random() * hexColorsAccepted.length)]);
		particle.velocity.x = Scalar.RandomRange(-0.2 * speed, 0.2 * speed);
		particle.velocity.z = Scalar.RandomRange(-0.2 * speed, 0.2 * speed);
		particle.velocity.y = Scalar.RandomRange(0.01, 0.12);
	}

}

results matching ""

    No results matching ""