diff --git a/README.md b/README.md index 4dd186c9..e504883f 100644 --- a/README.md +++ b/README.md @@ -204,6 +204,7 @@ npm install @pixi/particle-emitter * [Snow](https://pixijs.github.io/particle-emitter/examples/snow.html) * [Sparks](https://pixijs.github.io/particle-emitter/examples/sparks.html) * [Fountain](https://pixijs.github.io/particle-emitter/examples/fountain.html) +* [Confetti](https://pixijs.github.io/particle-emitter/examples/confetti.html) * [Animated Coins](https://pixijs.github.io/particle-emitter/examples/coins.html) * [Animated Bubbles](https://pixijs.github.io/particle-emitter/examples/animatedBubbles.html) * [Spaceship Destruction - Ordered Art](https://pixijs.github.io/particle-emitter/examples/spaceshipDestruction.html) diff --git a/docs/examples/bubbles.html b/docs/examples/bubbles.html index 97306446..bcc8f0ed 100644 --- a/docs/examples/bubbles.html +++ b/docs/examples/bubbles.html @@ -1,11 +1,11 @@ - - + + Bubbles - - + + @@ -13,14 +13,12 @@ -
Click Anywhere
- \ No newline at end of file + diff --git a/docs/examples/confetti.html b/docs/examples/confetti.html new file mode 100644 index 00000000..145a82ce --- /dev/null +++ b/docs/examples/confetti.html @@ -0,0 +1,122 @@ + + + + + + Confetti + + + + + + + + + + + +
+
Click Anywhere
+ + + + diff --git a/docs/examples/images/confetti-0.png b/docs/examples/images/confetti-0.png new file mode 100644 index 00000000..ce64b866 Binary files /dev/null and b/docs/examples/images/confetti-0.png differ diff --git a/docs/examples/images/confetti-1.png b/docs/examples/images/confetti-1.png new file mode 100644 index 00000000..b6dba2ac Binary files /dev/null and b/docs/examples/images/confetti-1.png differ diff --git a/docs/examples/images/confetti-2.png b/docs/examples/images/confetti-2.png new file mode 100644 index 00000000..6f0fa46e Binary files /dev/null and b/docs/examples/images/confetti-2.png differ diff --git a/docs/examples/index.html b/docs/examples/index.html index 5da23d87..1c235391 100644 --- a/docs/examples/index.html +++ b/docs/examples/index.html @@ -35,6 +35,7 @@

Emitter Examples

  • Snow
  • Sparks
  • Fountain
  • +
  • Confetti
  • Animated Coins
  • Animated Bubbles
  • Spaceship Destruction
  • diff --git a/src/EmitterConfig.ts b/src/EmitterConfig.ts index 68e69165..98fbee5f 100644 --- a/src/EmitterConfig.ts +++ b/src/EmitterConfig.ts @@ -112,7 +112,7 @@ export function upgradeConfig(config: EmitterConfigV2|EmitterConfigV1, art: any) // just ensure we aren't given any V3 config data if ('behaviors' in config) { - return config; + return config as EmitterConfigV3; } const out: EmitterConfigV3 = { diff --git a/src/behaviors/Alpha.ts b/src/behaviors/Alpha.ts index 60adf203..3de058d1 100644 --- a/src/behaviors/Alpha.ts +++ b/src/behaviors/Alpha.ts @@ -10,10 +10,10 @@ import { BehaviorEditorConfig } from './editor/Types'; * Example config: * ```javascript * { - * type: 'alpha', - * config: { - * alpha: { - * list: [{value: 0, time: 0}, {value: 1, time: 0.25}, {value: 0, time: 1}] + * "type": "alpha", + * "config": { + * "alpha": { + * "list": [{"value": 0, "time": 0}, {"value": 1, "time": 0.25}, {"value": 0, "time": 1}] * }, * } * } @@ -21,37 +21,38 @@ import { BehaviorEditorConfig } from './editor/Types'; */ export class AlphaBehavior implements IEmitterBehavior { - public static type = 'alpha'; - public static editorConfig: BehaviorEditorConfig = null; + public static type = 'alpha'; + public static editorConfig: BehaviorEditorConfig = null; - public order = BehaviorOrder.Normal; - private list: PropertyList; - constructor(config: { - /** - * Transparency of the particles from 0 (transparent) to 1 (opaque) - */ - alpha: ValueList; - }) - { - this.list = new PropertyList(false); - this.list.reset(PropertyNode.createList(config.alpha)); - } + public order = BehaviorOrder.Normal; + private list: PropertyList; + constructor(config: { + /** + * Transparency of the particles from 0 (transparent) to 1 (opaque) + */ + alpha: ValueList; + }) + { + this.list = new PropertyList(false); + this.list.reset(PropertyNode.createList(config.alpha)); + } - initParticles(first: Particle): void - { - let next = first; + initParticles(first: Particle): void + { + let next = first; - while (next) - { - next.alpha = this.list.first.value; - next = next.next; - } - } + while (next) + { + next.config.originAlpha = next.config.originAlpha ?? 1; + next.alpha = next.config.originAlpha * this.list.first.value; + next = next.next; + } + } - updateParticle(particle: Particle): void - { - particle.alpha = this.list.interpolate(particle.agePercent); - } + updateParticle(particle: Particle): void + { + particle.alpha = this.list.interpolate(particle.agePercent); + } } /** @@ -60,38 +61,56 @@ export class AlphaBehavior implements IEmitterBehavior * Example config: * ```javascript * { - * type: 'alphaStatic', - * config: { - * alpha: 0.75, + * "type": "alphaStatic", + * "config": { + * "alpha": 0.75, * } * } * ``` + * or + * ```javascript + * { + * "type": "alphaStatic", + * "config": { + * "alpha": {"min": 0.5, "max": 1} + * } + * } */ + +type AlphaData = { + min: number; + max: number; +}; export class StaticAlphaBehavior implements IEmitterBehavior { - public static type = 'alphaStatic'; - public static editorConfig: BehaviorEditorConfig = null; + public static type = 'alphaStatic'; + public static editorConfig: BehaviorEditorConfig = null; + + public order = BehaviorOrder.Normal; + private value: number | AlphaData; + constructor(config: { + /** + * Transparency of the particles from 0 (transparent) to 1 (opaque) + */ + alpha: number | AlphaData; + }) + { + this.value = config.alpha; + } - public order = BehaviorOrder.Normal; - private value: number; - constructor(config: { - /** - * Transparency of the particles from 0 (transparent) to 1 (opaque) - */ - alpha: number; - }) - { - this.value = config.alpha; - } + initParticles(first: Particle): void + { + let next = first; - initParticles(first: Particle): void - { - let next = first; + while (next) + { + const val + = typeof this.value === 'number' + ? this.value + : (Math.random() * (this.value.max - this.value.min)) + this.value.min; - while (next) - { - next.alpha = this.value; - next = next.next; - } - } + next.config.originAlpha = next.alpha = Math.min(val, 1); + next = next.next; + } + } } diff --git a/src/behaviors/Color.ts b/src/behaviors/Color.ts index ed13e7b1..0e41f63f 100644 --- a/src/behaviors/Color.ts +++ b/src/behaviors/Color.ts @@ -11,10 +11,10 @@ import { BehaviorEditorConfig } from './editor/Types'; * Example config: * ```javascript * { - * type: 'color', - * config: { - * color: { - * list: [{value: '#ff0000' time: 0}, {value: '#00ff00', time: 0.5}, {value: '#0000ff', time: 1}] + * "type": "color", + * "config": { + * "color": { + * "list": [{"value": "#ff0000", "time": 0}, {"value": "#00ff00", "time": 0.5}, {"value": "#0000ff", "time": 1}] * }, * } * } @@ -22,90 +22,93 @@ import { BehaviorEditorConfig } from './editor/Types'; */ export class ColorBehavior implements IEmitterBehavior { - public static type = 'color'; - public static editorConfig: BehaviorEditorConfig = null; + public static type = 'color'; + public static editorConfig: BehaviorEditorConfig = null; - public order = BehaviorOrder.Normal; - private list: PropertyList; - constructor(config: { - /** - * Color of the particles as 6 digit hex codes. - */ - color: ValueList; - }) - { - this.list = new PropertyList(true); - this.list.reset(PropertyNode.createList(config.color)); - } + public order = BehaviorOrder.Normal; + private list: PropertyList; + constructor(config: { + /** + * Color of the particles as 6 digit hex codes. + */ + color: ValueList; + }) + { + this.list = new PropertyList(true); + this.list.reset(PropertyNode.createList(config.color)); + } - initParticles(first: Particle): void - { - let next = first; - const color = this.list.first.value; - const tint = combineRGBComponents(color.r, color.g, color.b); + initParticles(first: Particle): void + { + let next = first; + const color = this.list.first.value; + const tint = combineRGBComponents(color.r, color.g, color.b); - while (next) - { - next.tint = tint; - next = next.next; - } - } + while (next) + { + next.tint = tint; + next = next.next; + } + } - updateParticle(particle: Particle): void - { - particle.tint = this.list.interpolate(particle.agePercent); - } + updateParticle(particle: Particle): void + { + particle.tint = this.list.interpolate(particle.agePercent); + } } /** - * A Color behavior that applies a single color to the particle's tint property at initialization. + * A Color behavior that applies a randomly picked color to the particle's tint property at initialization. * * Example config: * ```javascript * { - * type: 'colorStatic', - * config: { - * color: '#ffff00', + * "type": "colorStatic", + * "config": { + * "color": ["#ffffff", "#ffff00"], * } * } * ``` */ export class StaticColorBehavior implements IEmitterBehavior { - public static type = 'colorStatic'; - public static editorConfig: BehaviorEditorConfig = null; + public static type = 'colorStatic'; + public static editorConfig: BehaviorEditorConfig = null; - public order = BehaviorOrder.Normal; - private value: number; - constructor(config: { - /** - * Color of the particles as 6 digit hex codes. - */ - color: string; - }) - { - let color = config.color; + public order = BehaviorOrder.Spawn; + private values: number[] = []; + constructor(config: { + /** + * Color of the particles as 6 digit hex codes. + */ + color: string | string[]; + }) + { + const colors + = config.color instanceof Array ? config.color : [config.color]; - if (color.charAt(0) === '#') - { - color = color.substr(1); - } - else if (color.indexOf('0x') === 0) - { - color = color.substr(2); - } + colors.forEach((color) => + { + if (color.charAt(0) === '#') + { + color = color.substring(1); + } + else if (color.indexOf('0x') === 0) + { + color = color.substring(2); + } + this.values.push(parseInt(color, 16)); + }); + } - this.value = parseInt(color, 16); - } + initParticles(first: Particle): void + { + let next = first; - initParticles(first: Particle): void - { - let next = first; - - while (next) - { - next.tint = this.value; - next = next.next; - } - } + while (next) + { + next.tint = this.values[Math.round(Math.random() * this.values.length)]; + next = next.next; + } + } } diff --git a/src/behaviors/Scale.ts b/src/behaviors/Scale.ts index b892d06c..df33ac60 100644 --- a/src/behaviors/Scale.ts +++ b/src/behaviors/Scale.ts @@ -10,61 +10,66 @@ import { BehaviorEditorConfig } from './editor/Types'; * Example config: * ```javascript * { - * type: 'scale', - * config: { - * scale: { - * list: [{value: 0, time: 0}, {value: 1, time: 0.25}, {value: 0, time: 1}], - * isStepped: true + * "type": "scale", + * "config": { + * "scale": { + * "list": [{"value": 0, "time": 0}, {"value": 1, "time": 0.25}, {"value": 0, "time": 1}], + * "isStepped": "true" * }, - * minMult: 0.5 + * "minMult": 0.5 * } * } * ``` */ export class ScaleBehavior implements IEmitterBehavior { - public static type = 'scale'; - public static editorConfig: BehaviorEditorConfig = null; + public static type = 'scale'; + public static editorConfig: BehaviorEditorConfig = null; - public order = BehaviorOrder.Normal; - private list: PropertyList; - private minMult: number; - constructor(config: { - /** - * Scale of the particles, with a minimum value of 0 - */ - scale: ValueList; - /** - * A value between minimum scale multipler and 1 is randomly - * generated and multiplied with each scale value to provide the actual scale for each particle. - */ - minMult: number; - }) - { - this.list = new PropertyList(false); - this.list.reset(PropertyNode.createList(config.scale)); - this.minMult = config.minMult ?? 1; - } + public order = BehaviorOrder.Normal; + private list: PropertyList; + private minMult: number; + constructor(config: { + /** + * Scale of the particles, with a minimum value of 0 + */ + scale: ValueList; + /** + * A value between minimum scale multipler and 1 is randomly + * generated and multiplied with each scale value to provide the actual scale for each particle. + */ + minMult: number; + }) + { + this.list = new PropertyList(false); + this.list.reset(PropertyNode.createList(config.scale)); + this.minMult = config.minMult ?? 1; + } - initParticles(first: Particle): void - { - let next = first; + initParticles(first: Particle): void + { + let next = first; - while (next) - { - const mult = (Math.random() * (1 - this.minMult)) + this.minMult; + while (next) + { + const mult = (Math.random() * (1 - this.minMult)) + this.minMult; - next.config.scaleMult = mult; - next.scale.x = next.scale.y = this.list.first.value * mult; + next.config.scaleMult = mult; + next.config.originScale = next.config.originScale ?? 1; + next.scale.x = next.scale.y + = next.config.originScale * this.list.first.value * mult; - next = next.next; - } - } + next = next.next; + } + } - updateParticle(particle: Particle): void - { - particle.scale.x = particle.scale.y = this.list.interpolate(particle.agePercent) * particle.config.scaleMult; - } + updateParticle(particle: Particle): void + { + particle.scale.x = particle.scale.y + = this.list.interpolate(particle.agePercent) + * particle.config.scaleMult + * particle.config.originScale; + } } /** @@ -73,48 +78,47 @@ export class ScaleBehavior implements IEmitterBehavior * Example config: * ```javascript * { - * type: 'scaleStatic', - * config: { - * min: 0.25, - * max: 0.75, + * "type": "scaleStatic", + * "config": { + * "min": 0.25, + * "max": 0.75, * } * } * ``` */ export class StaticScaleBehavior implements IEmitterBehavior { - public static type = 'scaleStatic'; - public static editorConfig: BehaviorEditorConfig = null; + public static type = 'scaleStatic'; + public static editorConfig: BehaviorEditorConfig = null; - public order = BehaviorOrder.Normal; - private min: number; - private max: number; - constructor(config: { - /** - * Minimum scale of the particles, with a minimum value of 0 - */ - min: number; - /** - * Maximum scale of the particles, with a minimum value of 0 - */ - max: number; - }) - { - this.min = config.min; - this.max = config.max; - } + public order = BehaviorOrder.Spawn; + private min: number; + private max: number; + constructor(config: { + /** + * Minimum scale of the particles, with a minimum value of 0 + */ + min: number; + /** + * Maximum scale of the particles, with a minimum value of 0 + */ + max: number; + }) + { + this.min = config.min; + this.max = config.max; + } - initParticles(first: Particle): void - { - let next = first; + initParticles(first: Particle): void + { + let next = first; - while (next) - { - const scale = (Math.random() * (this.max - this.min)) + this.min; + while (next) + { + const scale = (Math.random() * (this.max - this.min)) + this.min; - next.scale.x = next.scale.y = scale; - - next = next.next; - } - } + next.config.originScale = next.scale.x = next.scale.y = scale; + next = next.next; + } + } } diff --git a/src/index.ts b/src/index.ts index 16c06cfb..3300d886 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,8 +2,8 @@ import { Emitter } from './Emitter'; import * as behaviors from './behaviors'; Emitter.registerBehavior(behaviors.AccelerationBehavior); -Emitter.registerBehavior(behaviors.AlphaBehavior); Emitter.registerBehavior(behaviors.StaticAlphaBehavior); +Emitter.registerBehavior(behaviors.AlphaBehavior); Emitter.registerBehavior(behaviors.RandomAnimatedTextureBehavior); Emitter.registerBehavior(behaviors.SingleAnimatedTextureBehavior); Emitter.registerBehavior(behaviors.BlendModeBehavior); @@ -14,11 +14,11 @@ Emitter.registerBehavior(behaviors.OrderedTextureBehavior); Emitter.registerBehavior(behaviors.PathBehavior); Emitter.registerBehavior(behaviors.PointSpawnBehavior); Emitter.registerBehavior(behaviors.RandomTextureBehavior); -Emitter.registerBehavior(behaviors.RotationBehavior); Emitter.registerBehavior(behaviors.StaticRotationBehavior); +Emitter.registerBehavior(behaviors.RotationBehavior); Emitter.registerBehavior(behaviors.NoRotationBehavior); -Emitter.registerBehavior(behaviors.ScaleBehavior); Emitter.registerBehavior(behaviors.StaticScaleBehavior); +Emitter.registerBehavior(behaviors.ScaleBehavior); Emitter.registerBehavior(behaviors.ShapeSpawnBehavior); Emitter.registerBehavior(behaviors.SingleTextureBehavior); Emitter.registerBehavior(behaviors.SpeedBehavior);