Skip to content

Commit

Permalink
fix: improved trail effect and tilt
Browse files Browse the repository at this point in the history
  • Loading branch information
matteobruni committed Apr 30, 2024
1 parent 5d62b2e commit 2892549
Show file tree
Hide file tree
Showing 16 changed files with 275 additions and 56 deletions.
62 changes: 34 additions & 28 deletions effects/trail/src/TrailDrawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,18 @@ const double = 2,
half = 0.5,
minWidth = -1,
defaultLength = 10,
defaultAlpha = 1;
defaultAlpha = 1,
origin = { x: 0, y: 0 } as ICoordinates;

interface TrailStep {
color: string | CanvasGradient | CanvasPattern;
position: ICoordinates;
transformData?: {
a: number;
b: number;
c: number;
d: number;
};
}

type TrailParticle = Particle & {
Expand All @@ -29,18 +36,27 @@ type TrailParticle = Particle & {
trailLength?: number;
trailMaxWidth?: number;
trailMinWidth?: number;
trailTransform?: boolean;
};

interface ITrailData extends IShapeValues {
fade: boolean;
length: RangeValue;
maxWidth: RangeValue;
minWidth: RangeValue;
transform: boolean;
}

const defaultTransform = {
a: 1,
b: 0,
c: 0,
d: 1,
};

export class TrailDrawer implements IEffectDrawer<TrailParticle> {
draw(data: IShapeDrawData<TrailParticle>): void {
const { context, radius, particle } = data,
const { context, radius, particle, transformData } = data,
diameter = radius * double,
pxRatio = particle.container.retina.pixelRatio,
currentPos = particle.getPosition(),
Expand All @@ -58,6 +74,7 @@ export class TrailDrawer implements IEffectDrawer<TrailParticle> {
x: currentPos.x,
y: currentPos.y,
},
transformData: { ...data.transformData },
});

if (trail.length < minTrailLength) {
Expand All @@ -69,39 +86,29 @@ export class TrailDrawer implements IEffectDrawer<TrailParticle> {
}

const trailLength = Math.min(trail.length, pathLength),
offsetPos = {
x: currentPos.x,
y: currentPos.y,
},
canvasSize = {
width: particle.container.canvas.size.width + diameter,
height: particle.container.canvas.size.height + diameter,
};

let lastPos = trail[trailLength - trailLengthOffset].position;

const defaultTransform = {
a: 1,
b: 0,
c: 0,
d: 1,
};

context.setTransform(
defaultTransform.a,
defaultTransform.b,
defaultTransform.c,
defaultTransform.d,
currentPos.x,
currentPos.y,
);

for (let i = trailLength; i > noItems; i--) {
const step = trail[i - trailLengthOffset],
position = step.position;
position = step.position,
stepTransformData = particle.trailTransform ? step.transformData ?? defaultTransform : defaultTransform;

context.setTransform(
stepTransformData.a,
stepTransformData.b,
stepTransformData.c,
stepTransformData.d,
position.x,
position.y,
);

context.beginPath();
context.moveTo(lastPos.x - offsetPos.x, lastPos.y - offsetPos.y);
context.moveTo(lastPos.x - position.x, lastPos.y - position.y);

const warp = {
x: (lastPos.x + canvasSize.width) % canvasSize.width,
Expand All @@ -118,8 +125,8 @@ export class TrailDrawer implements IEffectDrawer<TrailParticle> {
}

context.lineTo(
(Math.abs(lastPos.x - position.x) > canvasSize.width * half ? warp.x : position.x) - offsetPos.x,
(Math.abs(lastPos.y - position.y) > canvasSize.height * half ? warp.y : position.y) - offsetPos.y,
Math.abs(lastPos.x - position.x) > canvasSize.width * half ? warp.x : origin.x,
Math.abs(lastPos.y - position.y) > canvasSize.height * half ? warp.y : origin.y,
);

const width = Math.max((i / trailLength) * diameter, pxRatio, particle.trailMinWidth ?? minWidth),
Expand All @@ -136,8 +143,6 @@ export class TrailDrawer implements IEffectDrawer<TrailParticle> {
lastPos = position;
}

const { transformData } = data;

context.setTransform(
transformData.a,
transformData.b,
Expand All @@ -161,5 +166,6 @@ export class TrailDrawer implements IEffectDrawer<TrailParticle> {
particle.trailMinWidth = effectData?.minWidth
? getRangeValue(effectData.minWidth) * container.retina.pixelRatio
: undefined;
particle.trailTransform = effectData?.transform ?? false;
}
}
17 changes: 10 additions & 7 deletions engine/src/Core/Container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,9 @@ export class Container {

/**
* Destroys the current container, invalidating it
* @param remove - if true, removes the container from the engine
*/
destroy(): void {
destroy(remove = true): void {
if (!guardCheck(this)) {
return;
}
Expand Down Expand Up @@ -413,14 +414,16 @@ export class Container {

this.destroyed = true;

const mainArr = this._engine.dom(),
idx = mainArr.findIndex(t => t === this),
minIndex = 0;
if (remove) {
const mainArr = this._engine.items,
idx = mainArr.findIndex(t => t === this),
minIndex = 0;

if (idx >= minIndex) {
const deleteCount = 1;
if (idx >= minIndex) {
const deleteCount = 1;

mainArr.splice(idx, deleteCount);
mainArr.splice(idx, deleteCount);
}
}

this._engine.dispatchEvent(EventType.containerDestroyed, { container: this });
Expand Down
12 changes: 6 additions & 6 deletions engine/src/Core/Engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,13 +582,10 @@ export class Engine {
options = url ? await getDataFromUrl({ fallback: params.options, url, index }) : params.options;

/* elements */
const domContainer = getDomContainer(id, params.element),
currentOptions = itemFromSingleOrMultiple(options, index),
const currentOptions = itemFromSingleOrMultiple(options, index),
{ items } = this,
oldIndex = items.findIndex(v => v.id.description === id),
minIndex = 0;

const canvasEl = getCanvasFromContainer(domContainer),
minIndex = 0,
newItem = new Container(this, id, currentOptions);

if (oldIndex >= minIndex) {
Expand All @@ -598,14 +595,17 @@ export class Engine {
deleteCount = old ? one : none;

if (old && !old.destroyed) {
old.destroy();
old.destroy(false);
}

items.splice(oldIndex, deleteCount, newItem);
} else {
items.push(newItem);
}

const domContainer = getDomContainer(id, params.element),
canvasEl = getCanvasFromContainer(domContainer);

newItem.canvas.loadCanvas(canvasEl);

/* launch tsParticles */
Expand Down
3 changes: 3 additions & 0 deletions engine/src/Core/Particle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ export class Particle {

interactivity!: Interactivity;

isRotating!: boolean;

/**
* Last path timestamp
*/
Expand Down Expand Up @@ -471,6 +473,7 @@ export class Particle {
this.lastPathTime = 0;
this.destroyed = false;
this.unbreakable = false;
this.isRotating = false;
this.rotation = 0;
this.misplaced = false;
this.retina = {
Expand Down
19 changes: 13 additions & 6 deletions engine/src/Utils/CanvasUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import type { IHsl } from "../Core/Interfaces/Colors.js";
import type { Particle } from "../Core/Particle.js";
import { getStyleFromRgb } from "./ColorUtils.js";

const origin: ICoordinates = { x: 0, y: 0 };
const origin: ICoordinates = { x: 0, y: 0 },
defaultTransform = {
a: 1,
b: 0,
c: 0,
d: 1,
};

/**
* Draws a line between two points using canvas API in the given context.
Expand Down Expand Up @@ -91,12 +97,13 @@ export function drawParticle(data: IDrawParticleParams): void {
sin: Math.sin(angle),
cos: Math.cos(angle),
},
defaultTransformFactor = 1,
rotating = !!angle,
identity = 1,
transformData = {
a: rotateData.cos * (transform.a ?? defaultTransformFactor),
b: rotateData.sin * (transform.b ?? defaultTransformFactor),
c: -rotateData.sin * (transform.c ?? defaultTransformFactor),
d: rotateData.cos * (transform.d ?? defaultTransformFactor),
a: rotateData.cos * (transform.a ?? defaultTransform.a),
b: rotating ? rotateData.sin * (transform.b ?? identity) : transform.b ?? defaultTransform.b,
c: rotating ? -rotateData.sin * (transform.c ?? identity) : transform.c ?? defaultTransform.c,
d: rotateData.cos * (transform.d ?? defaultTransform.d),
};

context.setTransform(transformData.a, transformData.b, transformData.c, transformData.d, pos.x, pos.y);
Expand Down
4 changes: 3 additions & 1 deletion updaters/rotate/src/RotateUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export class RotateUpdater implements IParticleUpdater {
return false;
}

return !particle.destroyed && !particle.spawning && rotate.animation.enable && !rotate.path;
return !particle.destroyed && !particle.spawning && (!!rotate.value || rotate.animation.enable || rotate.path);
}

loadOptions(
Expand All @@ -121,6 +121,8 @@ export class RotateUpdater implements IParticleUpdater {
return;
}

particle.isRotating = !!particle.rotate;

if (!particle.rotate) {
return;
}
Expand Down
1 change: 0 additions & 1 deletion utils/configs/src/c/clickConfetti.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ const options: ISourceOptions = {
tilt: {
direction: "random",
enable: true,
move: true,
value: {
min: 0,
max: 360,
Expand Down
60 changes: 60 additions & 0 deletions utils/configs/src/e/effectTrailTransform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { ISourceOptions } from "@tsparticles/engine";

const options: ISourceOptions = {
key: "effectTrailTransform",
name: "Effect Trail Transform",
particles: {
number: {
value: 80,
density: {
enable: true,
},
},
color: {
value: "#ff0000",
animation: {
enable: true,
speed: 360,
sync: true,
},
},
effect: {
type: "trail",
options: {
trail: {
fade: true,
length: {
min: 10,
max: 30,
},
transform: true,
},
},
},
shape: {
type: "square",
},
size: {
value: 10,
},
move: {
enable: true,
speed: { min: 6, max: 15 },
},
tilt: {
direction: "random",
enable: true,
value: { min: 0, max: 360 },
animation: {
enable: true,
speed: 60,
sync: true,
},
},
},
background: {
color: "#000000",
},
};

export default options;
2 changes: 2 additions & 0 deletions utils/configs/src/e/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import effectBubble from "./effectBubble.js";
import effectTrail from "./effectTrail.js";
import effectTrailTransform from "./effectTrailTransform.js";
import emitter from "./emitter.js";
import emitterAbsorber from "./emitterAbsorber.js";
import emitterAngled from "./emitterAngled.js";
Expand All @@ -14,6 +15,7 @@ import emitterTextStrokeShape from "./emitterTextStrokeShape.js";
export default {
effectBubble,
effectTrail,
effectTrailTransform,
emitter,
emitterAbsorber,
emitterAngled,
Expand Down
1 change: 1 addition & 0 deletions utils/configs/src/f/fireworks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ const options: ISourceOptions = {
width: 1,
},
rotate: {
enable: true,
path: true,
},
move: {
Expand Down
1 change: 0 additions & 1 deletion utils/configs/src/m/mouseTrailNoise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ const options: ISourceOptions = {
tilt: {
direction: "random",
enable: true,
move: true,
value: {
min: 0,
max: 360,
Expand Down
1 change: 0 additions & 1 deletion utils/configs/src/s/shapeEmoji.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ const emitterRate = {
tilt: {
direction: "random",
enable: true,
move: true,
value: {
min: 0,
max: 360,
Expand Down
4 changes: 0 additions & 4 deletions utils/configs/src/t/tilt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,9 @@ const options: ISourceOptions = {
sync: true,
},
},
rotate: {
value: { min: 0, max: 360 },
},
tilt: {
direction: "random",
enable: true,
move: true,
value: {
min: 0,
max: 360,
Expand Down
Loading

0 comments on commit 2892549

Please sign in to comment.