Skip to content

Commit

Permalink
feat: fixed #739, added outModes instead of a single out mode, every …
Browse files Browse the repository at this point in the history
…edge now can be customized
  • Loading branch information
matteobruni committed Aug 27, 2020
1 parent cdb380c commit 67194dc
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 65 deletions.
3 changes: 2 additions & 1 deletion core/main/src/Core/Interfaces/IContainerPlugin.ts
Expand Up @@ -4,6 +4,7 @@ import type { ClickMode } from "../../Enums";
import type { RecursivePartial } from "../../Types";
import type { IOptions } from "../../Options/Interfaces/IOptions";
import type { IDelta } from "./IDelta";
import { OutModeDirection } from "../../Enums/Directions/OutModeDirection";

export interface IContainerPlugin {
draw?: (context: CanvasRenderingContext2D, delta: IDelta) => void;
Expand All @@ -20,6 +21,6 @@ export interface IContainerPlugin {
particlesInitialization?: () => boolean;
clickPositionValid?: (position: ICoordinates) => boolean;
handleClickMode?: (mode: ClickMode | string) => void;
particleBounce?: (particle: Particle, delta: IDelta) => boolean;
particleBounce?: (particle: Particle, delta: IDelta, direction: OutModeDirection) => boolean;
particleUpdate?: (particle: Particle, delta: IDelta) => void;
}
155 changes: 106 additions & 49 deletions core/main/src/Core/Particle/Updater.ts
@@ -1,8 +1,16 @@
import type { Container } from "../Container";
import type { Particle } from "../Particle";
import { Utils } from "../../Utils";
import { DestroyType, OpacityAnimationStatus, OutMode, RotateDirection, SizeAnimationStatus } from "../../Enums";
import {
DestroyType,
OpacityAnimationStatus,
OutMode,
OutModeAlt,
RotateDirection,
SizeAnimationStatus,
} from "../../Enums";
import type { IDelta } from "../Interfaces/IDelta";
import { OutModeDirection } from "../../Enums/Directions/OutModeDirection";

/**
* Particle updater, it manages movement
Expand Down Expand Up @@ -37,7 +45,7 @@ export class Updater {
this.updateStrokeColor(delta);

/* out of canvas modes */
this.updateOutMode(delta);
this.updateOutModes(delta);
}

private updateLife(delta: IDelta): void {
Expand Down Expand Up @@ -242,7 +250,63 @@ export class Updater {
}
}

