Skip to content

Commit

Permalink
feat: added background mask image support
Browse files Browse the repository at this point in the history
  • Loading branch information
matteobruni committed Jan 29, 2024
1 parent c736522 commit 0b30b1c
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 23 deletions.
55 changes: 44 additions & 11 deletions engine/src/Core/Canvas.ts
Expand Up @@ -53,6 +53,7 @@ export class Canvas {
private _context: CanvasRenderingContext2D | null;

private _coverColorStyle?: string;
private _coverImage?: { image: HTMLImageElement; opacity: number };
private _generated;
private _mutationObserver?: MutationObserver;
private _originalStyle?: CSSStyleDeclaration;
Expand Down Expand Up @@ -248,7 +249,7 @@ export class Canvas {
});
this.resize();
this._initStyle();
this._initCover();
await this._initCover();

try {
await this._initTrail();
Expand Down Expand Up @@ -380,7 +381,13 @@ export class Canvas {
if (options.backgroundMask.enable && options.backgroundMask.cover) {
clear(ctx, this.size);

this._paintBase(this._coverColorStyle);
if (this._coverImage) {
this._paintImage(this._coverImage.image, this._coverImage.opacity);
} else if (this._coverColorStyle) {
this._paintBase(this._coverColorStyle);
} else {
this._paintBase();
}
} else {
this._paintBase();
}
Expand Down Expand Up @@ -521,19 +528,45 @@ export class Canvas {
return [fColor, sColor];
};

private readonly _initCover: () => void = () => {
private readonly _initCover = async (): Promise<void> => {
const options = this.container.actualOptions,
cover = options.backgroundMask.cover,
color = cover.color,
coverRgb = rangeColorToRgb(color);
color = cover.color;

if (coverRgb) {
const coverColor = {
...coverRgb,
a: cover.opacity,
};
if (color) {
const coverRgb = rangeColorToRgb(color);

if (coverRgb) {
const coverColor = {
...coverRgb,
a: cover.opacity,
};

this._coverColorStyle = getStyleFromRgb(coverColor, coverColor.a);
}
} else {
await new Promise<void>((resolve, reject) => {
if (!cover.image) {
return;
}

const img = document.createElement("img");

this._coverColorStyle = getStyleFromRgb(coverColor, coverColor.a);
img.addEventListener("load", () => {
this._coverImage = {
image: img,
opacity: cover.opacity,
};

resolve();
});

img.addEventListener("error", (evt) => {
reject(evt.error);
});

img.src = cover.image;
});
}
};

Expand Down
Expand Up @@ -45,7 +45,7 @@ export class BackgroundMask implements IBackgroundMask, IOptionLoader<IBackgroun
const cover = data.cover as IBackgroundMaskCover,
color = (isString(data.cover) ? { color: data.cover } : data.cover) as IColor;

this.cover.load(cover.color !== undefined ? cover : { color: color });
this.cover.load(cover.color !== undefined || cover.image !== undefined ? cover : { color: color });
}

if (data.enable !== undefined) {
Expand Down
Expand Up @@ -6,12 +6,11 @@ import type { RecursivePartial } from "../../../Types/RecursivePartial.js";
/**
*/
export class BackgroundMaskCover implements IBackgroundMaskCover, IOptionLoader<IBackgroundMaskCover> {
color;
color?: OptionsColor;
image?: string;
opacity;

constructor() {
this.color = new OptionsColor();
this.color.value = "#fff";
this.opacity = 1;
}

Expand All @@ -24,6 +23,10 @@ export class BackgroundMaskCover implements IBackgroundMaskCover, IOptionLoader<
this.color = OptionsColor.create(this.color, data.color);
}

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

if (data.opacity !== undefined) {
this.opacity = data.opacity;
}
Expand Down
@@ -1,5 +1,6 @@
import type { IBackgroundMaskCover } from "./IBackgroundMaskCover.js";
import type { IColor } from "../../../Core/Interfaces/Colors.js";
import type { RecursivePartial } from "../../../Types/RecursivePartial.js";

/**
* The options to apply a base color to canvas to cover what's behind
Expand All @@ -21,7 +22,7 @@ export interface IBackgroundMask {
*
* The `cover` can also be a {@link IBackgroundMaskCover | cover object} like the one described below.
*/
cover: IBackgroundMaskCover | IColor | string;
cover: RecursivePartial<IBackgroundMaskCover> | IColor | string;

/**
* This property set the background mask mode, this mode enables the `composite` option to all elements drawn.
Expand Down
Expand Up @@ -7,7 +7,12 @@ export interface IBackgroundMaskCover {
/**
* The background color hiding all elements behind, string or {@link IOptionsColor} value.
*/
color: string | IOptionsColor;
color?: string | IOptionsColor;

/**
* The background image hiding all elements behind
*/
image?: string;

/**
* The opacity of the background
Expand Down
80 changes: 80 additions & 0 deletions utils/configs/src/b/backgroundMaskImage.ts
@@ -0,0 +1,80 @@
import type { ISourceOptions } from "@tsparticles/engine";

const options: ISourceOptions = {
key: "backgroundMaskImage",
name: "Background Mask Image",
particles: {
number: {
value: 80,
density: {
enable: true,
},
},
color: {
value: "#ffffff",
},
shape: {
type: "circle",
},
opacity: {
value: 1,
},
size: {
value: {
min: 1,
max: 30,
},
},
links: {
enable: true,
distance: 150,
color: "#ffffff",
opacity: 1,
width: 1,
},
move: {
enable: true,
speed: 2,
direction: "none",
},
},
interactivity: {
events: {
onHover: {
enable: true,
mode: "bubble",
},
onClick: {
enable: true,
mode: "push",
},
},
modes: {
bubble: {
distance: 400,
size: 100,
duration: 2,
opacity: 1,
},
push: {
quantity: 4,
},
},
},
backgroundMask: {
enable: true,
cover: {
image: "https://particles.js.org/images/background2.jpg",
opacity: 1,
},
},
background: {
color: "#ffffff",
image: "url('https://particles.js.org/images/background3.jpg')",
position: "50% 50%",
repeat: "no-repeat",
size: "cover",
},
};

export default options;
2 changes: 2 additions & 0 deletions utils/configs/src/b/index.ts
@@ -1,11 +1,13 @@
import backgroundMask from "./backgroundMask.js";
import backgroundMaskImage from "./backgroundMaskImage.js";
import basic from "./basic.js";
import big from "./big.js";
import blackHole from "./blackHole.js";
import bubble from "./bubble.js";

export default {
backgroundMask,
backgroundMaskImage,
basic,
big,
blackHole,
Expand Down
6 changes: 0 additions & 6 deletions utils/tests/src/Options.ts
Expand Up @@ -42,12 +42,6 @@ describe("Options tests", () => {
expect(options.background.opacity).to.be.equal(1);

/* background mask */
expect(options.backgroundMask.cover)
.to.be.an("object")
.to.have.property("color")
.to.be.an("object")
.to.have.property("value")
.to.equal("#fff");
expect(options.backgroundMask.cover).to.be.an("object").to.have.property("opacity").to.equal(1);
expect(options.backgroundMask.enable).to.be.false;

Expand Down

0 comments on commit 0b30b1c

Please sign in to comment.