private fixOutOfCanvasPosition(): void {
private updateOutModes(delta: IDelta): void {
const outModes = this.particle.particlesOptions.move.outModes;

this.updateOutMode(delta, outModes.bottom ?? outModes.default, OutModeDirection.bottom);
this.updateOutMode(delta, outModes.left ?? outModes.default, OutModeDirection.left);
this.updateOutMode(delta, outModes.right ?? outModes.default, OutModeDirection.right);
this.updateOutMode(delta, outModes.top ?? outModes.default, OutModeDirection.top);
}

private updateOutMode(
delta: IDelta,
outMode: OutMode | keyof typeof OutMode | OutModeAlt,
direction: OutModeDirection
) {
const container = this.container;
const particle = this.particle;
const gravityOptions = particle.particlesOptions.move.gravity;

switch (outMode) {
case OutMode.bounce:
case OutMode.bounceVertical:
case OutMode.bounceHorizontal:
case "bounceVertical":
case "bounceHorizontal":
this.updateBounce(delta, direction);

break;
case OutMode.destroy:
if (!Utils.isPointInside(particle.position, container.canvas.size, particle.size.value, direction)) {
container.particles.remove(particle);
}
break;
case OutMode.out:
if (!Utils.isPointInside(particle.position, container.canvas.size, particle.size.value, direction)) {
this.fixOutOfCanvasPosition(direction);
}
break;
case OutMode.none:
if (!gravityOptions.enable) {
container.particles.remove(particle);
} else {
const position = particle.position;

if (
(gravityOptions.acceleration >= 0 &&
position.y > container.canvas.size.height &&
direction === "bottom") ||
(gravityOptions.acceleration < 0 && position.y < 0 && direction === "top")
) {
container.particles.remove(particle);
}
}
break;
}
}

private fixOutOfCanvasPosition(direction: OutModeDirection): void {
const container = this.container;
const particle = this.particle;
const wrap = particle.particlesOptions.move.warp;
Expand All @@ -257,27 +321,27 @@ export class Updater {
const sizeValue = particle.size.value;
const nextBounds = Utils.calculateBounds(particle.position, sizeValue);

if (nextBounds.left > canvasSize.width - particle.offset.x) {
if (direction === OutModeDirection.right && nextBounds.left > canvasSize.width - particle.offset.x) {
particle.position.x = newPos.left;

if (!wrap) {
particle.position.y = Math.random() * canvasSize.height;
}
} else if (nextBounds.right < -particle.offset.x) {
} else if (direction === OutModeDirection.left && nextBounds.right < -particle.offset.x) {
particle.position.x = newPos.right;

if (!wrap) {
particle.position.y = Math.random() * canvasSize.height;
}
}

if (nextBounds.top > canvasSize.height - particle.offset.y) {
if (direction === OutModeDirection.bottom && nextBounds.top > canvasSize.height - particle.offset.y) {
if (!wrap) {
particle.position.x = Math.random() * canvasSize.width;
}

particle.position.y = newPos.top;
} else if (nextBounds.bottom < -particle.offset.y) {
} else if (direction === OutModeDirection.top && nextBounds.bottom < -particle.offset.y) {
if (!wrap) {
particle.position.x = Math.random() * canvasSize.width;
}
Expand All @@ -286,39 +350,14 @@ export class Updater {
}
}

private updateOutMode(delta: IDelta): void {
const container = this.container;
const particle = this.particle;

switch (particle.particlesOptions.move.outMode) {
case OutMode.bounce:
case OutMode.bounceVertical:
case OutMode.bounceHorizontal:
this.updateBounce(delta);

break;
case OutMode.destroy:
if (!Utils.isPointInside(particle.position, container.canvas.size, particle.size.value)) {
particle.destroy();
container.particles.remove(particle);
return;
}
break;
case OutMode.out:
if (!Utils.isPointInside(particle.position, container.canvas.size, particle.size.value)) {
this.fixOutOfCanvasPosition();
}
}
}

private updateBounce(delta: IDelta): void {
private updateBounce(delta: IDelta, direction: OutModeDirection): void {
const container = this.container;
const particle = this.particle;
let handled = false;

for (const [, plugin] of container.plugins) {
if (plugin.particleBounce !== undefined) {
handled = plugin.particleBounce(particle, delta);
handled = plugin.particleBounce(particle, delta, direction);
}

if (handled) {
Expand All @@ -337,45 +376,63 @@ export class Updater {
bounds = Utils.calculateBounds(pos, size),
canvasSize = container.canvas.size;

if (outMode === OutMode.bounce || outMode === OutMode.bounceHorizontal) {
if (outMode === OutMode.bounce || outMode === OutMode.bounceHorizontal || outMode === "bounceHorizontal") {
const velocity = particle.velocity.horizontal;
let bounced = false;

if ((bounds.right >= canvasSize.width && velocity > 0) || (bounds.left <= 0 && velocity < 0)) {
if (
(direction === OutModeDirection.right && bounds.right >= canvasSize.width && velocity > 0) ||
(direction === OutModeDirection.left && bounds.left <= 0 && velocity < 0)
) {
const factor = particle.particlesOptions.bounce.horizontal;
const newVelocity = factor.random.enable
? Utils.randomInRange(factor.value, factor.random.minimumValue)
: factor.value;

particle.velocity.horizontal *= -newVelocity;

bounced = true;
}

const minPos = offset.x + size;
if (bounced) {
const minPos = offset.x + size;

if (bounds.right >= canvasSize.width) {
particle.position.x = canvasSize.width - minPos;
} else if (bounds.left <= 0) {
particle.position.x = minPos;
if (bounds.right >= canvasSize.width) {
particle.position.x = canvasSize.width - minPos;
} else if (bounds.left <= 0) {
particle.position.x = minPos;
}
}
}

if (outMode === OutMode.bounce || outMode === OutMode.bounceVertical) {
if (outMode === OutMode.bounce || outMode === OutMode.bounceVertical || outMode === "bounceVertical") {
const velocity = particle.velocity.vertical;

if ((bounds.bottom >= container.canvas.size.height && velocity > 0) || (bounds.top <= 0 && velocity < 0)) {
let bounced = false;

if (
(direction === OutModeDirection.bottom &&
bounds.bottom >= container.canvas.size.height &&
velocity > 0) ||
(direction === OutModeDirection.top && bounds.top <= 0 && velocity < 0)
) {
const factor = particle.particlesOptions.bounce.vertical;
const newVelocity = factor.random.enable
? Utils.randomInRange(factor.value, factor.random.minimumValue)
: factor.value;

particle.velocity.vertical *= -newVelocity;

bounced = true;
}

const minPos = offset.y + size;
if (bounced) {
const minPos = offset.y + size;

if (bounds.bottom >= canvasSize.height) {
particle.position.y = canvasSize.height - minPos;
} else if (bounds.top <= 0) {
particle.position.y = minPos;
if (bounds.bottom >= canvasSize.height) {
particle.position.y = canvasSize.height - minPos;
} else if (bounds.top <= 0) {
particle.position.y = minPos;
}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions core/main/src/Enums/Directions/OutModeDirection.ts
@@ -0,0 +1,6 @@
export enum OutModeDirection {
bottom = "bottom",
left = "left",
right = "right",
top = "top",
}
1 change: 1 addition & 0 deletions core/main/src/Enums/Modes/OutMode.ts
Expand Up @@ -2,6 +2,7 @@ export enum OutMode {
bounce = "bounce",
bounceHorizontal = "bounce-horizontal",
bounceVertical = "bounce-vertical",
none = "none",
out = "out",
destroy = "destroy",
}
Expand Down
32 changes: 28 additions & 4 deletions core/main/src/Options/Classes/Particles/Move/Move.ts
Expand Up @@ -7,6 +7,7 @@ import { Noise } from "../Noise/Noise";
import type { IOptionLoader } from "../../../Interfaces/IOptionLoader";
import { MoveAngle } from "./MoveAngle";
import { MoveGravity } from "./MoveGravity";
import { OutModes } from "./OutModes";

export class Move implements IMove, IOptionLoader<IMove> {
/**
Expand Down Expand Up @@ -56,13 +57,30 @@ export class Move implements IMove, IOptionLoader<IMove> {
this.outMode = value;
}

/**
*
* @deprecated this property is obsolete, please use the new outMode
*/
public get outMode(): OutMode | keyof typeof OutMode | OutModeAlt {
return this.outModes.default;
}

/**
*
* @deprecated this property is obsolete, please use the new outMode
* @param value
*/
public set outMode(value: OutMode | keyof typeof OutMode | OutModeAlt) {
this.outModes.default = value;
}

public angle;
public attract;
public direction: MoveDirection | keyof typeof MoveDirection | MoveDirectionAlt;
public enable;
public gravity;
public noise;
public outMode: OutMode | keyof typeof OutMode | OutModeAlt;
public outModes: OutModes;
public random;
public speed;
public straight;
Expand All @@ -77,7 +95,7 @@ export class Move implements IMove, IOptionLoader<IMove> {
this.enable = false;
this.gravity = new MoveGravity();
this.noise = new Noise();
this.outMode = OutMode.out;
this.outModes = new OutModes();
this.random = false;
this.speed = 2;
this.straight = false;
Expand Down Expand Up @@ -114,8 +132,14 @@ export class Move implements IMove, IOptionLoader<IMove> {

const outMode = data.outMode ?? data.out_mode;

if (outMode !== undefined) {
this.outMode = outMode;
if (data.outModes !== undefined || outMode !== undefined) {
if (typeof data.outModes === "string" || (data.outModes === undefined && outMode !== undefined)) {
this.outModes.load({
default: data.outModes ?? outMode,
});
} else {
this.outModes.load(data.outModes);
}
}

if (data.random !== undefined) {
Expand Down
31 changes: 31 additions & 0 deletions core/main/src/Options/Classes/Particles/Move/OutModes.ts
@@ -0,0 +1,31 @@
import type { IOutModes } from "../../../Interfaces/Particles/Move/IOutModes";
import type { IOptionLoader } from "../../../Interfaces/IOptionLoader";
import { OutMode, OutModeAlt } from "../../../../Enums/Modes";
import type { RecursivePartial } from "../../../../Types";

export class OutModes implements IOutModes, IOptionLoader<IOutModes> {
public bottom?: OutMode | keyof typeof OutMode | OutModeAlt;
public default: OutMode | keyof typeof OutMode | OutModeAlt;
public left?: OutMode | keyof typeof OutMode | OutModeAlt;
public right?: OutMode | keyof typeof OutMode | OutModeAlt;
public top?: OutMode | keyof typeof OutMode | OutModeAlt;

constructor() {
this.default = OutMode.out;
}

public load(data?: RecursivePartial<IOutModes>): void {
if (!data) {
return;
}

if (data.default !== undefined) {
this.default = data.default;
}

this.bottom = data.bottom ?? data.default;
this.left = data.left ?? data.default;
this.right = data.right ?? data.default;
this.top = data.top ?? data.default;
}
}

0 comments on commit 67194dc

Please sign in to comment.