@tsparticles/shape-image
Advanced tools
Comparing version 3.0.0-alpha.1 to 3.0.0-beta.0
@@ -1,39 +0,107 @@ | ||
import { downloadSvgImage, loadImage, replaceImageColor } from "./Utils"; | ||
import { errorPrefix } from "@tsparticles/engine"; | ||
import { replaceImageColor } from "./Utils"; | ||
export class ImageDrawer { | ||
constructor() { | ||
this._images = []; | ||
constructor(engine) { | ||
this.loadImageShape = async (imageShape) => { | ||
if (!this._engine.loadImage) { | ||
throw new Error(`${errorPrefix} image shape not initialized`); | ||
} | ||
await this._engine.loadImage({ | ||
gif: imageShape.gif, | ||
name: imageShape.name, | ||
replaceColor: imageShape.replaceColor ?? false, | ||
src: imageShape.src, | ||
}); | ||
}; | ||
this._engine = engine; | ||
} | ||
addImage(container, image) { | ||
const containerImages = this.getImages(container); | ||
containerImages === null || containerImages === void 0 ? void 0 : containerImages.images.push(image); | ||
addImage(image) { | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
this._engine.images.push(image); | ||
} | ||
destroy() { | ||
this._images = []; | ||
} | ||
draw(context, particle, radius, opacity) { | ||
var _a; | ||
const image = particle.image, element = image === null || image === void 0 ? void 0 : image.element; | ||
if (!element) { | ||
draw(context, particle, radius, opacity, delta) { | ||
const image = particle.image, element = image?.element; | ||
if (!image) { | ||
return; | ||
} | ||
const ratio = (_a = image === null || image === void 0 ? void 0 : image.ratio) !== null && _a !== void 0 ? _a : 1, pos = { | ||
x: -radius, | ||
y: -radius, | ||
}; | ||
context.globalAlpha = opacity; | ||
context.drawImage(element, pos.x, pos.y, radius * 2, (radius * 2) / ratio); | ||
context.globalAlpha = 1; | ||
} | ||
getImages(container) { | ||
const containerImages = this._images.find((t) => t.id === container.id); | ||
if (!containerImages) { | ||
this._images.push({ | ||
id: container.id, | ||
images: [], | ||
}); | ||
return this.getImages(container); | ||
if (image.gif && image.gifData) { | ||
const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d"); | ||
if (!offscreenContext) { | ||
throw new Error("could not create offscreen canvas context"); | ||
} | ||
offscreenContext.imageSmoothingQuality = "low"; | ||
offscreenContext.imageSmoothingEnabled = false; | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
if (particle.gifLoopCount === undefined) { | ||
particle.gifLoopCount = image.gifLoopCount ?? 0; | ||
} | ||
let frameIndex = particle.gifFrame ?? 0; | ||
const pos = { x: -image.gifData.width * 0.5, y: -image.gifData.height * 0.5 }, frame = image.gifData.frames[frameIndex]; | ||
if (particle.gifTime === undefined) { | ||
particle.gifTime = 0; | ||
} | ||
if (!frame.bitmap) { | ||
return; | ||
} | ||
context.scale(radius / image.gifData.width, radius / image.gifData.height); | ||
switch (frame.disposalMethod) { | ||
case 4: | ||
case 5: | ||
case 6: | ||
case 7: | ||
case 0: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
break; | ||
case 1: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
break; | ||
case 2: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
if (image.gifData.globalColorTable.length === 0) { | ||
offscreenContext.putImageData(image.gifData.frames[0].image, pos.x + frame.left, pos.y + frame.top); | ||
} | ||
else { | ||
offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y); | ||
} | ||
break; | ||
case 3: | ||
{ | ||
const previousImageData = offscreenContext.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
offscreenContext.putImageData(previousImageData, 0, 0); | ||
} | ||
break; | ||
} | ||
particle.gifTime += delta.value; | ||
if (particle.gifTime > frame.delayTime) { | ||
particle.gifTime -= frame.delayTime; | ||
if (++frameIndex >= image.gifData.frames.length) { | ||
if (--particle.gifLoopCount <= 0) { | ||
return; | ||
} | ||
frameIndex = 0; | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
} | ||
particle.gifFrame = frameIndex; | ||
} | ||
context.scale(image.gifData.width / radius, image.gifData.height / radius); | ||
} | ||
else { | ||
return containerImages; | ||
else if (element) { | ||
const ratio = image.ratio, pos = { | ||
x: -radius, | ||
y: -radius, | ||
}; | ||
context.drawImage(element, pos.x, pos.y, radius * 2, (radius * 2) / ratio); | ||
} | ||
context.globalAlpha = 1; | ||
} | ||
@@ -43,2 +111,11 @@ getSidesCount() { | ||
} | ||
async init(container) { | ||
const options = container.actualOptions; | ||
if (!options.preload || !this._engine.loadImage) { | ||
return; | ||
} | ||
for (const imageData of options.preload) { | ||
await this._engine.loadImage(imageData); | ||
} | ||
} | ||
loadShape(particle) { | ||
@@ -48,5 +125,8 @@ if (particle.shape !== "image" && particle.shape !== "images") { | ||
} | ||
const container = particle.container, images = this.getImages(container).images, imageData = particle.shapeData, image = images.find((t) => t.source === imageData.src); | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
const imageData = particle.shapeData, image = this._engine.images.find((t) => t.name === imageData.name || t.source === imageData.src); | ||
if (!image) { | ||
this.loadImageShape(container, imageData).then(() => { | ||
this.loadImageShape(imageData).then(() => { | ||
this.loadShape(particle); | ||
@@ -57,10 +137,13 @@ }); | ||
particleInit(container, particle) { | ||
var _a; | ||
if (particle.shape !== "image" && particle.shape !== "images") { | ||
return; | ||
} | ||
const images = this.getImages(container).images, imageData = particle.shapeData, color = particle.getFillColor(), replaceColor = (_a = imageData.replaceColor) !== null && _a !== void 0 ? _a : imageData.replace_color, image = images.find((t) => t.source === imageData.src); | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
const images = this._engine.images, imageData = particle.shapeData, color = particle.getFillColor(), image = images.find((t) => t.name === imageData.name || t.source === imageData.src); | ||
if (!image) { | ||
return; | ||
} | ||
const replaceColor = imageData.replaceColor ?? image.replaceColor; | ||
if (image.loading) { | ||
@@ -73,3 +156,2 @@ setTimeout(() => { | ||
(async () => { | ||
var _a, _b; | ||
let imageRes; | ||
@@ -84,4 +166,7 @@ if (image.svgData && color) { | ||
element: image.element, | ||
gif: image.gif, | ||
gifData: image.gifData, | ||
gifLoopCount: image.gifLoopCount, | ||
loaded: true, | ||
ratio: imageData.width / imageData.height, | ||
ratio: imageData.width && imageData.height ? imageData.width / imageData.height : image.ratio ?? 1, | ||
replaceColor: replaceColor, | ||
@@ -94,3 +179,3 @@ source: imageData.src, | ||
} | ||
const fill = (_a = imageData.fill) !== null && _a !== void 0 ? _a : particle.fill, close = (_b = imageData.close) !== null && _b !== void 0 ? _b : particle.close, imageShape = { | ||
const fill = imageData.fill ?? particle.fill, close = imageData.close ?? particle.close, imageShape = { | ||
image: imageRes, | ||
@@ -105,23 +190,2 @@ fill, | ||
} | ||
async loadImageShape(container, imageShape) { | ||
var _a; | ||
const source = imageShape.src; | ||
if (!source) { | ||
throw new Error("Error tsParticles - No image.src"); | ||
} | ||
try { | ||
const image = { | ||
source: source, | ||
type: source.substring(source.length - 3), | ||
error: false, | ||
loading: true, | ||
}; | ||
this.addImage(container, image); | ||
const imageFunc = ((_a = imageShape.replaceColor) !== null && _a !== void 0 ? _a : imageShape.replace_color) ? downloadSvgImage : loadImage; | ||
await imageFunc(image); | ||
} | ||
catch (_b) { | ||
throw new Error(`tsParticles error - ${imageShape.src} not found`); | ||
} | ||
} | ||
} |
@@ -0,4 +1,44 @@ | ||
import { downloadSvgImage, loadGifImage, loadImage } from "./Utils"; | ||
import { ImageDrawer } from "./ImageDrawer"; | ||
export async function loadImageShape(engine) { | ||
await engine.addShape(["image", "images"], new ImageDrawer()); | ||
import { ImagePreloaderPlugin } from "./ImagePreloader"; | ||
import { errorPrefix } from "@tsparticles/engine"; | ||
function addLoadImageToEngine(engine) { | ||
if (engine.loadImage) { | ||
return; | ||
} | ||
engine.loadImage = async (data) => { | ||
if (!data.name && !data.src) { | ||
throw new Error(`${errorPrefix} no image source provided`); | ||
} | ||
if (!engine.images) { | ||
engine.images = []; | ||
} | ||
if (engine.images.find((t) => t.name === data.name || t.source === data.src)) { | ||
return; | ||
} | ||
try { | ||
const image = { | ||
gif: data.gif ?? false, | ||
name: data.name ?? data.src, | ||
source: data.src, | ||
type: data.src.substring(data.src.length - 3), | ||
error: false, | ||
loading: true, | ||
replaceColor: data.replaceColor, | ||
ratio: data.width && data.height ? data.width / data.height : undefined, | ||
}; | ||
engine.images.push(image); | ||
const imageFunc = data.gif ? loadGifImage : data.replaceColor ? downloadSvgImage : loadImage; | ||
await imageFunc(image); | ||
} | ||
catch { | ||
throw new Error(`${errorPrefix} ${data.name ?? data.src} not found`); | ||
} | ||
}; | ||
} | ||
export async function loadImageShape(engine, refresh = true) { | ||
addLoadImageToEngine(engine); | ||
const preloader = new ImagePreloaderPlugin(engine); | ||
await engine.addPlugin(preloader, refresh); | ||
await engine.addShape(["image", "images"], new ImageDrawer(engine), refresh); | ||
} |
@@ -1,2 +0,3 @@ | ||
import { getStyleFromHsl } from "@tsparticles/engine"; | ||
import { errorPrefix, getLogger, getStyleFromHsl } from "@tsparticles/engine"; | ||
import { decodeGIF, getGIFLoopAmount } from "./GifUtils/Utils"; | ||
const currentColorRegex = /(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi; | ||
@@ -28,3 +29,3 @@ function replaceColorSvg(imageShape, color, opacity) { | ||
image.loading = false; | ||
console.error(`Error tsParticles - loading image: ${image.source}`); | ||
getLogger().error(`${errorPrefix} loading image: ${image.source}`); | ||
resolve(); | ||
@@ -35,2 +36,20 @@ }); | ||
} | ||
export async function loadGifImage(image) { | ||
if (image.type !== "gif") { | ||
await loadImage(image); | ||
return; | ||
} | ||
image.loading = true; | ||
try { | ||
image.gifData = await decodeGIF(image.source); | ||
image.gifLoopCount = getGIFLoopAmount(image.gifData) ?? 0; | ||
if (image.gifLoopCount === 0) { | ||
image.gifLoopCount = Infinity; | ||
} | ||
} | ||
catch { | ||
image.error = true; | ||
} | ||
image.loading = false; | ||
} | ||
export async function downloadSvgImage(image) { | ||
@@ -44,6 +63,6 @@ if (image.type !== "svg") { | ||
if (!response.ok) { | ||
console.error("Error tsParticles - Image not found"); | ||
getLogger().error(`${errorPrefix} Image not found`); | ||
image.error = true; | ||
} | ||
if (!image.error) { | ||
else { | ||
image.svgData = await response.text(); | ||
@@ -54,9 +73,12 @@ } | ||
export function replaceImageColor(image, imageData, color, particle) { | ||
var _a, _b, _c; | ||
const svgColoredData = replaceColorSvg(image, color, (_b = (_a = particle.opacity) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 1), imageRes = { | ||
const svgColoredData = replaceColorSvg(image, color, particle.opacity?.value ?? 1), imageRes = { | ||
color, | ||
data: Object.assign(Object.assign({}, image), { svgData: svgColoredData }), | ||
gif: imageData.gif, | ||
data: { | ||
...image, | ||
svgData: svgColoredData, | ||
}, | ||
loaded: false, | ||
ratio: imageData.width / imageData.height, | ||
replaceColor: (_c = imageData.replaceColor) !== null && _c !== void 0 ? _c : imageData.replace_color, | ||
replaceColor: imageData.replaceColor, | ||
source: imageData.src, | ||
@@ -74,3 +96,7 @@ }; | ||
domUrl.revokeObjectURL(url); | ||
const img2 = Object.assign(Object.assign({}, image), { error: false, loading: true }); | ||
const img2 = { | ||
...image, | ||
error: false, | ||
loading: true, | ||
}; | ||
await loadImage(img2); | ||
@@ -77,0 +103,0 @@ imageRes.loaded = true; |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ImageDrawer = void 0; | ||
const engine_1 = require("@tsparticles/engine"); | ||
const Utils_1 = require("./Utils"); | ||
class ImageDrawer { | ||
constructor() { | ||
this._images = []; | ||
constructor(engine) { | ||
this.loadImageShape = async (imageShape) => { | ||
if (!this._engine.loadImage) { | ||
throw new Error(`${engine_1.errorPrefix} image shape not initialized`); | ||
} | ||
await this._engine.loadImage({ | ||
gif: imageShape.gif, | ||
name: imageShape.name, | ||
replaceColor: imageShape.replaceColor ?? false, | ||
src: imageShape.src, | ||
}); | ||
}; | ||
this._engine = engine; | ||
} | ||
addImage(container, image) { | ||
const containerImages = this.getImages(container); | ||
containerImages === null || containerImages === void 0 ? void 0 : containerImages.images.push(image); | ||
addImage(image) { | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
this._engine.images.push(image); | ||
} | ||
destroy() { | ||
this._images = []; | ||
} | ||
draw(context, particle, radius, opacity) { | ||
var _a; | ||
const image = particle.image, element = image === null || image === void 0 ? void 0 : image.element; | ||
if (!element) { | ||
draw(context, particle, radius, opacity, delta) { | ||
const image = particle.image, element = image?.element; | ||
if (!image) { | ||
return; | ||
} | ||
const ratio = (_a = image === null || image === void 0 ? void 0 : image.ratio) !== null && _a !== void 0 ? _a : 1, pos = { | ||
x: -radius, | ||
y: -radius, | ||
}; | ||
context.globalAlpha = opacity; | ||
context.drawImage(element, pos.x, pos.y, radius * 2, (radius * 2) / ratio); | ||
context.globalAlpha = 1; | ||
} | ||
getImages(container) { | ||
const containerImages = this._images.find((t) => t.id === container.id); | ||
if (!containerImages) { | ||
this._images.push({ | ||
id: container.id, | ||
images: [], | ||
}); | ||
return this.getImages(container); | ||
if (image.gif && image.gifData) { | ||
const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d"); | ||
if (!offscreenContext) { | ||
throw new Error("could not create offscreen canvas context"); | ||
} | ||
offscreenContext.imageSmoothingQuality = "low"; | ||
offscreenContext.imageSmoothingEnabled = false; | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
if (particle.gifLoopCount === undefined) { | ||
particle.gifLoopCount = image.gifLoopCount ?? 0; | ||
} | ||
let frameIndex = particle.gifFrame ?? 0; | ||
const pos = { x: -image.gifData.width * 0.5, y: -image.gifData.height * 0.5 }, frame = image.gifData.frames[frameIndex]; | ||
if (particle.gifTime === undefined) { | ||
particle.gifTime = 0; | ||
} | ||
if (!frame.bitmap) { | ||
return; | ||
} | ||
context.scale(radius / image.gifData.width, radius / image.gifData.height); | ||
switch (frame.disposalMethod) { | ||
case 4: | ||
case 5: | ||
case 6: | ||
case 7: | ||
case 0: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
break; | ||
case 1: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
break; | ||
case 2: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
if (image.gifData.globalColorTable.length === 0) { | ||
offscreenContext.putImageData(image.gifData.frames[0].image, pos.x + frame.left, pos.y + frame.top); | ||
} | ||
else { | ||
offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y); | ||
} | ||
break; | ||
case 3: | ||
{ | ||
const previousImageData = offscreenContext.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
offscreenContext.putImageData(previousImageData, 0, 0); | ||
} | ||
break; | ||
} | ||
particle.gifTime += delta.value; | ||
if (particle.gifTime > frame.delayTime) { | ||
particle.gifTime -= frame.delayTime; | ||
if (++frameIndex >= image.gifData.frames.length) { | ||
if (--particle.gifLoopCount <= 0) { | ||
return; | ||
} | ||
frameIndex = 0; | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
} | ||
particle.gifFrame = frameIndex; | ||
} | ||
context.scale(image.gifData.width / radius, image.gifData.height / radius); | ||
} | ||
else { | ||
return containerImages; | ||
else if (element) { | ||
const ratio = image.ratio, pos = { | ||
x: -radius, | ||
y: -radius, | ||
}; | ||
context.drawImage(element, pos.x, pos.y, radius * 2, (radius * 2) / ratio); | ||
} | ||
context.globalAlpha = 1; | ||
} | ||
@@ -55,2 +114,11 @@ getSidesCount() { | ||
} | ||
async init(container) { | ||
const options = container.actualOptions; | ||
if (!options.preload || !this._engine.loadImage) { | ||
return; | ||
} | ||
for (const imageData of options.preload) { | ||
await this._engine.loadImage(imageData); | ||
} | ||
} | ||
loadShape(particle) { | ||
@@ -60,5 +128,8 @@ if (particle.shape !== "image" && particle.shape !== "images") { | ||
} | ||
const container = particle.container, images = this.getImages(container).images, imageData = particle.shapeData, image = images.find((t) => t.source === imageData.src); | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
const imageData = particle.shapeData, image = this._engine.images.find((t) => t.name === imageData.name || t.source === imageData.src); | ||
if (!image) { | ||
this.loadImageShape(container, imageData).then(() => { | ||
this.loadImageShape(imageData).then(() => { | ||
this.loadShape(particle); | ||
@@ -69,10 +140,13 @@ }); | ||
particleInit(container, particle) { | ||
var _a; | ||
if (particle.shape !== "image" && particle.shape !== "images") { | ||
return; | ||
} | ||
const images = this.getImages(container).images, imageData = particle.shapeData, color = particle.getFillColor(), replaceColor = (_a = imageData.replaceColor) !== null && _a !== void 0 ? _a : imageData.replace_color, image = images.find((t) => t.source === imageData.src); | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
const images = this._engine.images, imageData = particle.shapeData, color = particle.getFillColor(), image = images.find((t) => t.name === imageData.name || t.source === imageData.src); | ||
if (!image) { | ||
return; | ||
} | ||
const replaceColor = imageData.replaceColor ?? image.replaceColor; | ||
if (image.loading) { | ||
@@ -84,7 +158,6 @@ setTimeout(() => { | ||
} | ||
(() => __awaiter(this, void 0, void 0, function* () { | ||
var _b, _c; | ||
(async () => { | ||
let imageRes; | ||
if (image.svgData && color) { | ||
imageRes = yield (0, Utils_1.replaceImageColor)(image, imageData, color, particle); | ||
imageRes = await (0, Utils_1.replaceImageColor)(image, imageData, color, particle); | ||
} | ||
@@ -96,4 +169,7 @@ else { | ||
element: image.element, | ||
gif: image.gif, | ||
gifData: image.gifData, | ||
gifLoopCount: image.gifLoopCount, | ||
loaded: true, | ||
ratio: imageData.width / imageData.height, | ||
ratio: imageData.width && imageData.height ? imageData.width / imageData.height : image.ratio ?? 1, | ||
replaceColor: replaceColor, | ||
@@ -106,3 +182,3 @@ source: imageData.src, | ||
} | ||
const fill = (_b = imageData.fill) !== null && _b !== void 0 ? _b : particle.fill, close = (_c = imageData.close) !== null && _c !== void 0 ? _c : particle.close, imageShape = { | ||
const fill = imageData.fill ?? particle.fill, close = imageData.close ?? particle.close, imageShape = { | ||
image: imageRes, | ||
@@ -115,28 +191,5 @@ fill, | ||
particle.close = imageShape.close; | ||
}))(); | ||
})(); | ||
} | ||
loadImageShape(container, imageShape) { | ||
var _a; | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const source = imageShape.src; | ||
if (!source) { | ||
throw new Error("Error tsParticles - No image.src"); | ||
} | ||
try { | ||
const image = { | ||
source: source, | ||
type: source.substring(source.length - 3), | ||
error: false, | ||
loading: true, | ||
}; | ||
this.addImage(container, image); | ||
const imageFunc = ((_a = imageShape.replaceColor) !== null && _a !== void 0 ? _a : imageShape.replace_color) ? Utils_1.downloadSvgImage : Utils_1.loadImage; | ||
yield imageFunc(image); | ||
} | ||
catch (_b) { | ||
throw new Error(`tsParticles error - ${imageShape.src} not found`); | ||
} | ||
}); | ||
} | ||
} | ||
exports.ImageDrawer = ImageDrawer; |
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.loadImageShape = void 0; | ||
const Utils_1 = require("./Utils"); | ||
const ImageDrawer_1 = require("./ImageDrawer"); | ||
function loadImageShape(engine) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
yield engine.addShape(["image", "images"], new ImageDrawer_1.ImageDrawer()); | ||
}); | ||
const ImagePreloader_1 = require("./ImagePreloader"); | ||
const engine_1 = require("@tsparticles/engine"); | ||
function addLoadImageToEngine(engine) { | ||
if (engine.loadImage) { | ||
return; | ||
} | ||
engine.loadImage = async (data) => { | ||
if (!data.name && !data.src) { | ||
throw new Error(`${engine_1.errorPrefix} no image source provided`); | ||
} | ||
if (!engine.images) { | ||
engine.images = []; | ||
} | ||
if (engine.images.find((t) => t.name === data.name || t.source === data.src)) { | ||
return; | ||
} | ||
try { | ||
const image = { | ||
gif: data.gif ?? false, | ||
name: data.name ?? data.src, | ||
source: data.src, | ||
type: data.src.substring(data.src.length - 3), | ||
error: false, | ||
loading: true, | ||
replaceColor: data.replaceColor, | ||
ratio: data.width && data.height ? data.width / data.height : undefined, | ||
}; | ||
engine.images.push(image); | ||
const imageFunc = data.gif ? Utils_1.loadGifImage : data.replaceColor ? Utils_1.downloadSvgImage : Utils_1.loadImage; | ||
await imageFunc(image); | ||
} | ||
catch { | ||
throw new Error(`${engine_1.errorPrefix} ${data.name ?? data.src} not found`); | ||
} | ||
}; | ||
} | ||
async function loadImageShape(engine, refresh = true) { | ||
addLoadImageToEngine(engine); | ||
const preloader = new ImagePreloader_1.ImagePreloaderPlugin(engine); | ||
await engine.addPlugin(preloader, refresh); | ||
await engine.addShape(["image", "images"], new ImageDrawer_1.ImageDrawer(engine), refresh); | ||
} | ||
exports.loadImageShape = loadImageShape; |
118
cjs/Utils.js
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.replaceImageColor = exports.downloadSvgImage = exports.loadImage = void 0; | ||
exports.replaceImageColor = exports.downloadSvgImage = exports.loadGifImage = exports.loadImage = void 0; | ||
const engine_1 = require("@tsparticles/engine"); | ||
const Utils_1 = require("./GifUtils/Utils"); | ||
const currentColorRegex = /(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi; | ||
@@ -27,51 +19,69 @@ function replaceColorSvg(imageShape, color, opacity) { | ||
} | ||
function loadImage(image) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
return new Promise((resolve) => { | ||
image.loading = true; | ||
const img = new Image(); | ||
image.element = img; | ||
img.addEventListener("load", () => { | ||
image.loading = false; | ||
resolve(); | ||
}); | ||
img.addEventListener("error", () => { | ||
image.element = undefined; | ||
image.error = true; | ||
image.loading = false; | ||
console.error(`Error tsParticles - loading image: ${image.source}`); | ||
resolve(); | ||
}); | ||
img.src = image.source; | ||
async function loadImage(image) { | ||
return new Promise((resolve) => { | ||
image.loading = true; | ||
const img = new Image(); | ||
image.element = img; | ||
img.addEventListener("load", () => { | ||
image.loading = false; | ||
resolve(); | ||
}); | ||
img.addEventListener("error", () => { | ||
image.element = undefined; | ||
image.error = true; | ||
image.loading = false; | ||
(0, engine_1.getLogger)().error(`${engine_1.errorPrefix} loading image: ${image.source}`); | ||
resolve(); | ||
}); | ||
img.src = image.source; | ||
}); | ||
} | ||
exports.loadImage = loadImage; | ||
function downloadSvgImage(image) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (image.type !== "svg") { | ||
yield loadImage(image); | ||
return; | ||
async function loadGifImage(image) { | ||
if (image.type !== "gif") { | ||
await loadImage(image); | ||
return; | ||
} | ||
image.loading = true; | ||
try { | ||
image.gifData = await (0, Utils_1.decodeGIF)(image.source); | ||
image.gifLoopCount = (0, Utils_1.getGIFLoopAmount)(image.gifData) ?? 0; | ||
if (image.gifLoopCount === 0) { | ||
image.gifLoopCount = Infinity; | ||
} | ||
image.loading = true; | ||
const response = yield fetch(image.source); | ||
if (!response.ok) { | ||
console.error("Error tsParticles - Image not found"); | ||
image.error = true; | ||
} | ||
if (!image.error) { | ||
image.svgData = yield response.text(); | ||
} | ||
image.loading = false; | ||
}); | ||
} | ||
catch { | ||
image.error = true; | ||
} | ||
image.loading = false; | ||
} | ||
exports.loadGifImage = loadGifImage; | ||
async function downloadSvgImage(image) { | ||
if (image.type !== "svg") { | ||
await loadImage(image); | ||
return; | ||
} | ||
image.loading = true; | ||
const response = await fetch(image.source); | ||
if (!response.ok) { | ||
(0, engine_1.getLogger)().error(`${engine_1.errorPrefix} Image not found`); | ||
image.error = true; | ||
} | ||
else { | ||
image.svgData = await response.text(); | ||
} | ||
image.loading = false; | ||
} | ||
exports.downloadSvgImage = downloadSvgImage; | ||
function replaceImageColor(image, imageData, color, particle) { | ||
var _a, _b, _c; | ||
const svgColoredData = replaceColorSvg(image, color, (_b = (_a = particle.opacity) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 1), imageRes = { | ||
const svgColoredData = replaceColorSvg(image, color, particle.opacity?.value ?? 1), imageRes = { | ||
color, | ||
data: Object.assign(Object.assign({}, image), { svgData: svgColoredData }), | ||
gif: imageData.gif, | ||
data: { | ||
...image, | ||
svgData: svgColoredData, | ||
}, | ||
loaded: false, | ||
ratio: imageData.width / imageData.height, | ||
replaceColor: (_c = imageData.replaceColor) !== null && _c !== void 0 ? _c : imageData.replace_color, | ||
replaceColor: imageData.replaceColor, | ||
source: imageData.src, | ||
@@ -87,10 +97,14 @@ }; | ||
}); | ||
img.addEventListener("error", () => __awaiter(this, void 0, void 0, function* () { | ||
img.addEventListener("error", async () => { | ||
domUrl.revokeObjectURL(url); | ||
const img2 = Object.assign(Object.assign({}, image), { error: false, loading: true }); | ||
yield loadImage(img2); | ||
const img2 = { | ||
...image, | ||
error: false, | ||
loading: true, | ||
}; | ||
await loadImage(img2); | ||
imageRes.loaded = true; | ||
imageRes.element = img2.element; | ||
resolve(imageRes); | ||
})); | ||
}); | ||
img.src = url; | ||
@@ -97,0 +111,0 @@ }); |
@@ -1,39 +0,107 @@ | ||
import { downloadSvgImage, loadImage, replaceImageColor } from "./Utils"; | ||
import { errorPrefix } from "@tsparticles/engine"; | ||
import { replaceImageColor } from "./Utils"; | ||
export class ImageDrawer { | ||
constructor() { | ||
this._images = []; | ||
constructor(engine) { | ||
this.loadImageShape = async (imageShape) => { | ||
if (!this._engine.loadImage) { | ||
throw new Error(`${errorPrefix} image shape not initialized`); | ||
} | ||
await this._engine.loadImage({ | ||
gif: imageShape.gif, | ||
name: imageShape.name, | ||
replaceColor: imageShape.replaceColor ?? false, | ||
src: imageShape.src, | ||
}); | ||
}; | ||
this._engine = engine; | ||
} | ||
addImage(container, image) { | ||
const containerImages = this.getImages(container); | ||
containerImages === null || containerImages === void 0 ? void 0 : containerImages.images.push(image); | ||
addImage(image) { | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
this._engine.images.push(image); | ||
} | ||
destroy() { | ||
this._images = []; | ||
} | ||
draw(context, particle, radius, opacity) { | ||
var _a; | ||
const image = particle.image, element = image === null || image === void 0 ? void 0 : image.element; | ||
if (!element) { | ||
draw(context, particle, radius, opacity, delta) { | ||
const image = particle.image, element = image?.element; | ||
if (!image) { | ||
return; | ||
} | ||
const ratio = (_a = image === null || image === void 0 ? void 0 : image.ratio) !== null && _a !== void 0 ? _a : 1, pos = { | ||
x: -radius, | ||
y: -radius, | ||
}; | ||
context.globalAlpha = opacity; | ||
context.drawImage(element, pos.x, pos.y, radius * 2, (radius * 2) / ratio); | ||
context.globalAlpha = 1; | ||
} | ||
getImages(container) { | ||
const containerImages = this._images.find((t) => t.id === container.id); | ||
if (!containerImages) { | ||
this._images.push({ | ||
id: container.id, | ||
images: [], | ||
}); | ||
return this.getImages(container); | ||
if (image.gif && image.gifData) { | ||
const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d"); | ||
if (!offscreenContext) { | ||
throw new Error("could not create offscreen canvas context"); | ||
} | ||
offscreenContext.imageSmoothingQuality = "low"; | ||
offscreenContext.imageSmoothingEnabled = false; | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
if (particle.gifLoopCount === undefined) { | ||
particle.gifLoopCount = image.gifLoopCount ?? 0; | ||
} | ||
let frameIndex = particle.gifFrame ?? 0; | ||
const pos = { x: -image.gifData.width * 0.5, y: -image.gifData.height * 0.5 }, frame = image.gifData.frames[frameIndex]; | ||
if (particle.gifTime === undefined) { | ||
particle.gifTime = 0; | ||
} | ||
if (!frame.bitmap) { | ||
return; | ||
} | ||
context.scale(radius / image.gifData.width, radius / image.gifData.height); | ||
switch (frame.disposalMethod) { | ||
case 4: | ||
case 5: | ||
case 6: | ||
case 7: | ||
case 0: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
break; | ||
case 1: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
break; | ||
case 2: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
if (image.gifData.globalColorTable.length === 0) { | ||
offscreenContext.putImageData(image.gifData.frames[0].image, pos.x + frame.left, pos.y + frame.top); | ||
} | ||
else { | ||
offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y); | ||
} | ||
break; | ||
case 3: | ||
{ | ||
const previousImageData = offscreenContext.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
offscreenContext.putImageData(previousImageData, 0, 0); | ||
} | ||
break; | ||
} | ||
particle.gifTime += delta.value; | ||
if (particle.gifTime > frame.delayTime) { | ||
particle.gifTime -= frame.delayTime; | ||
if (++frameIndex >= image.gifData.frames.length) { | ||
if (--particle.gifLoopCount <= 0) { | ||
return; | ||
} | ||
frameIndex = 0; | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
} | ||
particle.gifFrame = frameIndex; | ||
} | ||
context.scale(image.gifData.width / radius, image.gifData.height / radius); | ||
} | ||
else { | ||
return containerImages; | ||
else if (element) { | ||
const ratio = image.ratio, pos = { | ||
x: -radius, | ||
y: -radius, | ||
}; | ||
context.drawImage(element, pos.x, pos.y, radius * 2, (radius * 2) / ratio); | ||
} | ||
context.globalAlpha = 1; | ||
} | ||
@@ -43,2 +111,11 @@ getSidesCount() { | ||
} | ||
async init(container) { | ||
const options = container.actualOptions; | ||
if (!options.preload || !this._engine.loadImage) { | ||
return; | ||
} | ||
for (const imageData of options.preload) { | ||
await this._engine.loadImage(imageData); | ||
} | ||
} | ||
loadShape(particle) { | ||
@@ -48,5 +125,8 @@ if (particle.shape !== "image" && particle.shape !== "images") { | ||
} | ||
const container = particle.container, images = this.getImages(container).images, imageData = particle.shapeData, image = images.find((t) => t.source === imageData.src); | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
const imageData = particle.shapeData, image = this._engine.images.find((t) => t.name === imageData.name || t.source === imageData.src); | ||
if (!image) { | ||
this.loadImageShape(container, imageData).then(() => { | ||
this.loadImageShape(imageData).then(() => { | ||
this.loadShape(particle); | ||
@@ -57,10 +137,13 @@ }); | ||
particleInit(container, particle) { | ||
var _a; | ||
if (particle.shape !== "image" && particle.shape !== "images") { | ||
return; | ||
} | ||
const images = this.getImages(container).images, imageData = particle.shapeData, color = particle.getFillColor(), replaceColor = (_a = imageData.replaceColor) !== null && _a !== void 0 ? _a : imageData.replace_color, image = images.find((t) => t.source === imageData.src); | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
const images = this._engine.images, imageData = particle.shapeData, color = particle.getFillColor(), image = images.find((t) => t.name === imageData.name || t.source === imageData.src); | ||
if (!image) { | ||
return; | ||
} | ||
const replaceColor = imageData.replaceColor ?? image.replaceColor; | ||
if (image.loading) { | ||
@@ -73,3 +156,2 @@ setTimeout(() => { | ||
(async () => { | ||
var _a, _b; | ||
let imageRes; | ||
@@ -84,4 +166,7 @@ if (image.svgData && color) { | ||
element: image.element, | ||
gif: image.gif, | ||
gifData: image.gifData, | ||
gifLoopCount: image.gifLoopCount, | ||
loaded: true, | ||
ratio: imageData.width / imageData.height, | ||
ratio: imageData.width && imageData.height ? imageData.width / imageData.height : image.ratio ?? 1, | ||
replaceColor: replaceColor, | ||
@@ -94,3 +179,3 @@ source: imageData.src, | ||
} | ||
const fill = (_a = imageData.fill) !== null && _a !== void 0 ? _a : particle.fill, close = (_b = imageData.close) !== null && _b !== void 0 ? _b : particle.close, imageShape = { | ||
const fill = imageData.fill ?? particle.fill, close = imageData.close ?? particle.close, imageShape = { | ||
image: imageRes, | ||
@@ -105,23 +190,2 @@ fill, | ||
} | ||
async loadImageShape(container, imageShape) { | ||
var _a; | ||
const source = imageShape.src; | ||
if (!source) { | ||
throw new Error("Error tsParticles - No image.src"); | ||
} | ||
try { | ||
const image = { | ||
source: source, | ||
type: source.substring(source.length - 3), | ||
error: false, | ||
loading: true, | ||
}; | ||
this.addImage(container, image); | ||
const imageFunc = ((_a = imageShape.replaceColor) !== null && _a !== void 0 ? _a : imageShape.replace_color) ? downloadSvgImage : loadImage; | ||
await imageFunc(image); | ||
} | ||
catch (_b) { | ||
throw new Error(`tsParticles error - ${imageShape.src} not found`); | ||
} | ||
} | ||
} |
@@ -0,4 +1,44 @@ | ||
import { downloadSvgImage, loadGifImage, loadImage } from "./Utils"; | ||
import { ImageDrawer } from "./ImageDrawer"; | ||
export async function loadImageShape(engine) { | ||
await engine.addShape(["image", "images"], new ImageDrawer()); | ||
import { ImagePreloaderPlugin } from "./ImagePreloader"; | ||
import { errorPrefix } from "@tsparticles/engine"; | ||
function addLoadImageToEngine(engine) { | ||
if (engine.loadImage) { | ||
return; | ||
} | ||
engine.loadImage = async (data) => { | ||
if (!data.name && !data.src) { | ||
throw new Error(`${errorPrefix} no image source provided`); | ||
} | ||
if (!engine.images) { | ||
engine.images = []; | ||
} | ||
if (engine.images.find((t) => t.name === data.name || t.source === data.src)) { | ||
return; | ||
} | ||
try { | ||
const image = { | ||
gif: data.gif ?? false, | ||
name: data.name ?? data.src, | ||
source: data.src, | ||
type: data.src.substring(data.src.length - 3), | ||
error: false, | ||
loading: true, | ||
replaceColor: data.replaceColor, | ||
ratio: data.width && data.height ? data.width / data.height : undefined, | ||
}; | ||
engine.images.push(image); | ||
const imageFunc = data.gif ? loadGifImage : data.replaceColor ? downloadSvgImage : loadImage; | ||
await imageFunc(image); | ||
} | ||
catch { | ||
throw new Error(`${errorPrefix} ${data.name ?? data.src} not found`); | ||
} | ||
}; | ||
} | ||
export async function loadImageShape(engine, refresh = true) { | ||
addLoadImageToEngine(engine); | ||
const preloader = new ImagePreloaderPlugin(engine); | ||
await engine.addPlugin(preloader, refresh); | ||
await engine.addShape(["image", "images"], new ImageDrawer(engine), refresh); | ||
} |
@@ -1,2 +0,3 @@ | ||
import { getStyleFromHsl } from "@tsparticles/engine"; | ||
import { errorPrefix, getLogger, getStyleFromHsl } from "@tsparticles/engine"; | ||
import { decodeGIF, getGIFLoopAmount } from "./GifUtils/Utils"; | ||
const currentColorRegex = /(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi; | ||
@@ -28,3 +29,3 @@ function replaceColorSvg(imageShape, color, opacity) { | ||
image.loading = false; | ||
console.error(`Error tsParticles - loading image: ${image.source}`); | ||
getLogger().error(`${errorPrefix} loading image: ${image.source}`); | ||
resolve(); | ||
@@ -35,2 +36,20 @@ }); | ||
} | ||
export async function loadGifImage(image) { | ||
if (image.type !== "gif") { | ||
await loadImage(image); | ||
return; | ||
} | ||
image.loading = true; | ||
try { | ||
image.gifData = await decodeGIF(image.source); | ||
image.gifLoopCount = getGIFLoopAmount(image.gifData) ?? 0; | ||
if (image.gifLoopCount === 0) { | ||
image.gifLoopCount = Infinity; | ||
} | ||
} | ||
catch { | ||
image.error = true; | ||
} | ||
image.loading = false; | ||
} | ||
export async function downloadSvgImage(image) { | ||
@@ -44,6 +63,6 @@ if (image.type !== "svg") { | ||
if (!response.ok) { | ||
console.error("Error tsParticles - Image not found"); | ||
getLogger().error(`${errorPrefix} Image not found`); | ||
image.error = true; | ||
} | ||
if (!image.error) { | ||
else { | ||
image.svgData = await response.text(); | ||
@@ -54,9 +73,12 @@ } | ||
export function replaceImageColor(image, imageData, color, particle) { | ||
var _a, _b, _c; | ||
const svgColoredData = replaceColorSvg(image, color, (_b = (_a = particle.opacity) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 1), imageRes = { | ||
const svgColoredData = replaceColorSvg(image, color, particle.opacity?.value ?? 1), imageRes = { | ||
color, | ||
data: Object.assign(Object.assign({}, image), { svgData: svgColoredData }), | ||
gif: imageData.gif, | ||
data: { | ||
...image, | ||
svgData: svgColoredData, | ||
}, | ||
loaded: false, | ||
ratio: imageData.width / imageData.height, | ||
replaceColor: (_c = imageData.replaceColor) !== null && _c !== void 0 ? _c : imageData.replace_color, | ||
replaceColor: imageData.replaceColor, | ||
source: imageData.src, | ||
@@ -74,3 +96,7 @@ }; | ||
domUrl.revokeObjectURL(url); | ||
const img2 = Object.assign(Object.assign({}, image), { error: false, loading: true }); | ||
const img2 = { | ||
...image, | ||
error: false, | ||
loading: true, | ||
}; | ||
await loadImage(img2); | ||
@@ -77,0 +103,0 @@ imageRes.loaded = true; |
{ | ||
"name": "@tsparticles/shape-image", | ||
"version": "3.0.0-alpha.1", | ||
"version": "3.0.0-beta.0", | ||
"description": "tsParticles image shape", | ||
@@ -48,8 +48,9 @@ "homepage": "https://particles.js.org", | ||
"types": "types/index.d.ts", | ||
"sideEffects": false, | ||
"dependencies": { | ||
"@tsparticles/engine": "^3.0.0-beta.0" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"dependencies": { | ||
"@tsparticles/engine": "^3.0.0-alpha.1" | ||
} | ||
} | ||
} |
@@ -5,5 +5,5 @@ [![banner](https://particles.js.org/images/banner2.png)](https://particles.js.org) | ||
[![jsDelivr](https://data.jsdelivr.com/v1/package/npm/tsparticles-shape-image/badge)](https://www.jsdelivr.com/package/npm/tsparticles-shape-image) | ||
[![npmjs](https://badge.fury.io/js/tsparticles-shape-image.svg)](https://www.npmjs.com/package/tsparticles-shape-image) | ||
[![npmjs](https://img.shields.io/npm/dt/tsparticles-shape-image)](https://www.npmjs.com/package/tsparticles-shape-image) [![GitHub Sponsors](https://img.shields.io/github/sponsors/matteobruni)](https://github.com/sponsors/matteobruni) | ||
[![jsDelivr](https://data.jsdelivr.com/v1/package/npm/@tsparticles/shape-image/badge)](https://www.jsdelivr.com/package/npm/@tsparticles/shape-image) | ||
[![npmjs](https://badge.fury.io/js/@tsparticles/shape-image.svg)](https://www.npmjs.com/package/@tsparticles/shape-image) | ||
[![npmjs](https://img.shields.io/npm/dt/@tsparticles/shape-image)](https://www.npmjs.com/package/@tsparticles/shape-image) [![GitHub Sponsors](https://img.shields.io/github/sponsors/matteobruni)](https://github.com/sponsors/matteobruni) | ||
@@ -30,3 +30,3 @@ [tsParticles](https://github.com/matteobruni/tsparticles) additional image shape. | ||
(async () => { | ||
await loadImageShape(); | ||
await loadImageShape(tsParticles); | ||
@@ -48,3 +48,3 @@ await tsParticles.load({ | ||
```shell | ||
$ npm install tsparticles-shape-image | ||
$ npm install @tsparticles/shape-image | ||
``` | ||
@@ -55,3 +55,3 @@ | ||
```shell | ||
$ yarn add tsparticles-shape-image | ||
$ yarn add @tsparticles/shape-image | ||
``` | ||
@@ -62,6 +62,8 @@ | ||
```javascript | ||
const { tsParticles } = require("tsparticles-engine"); | ||
const { loadImageShape } = require("tsparticles-shape-image"); | ||
const { tsParticles } = require("@tsparticles/engine"); | ||
const { loadImageShape } = require("@tsparticles/shape-image"); | ||
loadImageShape(tsParticles); | ||
(async () => { | ||
await loadImageShape(tsParticles); | ||
})(); | ||
``` | ||
@@ -72,6 +74,8 @@ | ||
```javascript | ||
import { tsParticles } from "tsparticles-engine"; | ||
import { loadImageShape } from "tsparticles-shape-image"; | ||
import { tsParticles } from "@tsparticles/engine"; | ||
import { loadImageShape } from "@tsparticles/shape-image"; | ||
loadImageShape(tsParticles); | ||
(async () => { | ||
await loadImageShape(tsParticles); | ||
})(); | ||
``` |
@@ -7,3 +7,3 @@ /*! | ||
* How to use? : Check the GitHub README | ||
* v3.0.0-alpha.1 | ||
* v3.0.0-beta.0 | ||
*/ | ||
@@ -95,3 +95,3 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
__webpack_require__.d(__webpack_exports__, { | ||
"loadImageShape": () => (/* binding */ loadImageShape) | ||
loadImageShape: () => (/* binding */ loadImageShape) | ||
}); | ||
@@ -101,4 +101,421 @@ | ||
var engine_root_window_ = __webpack_require__(533); | ||
;// CONCATENATED MODULE: ./dist/browser/GifUtils/Constants.js | ||
const InterlaceOffsets = [0, 4, 2, 1]; | ||
const InterlaceSteps = [8, 8, 4, 2]; | ||
;// CONCATENATED MODULE: ./dist/browser/GifUtils/ByteStream.js | ||
class ByteStream { | ||
constructor(bytes) { | ||
this.pos = 0; | ||
this.data = new Uint8ClampedArray(bytes); | ||
} | ||
getString(count) { | ||
const slice = this.data.slice(this.pos, this.pos + count); | ||
this.pos += slice.length; | ||
return slice.reduce((acc, curr) => acc + String.fromCharCode(curr), ""); | ||
} | ||
nextByte() { | ||
return this.data[this.pos++]; | ||
} | ||
nextTwoBytes() { | ||
this.pos += 2; | ||
return this.data[this.pos - 2] + (this.data[this.pos - 1] << 8); | ||
} | ||
readSubBlocks() { | ||
let blockString = "", | ||
size = 0; | ||
do { | ||
size = this.data[this.pos++]; | ||
for (let count = size; --count >= 0; blockString += String.fromCharCode(this.data[this.pos++])) {} | ||
} while (size !== 0); | ||
return blockString; | ||
} | ||
readSubBlocksBin() { | ||
let size = 0, | ||
len = 0; | ||
for (let offset = 0; (size = this.data[this.pos + offset]) !== 0; offset += size + 1) { | ||
len += size; | ||
} | ||
const blockData = new Uint8Array(len); | ||
for (let i = 0; (size = this.data[this.pos++]) !== 0;) { | ||
for (let count = size; --count >= 0; blockData[i++] = this.data[this.pos++]) {} | ||
} | ||
return blockData; | ||
} | ||
skipSubBlocks() { | ||
for (; this.data[this.pos] !== 0; this.pos += this.data[this.pos] + 1) {} | ||
this.pos++; | ||
} | ||
} | ||
;// CONCATENATED MODULE: ./dist/browser/GifUtils/Utils.js | ||
function parseColorTable(byteStream, count) { | ||
const colors = []; | ||
for (let i = 0; i < count; i++) { | ||
colors.push({ | ||
r: byteStream.data[byteStream.pos], | ||
g: byteStream.data[byteStream.pos + 1], | ||
b: byteStream.data[byteStream.pos + 2] | ||
}); | ||
byteStream.pos += 3; | ||
} | ||
return colors; | ||
} | ||
async function parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyIndex) { | ||
switch (byteStream.nextByte()) { | ||
case 249: | ||
{ | ||
const frame = gif.frames[getFrameIndex(false)]; | ||
byteStream.pos++; | ||
const packedByte = byteStream.nextByte(); | ||
frame.GCreserved = (packedByte & 0xe0) >>> 5; | ||
frame.disposalMethod = (packedByte & 0x1c) >>> 2; | ||
frame.userInputDelayFlag = (packedByte & 2) === 2; | ||
const transparencyFlag = (packedByte & 1) === 1; | ||
frame.delayTime = byteStream.nextTwoBytes() * 0xa; | ||
const transparencyIndex = byteStream.nextByte(); | ||
if (transparencyFlag) { | ||
getTransparencyIndex(transparencyIndex); | ||
} | ||
byteStream.pos++; | ||
break; | ||
} | ||
case 255: | ||
{ | ||
byteStream.pos++; | ||
const applicationExtension = { | ||
identifier: byteStream.getString(8), | ||
authenticationCode: byteStream.getString(3), | ||
data: byteStream.readSubBlocksBin() | ||
}; | ||
gif.applicationExtensions.push(applicationExtension); | ||
break; | ||
} | ||
case 254: | ||
{ | ||
gif.comments.push([getFrameIndex(false), byteStream.readSubBlocks()]); | ||
break; | ||
} | ||
case 1: | ||
{ | ||
if (gif.globalColorTable.length === 0) { | ||
throw new EvalError("plain text extension without global color table"); | ||
} | ||
byteStream.pos++; | ||
gif.frames[getFrameIndex(false)].plainTextData = { | ||
left: byteStream.nextTwoBytes(), | ||
top: byteStream.nextTwoBytes(), | ||
width: byteStream.nextTwoBytes(), | ||
height: byteStream.nextTwoBytes(), | ||
charSize: { | ||
width: byteStream.nextTwoBytes(), | ||
height: byteStream.nextTwoBytes() | ||
}, | ||
foregroundColor: byteStream.nextByte(), | ||
backgroundColor: byteStream.nextByte(), | ||
text: byteStream.readSubBlocks() | ||
}; | ||
break; | ||
} | ||
default: | ||
byteStream.skipSubBlocks(); | ||
break; | ||
} | ||
} | ||
async function parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback) { | ||
const frame = gif.frames[getFrameIndex(true)]; | ||
frame.left = byteStream.nextTwoBytes(); | ||
frame.top = byteStream.nextTwoBytes(); | ||
frame.width = byteStream.nextTwoBytes(); | ||
frame.height = byteStream.nextTwoBytes(); | ||
const packedByte = byteStream.nextByte(), | ||
localColorTableFlag = (packedByte & 0x80) === 0x80, | ||
interlacedFlag = (packedByte & 0x40) === 0x40; | ||
frame.sortFlag = (packedByte & 0x20) === 0x20; | ||
frame.reserved = (packedByte & 0x18) >>> 3; | ||
const localColorCount = 1 << (packedByte & 7) + 1; | ||
if (localColorTableFlag) { | ||
frame.localColorTable = parseColorTable(byteStream, localColorCount); | ||
} | ||
const getColor = index => { | ||
const { | ||
r, | ||
g, | ||
b | ||
} = (localColorTableFlag ? frame.localColorTable : gif.globalColorTable)[index]; | ||
return { | ||
r, | ||
g, | ||
b, | ||
a: index === getTransparencyIndex(null) ? avgAlpha ? ~~((r + g + b) / 3) : 0 : 255 | ||
}; | ||
}; | ||
const image = (() => { | ||
try { | ||
return new ImageData(frame.width, frame.height, { | ||
colorSpace: "srgb" | ||
}); | ||
} catch (error) { | ||
if (error instanceof DOMException && error.name === "IndexSizeError") { | ||
return null; | ||
} | ||
throw error; | ||
} | ||
})(); | ||
if (image == null) { | ||
throw new EvalError("GIF frame size is to large"); | ||
} | ||
const minCodeSize = byteStream.nextByte(), | ||
imageData = byteStream.readSubBlocksBin(), | ||
clearCode = 1 << minCodeSize; | ||
const readBits = (pos, len) => { | ||
const bytePos = pos >>> 3, | ||
bitPos = pos & 7; | ||
return (imageData[bytePos] + (imageData[bytePos + 1] << 8) + (imageData[bytePos + 2] << 16) & (1 << len) - 1 << bitPos) >>> bitPos; | ||
}; | ||
if (interlacedFlag) { | ||
for (let code = 0, size = minCodeSize + 1, pos = 0, dic = [[0]], pass = 0; pass < 4; pass++) { | ||
if (InterlaceOffsets[pass] < frame.height) { | ||
for (let pixelPos = 0, lineIndex = 0;;) { | ||
const last = code; | ||
code = readBits(pos, size); | ||
pos += size + 1; | ||
if (code === clearCode) { | ||
size = minCodeSize + 1; | ||
dic.length = clearCode + 2; | ||
for (let i = 0; i < dic.length; i++) { | ||
dic[i] = i < clearCode ? [i] : []; | ||
} | ||
} else { | ||
if (code >= dic.length) { | ||
dic.push(dic[last].concat(dic[last][0])); | ||
} else if (last !== clearCode) { | ||
dic.push(dic[last].concat(dic[code][0])); | ||
} | ||
for (let i = 0; i < dic[code].length; i++) { | ||
const { | ||
r, | ||
g, | ||
b, | ||
a | ||
} = getColor(dic[code][i]); | ||
image.data.set([r, g, b, a], InterlaceOffsets[pass] * frame.width + InterlaceSteps[pass] * lineIndex + pixelPos % (frame.width * 4)); | ||
pixelPos += 4; | ||
} | ||
if (dic.length === 1 << size && size < 0xc) { | ||
size++; | ||
} | ||
} | ||
if (pixelPos === frame.width * 4 * (lineIndex + 1)) { | ||
lineIndex++; | ||
if (InterlaceOffsets[pass] + InterlaceSteps[pass] * lineIndex >= frame.height) { | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
progressCallback?.(byteStream.pos / (byteStream.data.length - 1), getFrameIndex(false) + 1, image, { | ||
x: frame.left, | ||
y: frame.top | ||
}, { | ||
width: gif.width, | ||
height: gif.height | ||
}); | ||
} | ||
frame.image = image; | ||
frame.bitmap = await createImageBitmap(image); | ||
} else { | ||
for (let code = 0, size = minCodeSize + 1, pos = 0, dic = [[0]], pixelPos = -4;;) { | ||
const last = code; | ||
code = readBits(pos, size); | ||
pos += size; | ||
if (code === clearCode) { | ||
size = minCodeSize + 1; | ||
dic.length = clearCode + 2; | ||
for (let i = 0; i < dic.length; i++) { | ||
dic[i] = i < clearCode ? [i] : []; | ||
} | ||
} else { | ||
if (code === clearCode + 1) { | ||
break; | ||
} | ||
if (code >= dic.length) { | ||
dic.push(dic[last].concat(dic[last][0])); | ||
} else if (last !== clearCode) { | ||
dic.push(dic[last].concat(dic[code][0])); | ||
} | ||
for (let i = 0; i < dic[code].length; i++) { | ||
const { | ||
r, | ||
g, | ||
b, | ||
a | ||
} = getColor(dic[code][i]); | ||
image.data.set([r, g, b, a], pixelPos += 4); | ||
} | ||
if (dic.length >= 1 << size && size < 0xc) { | ||
size++; | ||
} | ||
} | ||
} | ||
frame.image = image; | ||
frame.bitmap = await createImageBitmap(image); | ||
progressCallback?.((byteStream.pos + 1) / byteStream.data.length, getFrameIndex(false) + 1, frame.image, { | ||
x: frame.left, | ||
y: frame.top | ||
}, { | ||
width: gif.width, | ||
height: gif.height | ||
}); | ||
} | ||
} | ||
async function parseBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback) { | ||
switch (byteStream.nextByte()) { | ||
case 59: | ||
return true; | ||
case 44: | ||
await parseImageBlock(byteStream, gif, avgAlpha, getFrameIndex, getTransparencyIndex, progressCallback); | ||
break; | ||
case 33: | ||
await parseExtensionBlock(byteStream, gif, getFrameIndex, getTransparencyIndex); | ||
break; | ||
default: | ||
throw new EvalError("undefined block found"); | ||
} | ||
return false; | ||
} | ||
function getGIFLoopAmount(gif) { | ||
for (const extension of gif.applicationExtensions) { | ||
if (extension.identifier + extension.authenticationCode !== "NETSCAPE2.0") { | ||
continue; | ||
} | ||
return extension.data[1] + (extension.data[2] << 8); | ||
} | ||
return NaN; | ||
} | ||
async function decodeGIF(gifURL, progressCallback, avgAlpha) { | ||
if (!avgAlpha) avgAlpha = false; | ||
const res = await fetch(gifURL); | ||
if (!res.ok && res.status === 404) { | ||
throw new EvalError("file not found"); | ||
} | ||
const buffer = await res.arrayBuffer(); | ||
const gif = { | ||
width: 0, | ||
height: 0, | ||
totalTime: 0, | ||
colorRes: 0, | ||
pixelAspectRatio: 0, | ||
frames: [], | ||
sortFlag: false, | ||
globalColorTable: [], | ||
backgroundImage: new ImageData(1, 1, { | ||
colorSpace: "srgb" | ||
}), | ||
comments: [], | ||
applicationExtensions: [] | ||
}, | ||
byteStream = new ByteStream(new Uint8ClampedArray(buffer)); | ||
if (byteStream.getString(6) !== "GIF89a") { | ||
throw new Error("not a supported GIF file"); | ||
} | ||
gif.width = byteStream.nextTwoBytes(); | ||
gif.height = byteStream.nextTwoBytes(); | ||
const packedByte = byteStream.nextByte(), | ||
globalColorTableFlag = (packedByte & 0x80) === 0x80; | ||
gif.colorRes = (packedByte & 0x70) >>> 4; | ||
gif.sortFlag = (packedByte & 8) === 8; | ||
const globalColorCount = 1 << (packedByte & 7) + 1, | ||
backgroundColorIndex = byteStream.nextByte(); | ||
gif.pixelAspectRatio = byteStream.nextByte(); | ||
if (gif.pixelAspectRatio !== 0) { | ||
gif.pixelAspectRatio = (gif.pixelAspectRatio + 0xf) / 0x40; | ||
} | ||
if (globalColorTableFlag) { | ||
gif.globalColorTable = parseColorTable(byteStream, globalColorCount); | ||
} | ||
const backgroundImage = (() => { | ||
try { | ||
return new ImageData(gif.width, gif.height, { | ||
colorSpace: "srgb" | ||
}); | ||
} catch (error) { | ||
if (error instanceof DOMException && error.name === "IndexSizeError") { | ||
return null; | ||
} | ||
throw error; | ||
} | ||
})(); | ||
if (backgroundImage == null) { | ||
throw new Error("GIF frame size is to large"); | ||
} | ||
const { | ||
r, | ||
g, | ||
b | ||
} = gif.globalColorTable[backgroundColorIndex]; | ||
backgroundImage.data.set(globalColorTableFlag ? [r, g, b, 255] : [0, 0, 0, 0]); | ||
for (let i = 4; i < backgroundImage.data.length; i *= 2) { | ||
backgroundImage.data.copyWithin(i, 0, i); | ||
} | ||
gif.backgroundImage = backgroundImage; | ||
let frameIndex = -1, | ||
incrementFrameIndex = true, | ||
transparencyIndex = -1; | ||
const getframeIndex = increment => { | ||
if (increment) { | ||
incrementFrameIndex = true; | ||
} | ||
return frameIndex; | ||
}; | ||
const getTransparencyIndex = newValue => { | ||
if (newValue != null) { | ||
transparencyIndex = newValue; | ||
} | ||
return transparencyIndex; | ||
}; | ||
try { | ||
do { | ||
if (incrementFrameIndex) { | ||
gif.frames.push({ | ||
left: 0, | ||
top: 0, | ||
width: 0, | ||
height: 0, | ||
disposalMethod: 0, | ||
image: new ImageData(1, 1, { | ||
colorSpace: "srgb" | ||
}), | ||
plainTextData: null, | ||
userInputDelayFlag: false, | ||
delayTime: 0, | ||
sortFlag: false, | ||
localColorTable: [], | ||
reserved: 0, | ||
GCreserved: 0 | ||
}); | ||
frameIndex++; | ||
transparencyIndex = -1; | ||
incrementFrameIndex = false; | ||
} | ||
} while (!(await parseBlock(byteStream, gif, avgAlpha, getframeIndex, getTransparencyIndex, progressCallback))); | ||
gif.frames.length--; | ||
for (const frame of gif.frames) { | ||
if (frame.userInputDelayFlag && frame.delayTime === 0) { | ||
gif.totalTime = Infinity; | ||
break; | ||
} | ||
gif.totalTime += frame.delayTime; | ||
} | ||
return gif; | ||
} catch (error) { | ||
if (error instanceof EvalError) { | ||
throw new Error(`error while parsing frame ${frameIndex} "${error.message}"`); | ||
} | ||
throw error; | ||
} | ||
} | ||
;// CONCATENATED MODULE: ./dist/browser/Utils.js | ||
const currentColorRegex = /(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi; | ||
@@ -132,3 +549,3 @@ function replaceColorSvg(imageShape, color, opacity) { | ||
image.loading = false; | ||
console.error(`Error tsParticles - loading image: ${image.source}`); | ||
(0,engine_root_window_.getLogger)().error(`${engine_root_window_.errorPrefix} loading image: ${image.source}`); | ||
resolve(); | ||
@@ -139,2 +556,19 @@ }); | ||
} | ||
async function loadGifImage(image) { | ||
if (image.type !== "gif") { | ||
await loadImage(image); | ||
return; | ||
} | ||
image.loading = true; | ||
try { | ||
image.gifData = await decodeGIF(image.source); | ||
image.gifLoopCount = getGIFLoopAmount(image.gifData) ?? 0; | ||
if (image.gifLoopCount === 0) { | ||
image.gifLoopCount = Infinity; | ||
} | ||
} catch { | ||
image.error = true; | ||
} | ||
image.loading = false; | ||
} | ||
async function downloadSvgImage(image) { | ||
@@ -148,6 +582,5 @@ if (image.type !== "svg") { | ||
if (!response.ok) { | ||
console.error("Error tsParticles - Image not found"); | ||
(0,engine_root_window_.getLogger)().error(`${engine_root_window_.errorPrefix} Image not found`); | ||
image.error = true; | ||
} | ||
if (!image.error) { | ||
} else { | ||
image.svgData = await response.text(); | ||
@@ -158,12 +591,13 @@ } | ||
function replaceImageColor(image, imageData, color, particle) { | ||
var _a, _b, _c; | ||
const svgColoredData = replaceColorSvg(image, color, (_b = (_a = particle.opacity) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 1), | ||
const svgColoredData = replaceColorSvg(image, color, particle.opacity?.value ?? 1), | ||
imageRes = { | ||
color, | ||
data: Object.assign(Object.assign({}, image), { | ||
gif: imageData.gif, | ||
data: { | ||
...image, | ||
svgData: svgColoredData | ||
}), | ||
}, | ||
loaded: false, | ||
ratio: imageData.width / imageData.height, | ||
replaceColor: (_c = imageData.replaceColor) !== null && _c !== void 0 ? _c : imageData.replace_color, | ||
replaceColor: imageData.replaceColor, | ||
source: imageData.src | ||
@@ -186,6 +620,7 @@ }; | ||
domUrl.revokeObjectURL(url); | ||
const img2 = Object.assign(Object.assign({}, image), { | ||
const img2 = { | ||
...image, | ||
error: false, | ||
loading: true | ||
}); | ||
}; | ||
await loadImage(img2); | ||
@@ -201,44 +636,125 @@ imageRes.loaded = true; | ||
class ImageDrawer { | ||
constructor() { | ||
this._images = []; | ||
constructor(engine) { | ||
this.loadImageShape = async imageShape => { | ||
if (!this._engine.loadImage) { | ||
throw new Error(`${engine_root_window_.errorPrefix} image shape not initialized`); | ||
} | ||
await this._engine.loadImage({ | ||
gif: imageShape.gif, | ||
name: imageShape.name, | ||
replaceColor: imageShape.replaceColor ?? false, | ||
src: imageShape.src | ||
}); | ||
}; | ||
this._engine = engine; | ||
} | ||
addImage(container, image) { | ||
const containerImages = this.getImages(container); | ||
containerImages === null || containerImages === void 0 ? void 0 : containerImages.images.push(image); | ||
addImage(image) { | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
this._engine.images.push(image); | ||
} | ||
destroy() { | ||
this._images = []; | ||
} | ||
draw(context, particle, radius, opacity) { | ||
var _a; | ||
draw(context, particle, radius, opacity, delta) { | ||
const image = particle.image, | ||
element = image === null || image === void 0 ? void 0 : image.element; | ||
if (!element) { | ||
element = image?.element; | ||
if (!image) { | ||
return; | ||
} | ||
const ratio = (_a = image === null || image === void 0 ? void 0 : image.ratio) !== null && _a !== void 0 ? _a : 1, | ||
pos = { | ||
x: -radius, | ||
y: -radius | ||
}; | ||
context.globalAlpha = opacity; | ||
context.drawImage(element, pos.x, pos.y, radius * 2, radius * 2 / ratio); | ||
if (image.gif && image.gifData) { | ||
const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), | ||
offscreenContext = offscreenCanvas.getContext("2d"); | ||
if (!offscreenContext) { | ||
throw new Error("could not create offscreen canvas context"); | ||
} | ||
offscreenContext.imageSmoothingQuality = "low"; | ||
offscreenContext.imageSmoothingEnabled = false; | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
if (particle.gifLoopCount === undefined) { | ||
particle.gifLoopCount = image.gifLoopCount ?? 0; | ||
} | ||
let frameIndex = particle.gifFrame ?? 0; | ||
const pos = { | ||
x: -image.gifData.width * 0.5, | ||
y: -image.gifData.height * 0.5 | ||
}, | ||
frame = image.gifData.frames[frameIndex]; | ||
if (particle.gifTime === undefined) { | ||
particle.gifTime = 0; | ||
} | ||
if (!frame.bitmap) { | ||
return; | ||
} | ||
context.scale(radius / image.gifData.width, radius / image.gifData.height); | ||
switch (frame.disposalMethod) { | ||
case 4: | ||
case 5: | ||
case 6: | ||
case 7: | ||
case 0: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
break; | ||
case 1: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
break; | ||
case 2: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
if (image.gifData.globalColorTable.length === 0) { | ||
offscreenContext.putImageData(image.gifData.frames[0].image, pos.x + frame.left, pos.y + frame.top); | ||
} else { | ||
offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y); | ||
} | ||
break; | ||
case 3: | ||
{ | ||
const previousImageData = offscreenContext.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
offscreenContext.putImageData(previousImageData, 0, 0); | ||
} | ||
break; | ||
} | ||
particle.gifTime += delta.value; | ||
if (particle.gifTime > frame.delayTime) { | ||
particle.gifTime -= frame.delayTime; | ||
if (++frameIndex >= image.gifData.frames.length) { | ||
if (--particle.gifLoopCount <= 0) { | ||
return; | ||
} | ||
frameIndex = 0; | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
} | ||
particle.gifFrame = frameIndex; | ||
} | ||
context.scale(image.gifData.width / radius, image.gifData.height / radius); | ||
} else if (element) { | ||
const ratio = image.ratio, | ||
pos = { | ||
x: -radius, | ||
y: -radius | ||
}; | ||
context.drawImage(element, pos.x, pos.y, radius * 2, radius * 2 / ratio); | ||
} | ||
context.globalAlpha = 1; | ||
} | ||
getImages(container) { | ||
const containerImages = this._images.find(t => t.id === container.id); | ||
if (!containerImages) { | ||
this._images.push({ | ||
id: container.id, | ||
images: [] | ||
}); | ||
return this.getImages(container); | ||
} else { | ||
return containerImages; | ||
} | ||
} | ||
getSidesCount() { | ||
return 12; | ||
} | ||
async init(container) { | ||
const options = container.actualOptions; | ||
if (!options.preload || !this._engine.loadImage) { | ||
return; | ||
} | ||
for (const imageData of options.preload) { | ||
await this._engine.loadImage(imageData); | ||
} | ||
} | ||
loadShape(particle) { | ||
@@ -248,8 +764,9 @@ if (particle.shape !== "image" && particle.shape !== "images") { | ||
} | ||
const container = particle.container, | ||
images = this.getImages(container).images, | ||
imageData = particle.shapeData, | ||
image = images.find(t => t.source === imageData.src); | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
const imageData = particle.shapeData, | ||
image = this._engine.images.find(t => t.name === imageData.name || t.source === imageData.src); | ||
if (!image) { | ||
this.loadImageShape(container, imageData).then(() => { | ||
this.loadImageShape(imageData).then(() => { | ||
this.loadShape(particle); | ||
@@ -260,14 +777,16 @@ }); | ||
particleInit(container, particle) { | ||
var _a; | ||
if (particle.shape !== "image" && particle.shape !== "images") { | ||
return; | ||
} | ||
const images = this.getImages(container).images, | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
const images = this._engine.images, | ||
imageData = particle.shapeData, | ||
color = particle.getFillColor(), | ||
replaceColor = (_a = imageData.replaceColor) !== null && _a !== void 0 ? _a : imageData.replace_color, | ||
image = images.find(t => t.source === imageData.src); | ||
image = images.find(t => t.name === imageData.name || t.source === imageData.src); | ||
if (!image) { | ||
return; | ||
} | ||
const replaceColor = imageData.replaceColor ?? image.replaceColor; | ||
if (image.loading) { | ||
@@ -280,3 +799,2 @@ setTimeout(() => { | ||
(async () => { | ||
var _a, _b; | ||
let imageRes; | ||
@@ -290,4 +808,7 @@ if (image.svgData && color) { | ||
element: image.element, | ||
gif: image.gif, | ||
gifData: image.gifData, | ||
gifLoopCount: image.gifLoopCount, | ||
loaded: true, | ||
ratio: imageData.width / imageData.height, | ||
ratio: imageData.width && imageData.height ? imageData.width / imageData.height : image.ratio ?? 1, | ||
replaceColor: replaceColor, | ||
@@ -300,4 +821,4 @@ source: imageData.src | ||
} | ||
const fill = (_a = imageData.fill) !== null && _a !== void 0 ? _a : particle.fill, | ||
close = (_b = imageData.close) !== null && _b !== void 0 ? _b : particle.close, | ||
const fill = imageData.fill ?? particle.fill, | ||
close = imageData.close ?? particle.close, | ||
imageShape = { | ||
@@ -313,27 +834,109 @@ image: imageRes, | ||
} | ||
async loadImageShape(container, imageShape) { | ||
var _a; | ||
const source = imageShape.src; | ||
if (!source) { | ||
throw new Error("Error tsParticles - No image.src"); | ||
} | ||
;// CONCATENATED MODULE: ./dist/browser/Options/Classes/Preload.js | ||
class Preload { | ||
constructor() { | ||
this.src = ""; | ||
this.gif = false; | ||
} | ||
load(data) { | ||
if (!data) { | ||
return; | ||
} | ||
if (data.gif !== undefined) { | ||
this.gif = data.gif; | ||
} | ||
if (data.height !== undefined) { | ||
this.height = data.height; | ||
} | ||
if (data.name !== undefined) { | ||
this.name = data.name; | ||
} | ||
if (data.replaceColor !== undefined) { | ||
this.replaceColor = data.replaceColor; | ||
} | ||
if (data.src !== undefined) { | ||
this.src = data.src; | ||
} | ||
if (data.width !== undefined) { | ||
this.width = data.width; | ||
} | ||
} | ||
} | ||
;// CONCATENATED MODULE: ./dist/browser/ImagePreloader.js | ||
class ImagePreloaderPlugin { | ||
constructor(engine) { | ||
this.id = "imagePreloader"; | ||
this._engine = engine; | ||
} | ||
getPlugin() { | ||
return {}; | ||
} | ||
loadOptions(options, source) { | ||
if (!source || !source.preload) { | ||
return; | ||
} | ||
if (!options.preload) { | ||
options.preload = []; | ||
} | ||
const preloadOptions = options.preload; | ||
for (const item of source.preload) { | ||
const existing = preloadOptions.find(t => t.name === item.name || t.src === item.src); | ||
if (existing) { | ||
existing.load(item); | ||
} else { | ||
const preload = new Preload(); | ||
preload.load(item); | ||
preloadOptions.push(preload); | ||
} | ||
} | ||
} | ||
needsPlugin() { | ||
return true; | ||
} | ||
} | ||
;// CONCATENATED MODULE: ./dist/browser/index.js | ||
function addLoadImageToEngine(engine) { | ||
if (engine.loadImage) { | ||
return; | ||
} | ||
engine.loadImage = async data => { | ||
if (!data.name && !data.src) { | ||
throw new Error(`${engine_root_window_.errorPrefix} no image source provided`); | ||
} | ||
if (!engine.images) { | ||
engine.images = []; | ||
} | ||
if (engine.images.find(t => t.name === data.name || t.source === data.src)) { | ||
return; | ||
} | ||
try { | ||
const image = { | ||
source: source, | ||
type: source.substring(source.length - 3), | ||
gif: data.gif ?? false, | ||
name: data.name ?? data.src, | ||
source: data.src, | ||
type: data.src.substring(data.src.length - 3), | ||
error: false, | ||
loading: true | ||
loading: true, | ||
replaceColor: data.replaceColor, | ||
ratio: data.width && data.height ? data.width / data.height : undefined | ||
}; | ||
this.addImage(container, image); | ||
const imageFunc = ((_a = imageShape.replaceColor) !== null && _a !== void 0 ? _a : imageShape.replace_color) ? downloadSvgImage : loadImage; | ||
engine.images.push(image); | ||
const imageFunc = data.gif ? loadGifImage : data.replaceColor ? downloadSvgImage : loadImage; | ||
await imageFunc(image); | ||
} catch (_b) { | ||
throw new Error(`tsParticles error - ${imageShape.src} not found`); | ||
} catch { | ||
throw new Error(`${engine_root_window_.errorPrefix} ${data.name ?? data.src} not found`); | ||
} | ||
} | ||
}; | ||
} | ||
;// CONCATENATED MODULE: ./dist/browser/index.js | ||
async function loadImageShape(engine) { | ||
await engine.addShape(["image", "images"], new ImageDrawer()); | ||
async function loadImageShape(engine, refresh = true) { | ||
addLoadImageToEngine(engine); | ||
const preloader = new ImagePreloaderPlugin(engine); | ||
await engine.addPlugin(preloader, refresh); | ||
await engine.addShape(["image", "images"], new ImageDrawer(engine), refresh); | ||
} | ||
@@ -340,0 +943,0 @@ })(); |
/*! For license information please see tsparticles.shape.image.min.js.LICENSE.txt */ | ||
!function(e,r){if("object"==typeof exports&&"object"==typeof module)module.exports=r(require("@tsparticles/engine"));else if("function"==typeof define&&define.amd)define(["@tsparticles/engine"],r);else{var t="object"==typeof exports?r(require("@tsparticles/engine")):r(e.window);for(var o in t)("object"==typeof exports?exports:e)[o]=t[o]}}(this,(e=>(()=>{"use strict";var r={533:r=>{r.exports=e}},t={};function o(e){var a=t[e];if(void 0!==a)return a.exports;var i=t[e]={exports:{}};return r[e](i,i.exports,o),i.exports}o.d=(e,r)=>{for(var t in r)o.o(r,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},o.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var a={};return(()=>{o.r(a),o.d(a,{loadImageShape:()=>l});var e=o(533);const r=/(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi;async function t(e){return new Promise((r=>{e.loading=!0;const t=new Image;e.element=t,t.addEventListener("load",(()=>{e.loading=!1,r()})),t.addEventListener("error",(()=>{e.element=void 0,e.error=!0,e.loading=!1,console.error(`Error tsParticles - loading image: ${e.source}`),r()})),t.src=e.source}))}async function i(e){if("svg"!==e.type)return void await t(e);e.loading=!0;const r=await fetch(e.source);r.ok||(console.error("Error tsParticles - Image not found"),e.error=!0),e.error||(e.svgData=await r.text()),e.loading=!1}function s(o,a,i,s){var n,l,c;const d=function(t,o,a){const{svgData:i}=t;if(!i)return"";const s=(0,e.getStyleFromHsl)(o,a);if(i.includes("fill"))return i.replace(r,(()=>s));const n=i.indexOf(">");return`${i.substring(0,n)} fill="${s}"${i.substring(n)}`}(o,i,null!==(l=null===(n=s.opacity)||void 0===n?void 0:n.value)&&void 0!==l?l:1),g={color:i,data:Object.assign(Object.assign({},o),{svgData:d}),loaded:!1,ratio:a.width/a.height,replaceColor:null!==(c=a.replaceColor)&&void 0!==c?c:a.replace_color,source:a.src};return new Promise((e=>{const r=new Blob([d],{type:"image/svg+xml"}),a=URL||window.URL||window.webkitURL||window,i=a.createObjectURL(r),s=new Image;s.addEventListener("load",(()=>{g.loaded=!0,g.element=s,e(g),a.revokeObjectURL(i)})),s.addEventListener("error",(async()=>{a.revokeObjectURL(i);const r=Object.assign(Object.assign({},o),{error:!1,loading:!0});await t(r),g.loaded=!0,g.element=r.element,e(g)})),s.src=i}))}class n{constructor(){this._images=[]}addImage(e,r){const t=this.getImages(e);null==t||t.images.push(r)}destroy(){this._images=[]}draw(e,r,t,o){var a;const i=r.image,s=null==i?void 0:i.element;if(!s)return;const n=null!==(a=null==i?void 0:i.ratio)&&void 0!==a?a:1,l={x:-t,y:-t};e.globalAlpha=o,e.drawImage(s,l.x,l.y,2*t,2*t/n),e.globalAlpha=1}getImages(e){const r=this._images.find((r=>r.id===e.id));return r||(this._images.push({id:e.id,images:[]}),this.getImages(e))}getSidesCount(){return 12}loadShape(e){if("image"!==e.shape&&"images"!==e.shape)return;const r=e.container,t=this.getImages(r).images,o=e.shapeData;t.find((e=>e.source===o.src))||this.loadImageShape(r,o).then((()=>{this.loadShape(e)}))}particleInit(e,r){var t;if("image"!==r.shape&&"images"!==r.shape)return;const o=this.getImages(e).images,a=r.shapeData,i=r.getFillColor(),n=null!==(t=a.replaceColor)&&void 0!==t?t:a.replace_color,l=o.find((e=>e.source===a.src));l&&(l.loading?setTimeout((()=>{this.particleInit(e,r)})):(async()=>{var e,t;let o;o=l.svgData&&i?await s(l,a,i,r):{color:i,data:l,element:l.element,loaded:!0,ratio:a.width/a.height,replaceColor:n,source:a.src},o.ratio||(o.ratio=1);const c={image:o,fill:null!==(e=a.fill)&&void 0!==e?e:r.fill,close:null!==(t=a.close)&&void 0!==t?t:r.close};r.image=c.image,r.fill=c.fill,r.close=c.close})())}async loadImageShape(e,r){var o;const a=r.src;if(!a)throw new Error("Error tsParticles - No image.src");try{const s={source:a,type:a.substring(a.length-3),error:!1,loading:!0};this.addImage(e,s);const n=(null!==(o=r.replaceColor)&&void 0!==o?o:r.replace_color)?i:t;await n(s)}catch(e){throw new Error(`tsParticles error - ${r.src} not found`)}}}async function l(e){await e.addShape(["image","images"],new n)}})(),a})())); | ||
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("@tsparticles/engine"));else if("function"==typeof define&&define.amd)define(["@tsparticles/engine"],t);else{var a="object"==typeof exports?t(require("@tsparticles/engine")):t(e.window);for(var o in a)("object"==typeof exports?exports:e)[o]=a[o]}}(this,(e=>(()=>{"use strict";var t={533:t=>{t.exports=e}},a={};function o(e){var i=a[e];if(void 0!==i)return i.exports;var r=a[e]={exports:{}};return t[e](r,r.exports,o),r.exports}o.d=(e,t)=>{for(var a in t)o.o(t,a)&&!o.o(e,a)&&Object.defineProperty(e,a,{enumerable:!0,get:t[a]})},o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var i={};return(()=>{o.r(i),o.d(i,{loadImageShape:()=>u});var e=o(533);const t=[0,4,2,1],a=[8,8,4,2];class r{constructor(e){this.pos=0,this.data=new Uint8ClampedArray(e)}getString(e){const t=this.data.slice(this.pos,this.pos+e);return this.pos+=t.length,t.reduce(((e,t)=>e+String.fromCharCode(t)),"")}nextByte(){return this.data[this.pos++]}nextTwoBytes(){return this.pos+=2,this.data[this.pos-2]+(this.data[this.pos-1]<<8)}readSubBlocks(){let e="",t=0;do{t=this.data[this.pos++];for(let a=t;--a>=0;e+=String.fromCharCode(this.data[this.pos++]));}while(0!==t);return e}readSubBlocksBin(){let e=0,t=0;for(let a=0;0!==(e=this.data[this.pos+a]);a+=e+1)t+=e;const a=new Uint8Array(t);for(let t=0;0!==(e=this.data[this.pos++]);)for(let o=e;--o>=0;a[t++]=this.data[this.pos++]);return a}skipSubBlocks(){for(;0!==this.data[this.pos];this.pos+=this.data[this.pos]+1);this.pos++}}function n(e,t){const a=[];for(let o=0;o<t;o++)a.push({r:e.data[e.pos],g:e.data[e.pos+1],b:e.data[e.pos+2]}),e.pos+=3;return a}async function s(e,o,i,r,s,l){switch(e.nextByte()){case 59:return!0;case 44:await async function(e,o,i,r,s,l){const c=o.frames[r(!0)];c.left=e.nextTwoBytes(),c.top=e.nextTwoBytes(),c.width=e.nextTwoBytes(),c.height=e.nextTwoBytes();const g=e.nextByte(),h=128==(128&g),d=64==(64&g);c.sortFlag=32==(32&g),c.reserved=(24&g)>>>3;const f=1<<1+(7&g);h&&(c.localColorTable=n(e,f));const p=e=>{const{r:t,g:a,b:r}=(h?c.localColorTable:o.globalColorTable)[e];return{r:t,g:a,b:r,a:e===s(null)?i?~~((t+a+r)/3):0:255}},m=(()=>{try{return new ImageData(c.width,c.height,{colorSpace:"srgb"})}catch(e){if(e instanceof DOMException&&"IndexSizeError"===e.name)return null;throw e}})();if(null==m)throw new EvalError("GIF frame size is to large");const u=e.nextByte(),w=e.readSubBlocksBin(),y=1<<u,b=(e,t)=>{const a=e>>>3,o=7&e;return(w[a]+(w[a+1]<<8)+(w[a+2]<<16)&(1<<t)-1<<o)>>>o};if(d){for(let i=0,n=u+1,s=0,g=[[0]],h=0;h<4;h++){if(t[h]<c.height)for(let e=0,o=0;;){const r=i;if(i=b(s,n),s+=n+1,i===y){n=u+1,g.length=y+2;for(let e=0;e<g.length;e++)g[e]=e<y?[e]:[]}else{i>=g.length?g.push(g[r].concat(g[r][0])):r!==y&&g.push(g[r].concat(g[i][0]));for(let r=0;r<g[i].length;r++){const{r:n,g:s,b:l,a:d}=p(g[i][r]);m.data.set([n,s,l,d],t[h]*c.width+a[h]*o+e%(4*c.width)),e+=4}g.length===1<<n&&n<12&&n++}if(e===4*c.width*(o+1)&&(o++,t[h]+a[h]*o>=c.height))break}l?.(e.pos/(e.data.length-1),r(!1)+1,m,{x:c.left,y:c.top},{width:o.width,height:o.height})}c.image=m,c.bitmap=await createImageBitmap(m)}else{for(let e=0,t=u+1,a=0,o=[[0]],i=-4;;){const r=e;if(e=b(a,t),a+=t,e===y){t=u+1,o.length=y+2;for(let e=0;e<o.length;e++)o[e]=e<y?[e]:[]}else{if(e===y+1)break;e>=o.length?o.push(o[r].concat(o[r][0])):r!==y&&o.push(o[r].concat(o[e][0]));for(let t=0;t<o[e].length;t++){const{r:a,g:r,b:n,a:s}=p(o[e][t]);m.data.set([a,r,n,s],i+=4)}o.length>=1<<t&&t<12&&t++}}c.image=m,c.bitmap=await createImageBitmap(m),l?.((e.pos+1)/e.data.length,r(!1)+1,c.image,{x:c.left,y:c.top},{width:o.width,height:o.height})}}(e,o,i,r,s,l);break;case 33:await async function(e,t,a,o){switch(e.nextByte()){case 249:{const i=t.frames[a(!1)];e.pos++;const r=e.nextByte();i.GCreserved=(224&r)>>>5,i.disposalMethod=(28&r)>>>2,i.userInputDelayFlag=2==(2&r);const n=1==(1&r);i.delayTime=10*e.nextTwoBytes();const s=e.nextByte();n&&o(s),e.pos++;break}case 255:{e.pos++;const a={identifier:e.getString(8),authenticationCode:e.getString(3),data:e.readSubBlocksBin()};t.applicationExtensions.push(a);break}case 254:t.comments.push([a(!1),e.readSubBlocks()]);break;case 1:if(0===t.globalColorTable.length)throw new EvalError("plain text extension without global color table");e.pos++,t.frames[a(!1)].plainTextData={left:e.nextTwoBytes(),top:e.nextTwoBytes(),width:e.nextTwoBytes(),height:e.nextTwoBytes(),charSize:{width:e.nextTwoBytes(),height:e.nextTwoBytes()},foregroundColor:e.nextByte(),backgroundColor:e.nextByte(),text:e.readSubBlocks()};break;default:e.skipSubBlocks()}}(e,o,r,s);break;default:throw new EvalError("undefined block found")}return!1}const l=/(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi;async function c(t){return new Promise((a=>{t.loading=!0;const o=new Image;t.element=o,o.addEventListener("load",(()=>{t.loading=!1,a()})),o.addEventListener("error",(()=>{t.element=void 0,t.error=!0,t.loading=!1,(0,e.getLogger)().error(`${e.errorPrefix} loading image: ${t.source}`),a()})),o.src=t.source}))}async function g(e){if("gif"===e.type){e.loading=!0;try{e.gifData=await async function(e,t,a){a||(a=!1);const o=await fetch(e);if(!o.ok&&404===o.status)throw new EvalError("file not found");const i=await o.arrayBuffer(),l={width:0,height:0,totalTime:0,colorRes:0,pixelAspectRatio:0,frames:[],sortFlag:!1,globalColorTable:[],backgroundImage:new ImageData(1,1,{colorSpace:"srgb"}),comments:[],applicationExtensions:[]},c=new r(new Uint8ClampedArray(i));if("GIF89a"!==c.getString(6))throw new Error("not a supported GIF file");l.width=c.nextTwoBytes(),l.height=c.nextTwoBytes();const g=c.nextByte(),h=128==(128&g);l.colorRes=(112&g)>>>4,l.sortFlag=8==(8&g);const d=1<<1+(7&g),f=c.nextByte();l.pixelAspectRatio=c.nextByte(),0!==l.pixelAspectRatio&&(l.pixelAspectRatio=(l.pixelAspectRatio+15)/64),h&&(l.globalColorTable=n(c,d));const p=(()=>{try{return new ImageData(l.width,l.height,{colorSpace:"srgb"})}catch(e){if(e instanceof DOMException&&"IndexSizeError"===e.name)return null;throw e}})();if(null==p)throw new Error("GIF frame size is to large");const{r:m,g:u,b:w}=l.globalColorTable[f];p.data.set(h?[m,u,w,255]:[0,0,0,0]);for(let e=4;e<p.data.length;e*=2)p.data.copyWithin(e,0,e);l.backgroundImage=p;let y=-1,b=!0,x=-1;const v=e=>(e&&(b=!0),y),C=e=>(null!=e&&(x=e),x);try{do{b&&(l.frames.push({left:0,top:0,width:0,height:0,disposalMethod:0,image:new ImageData(1,1,{colorSpace:"srgb"}),plainTextData:null,userInputDelayFlag:!1,delayTime:0,sortFlag:!1,localColorTable:[],reserved:0,GCreserved:0}),y++,x=-1,b=!1)}while(!await s(c,l,a,v,C,t));l.frames.length--;for(const e of l.frames){if(e.userInputDelayFlag&&0===e.delayTime){l.totalTime=1/0;break}l.totalTime+=e.delayTime}return l}catch(e){if(e instanceof EvalError)throw new Error(`error while parsing frame ${y} "${e.message}"`);throw e}}(e.source),e.gifLoopCount=function(e){for(const t of e.applicationExtensions)if(t.identifier+t.authenticationCode==="NETSCAPE2.0")return t.data[1]+(t.data[2]<<8);return NaN}(e.gifData)??0,0===e.gifLoopCount&&(e.gifLoopCount=1/0)}catch{e.error=!0}e.loading=!1}else await c(e)}async function h(t){if("svg"!==t.type)return void await c(t);t.loading=!0;const a=await fetch(t.source);a.ok?t.svgData=await a.text():((0,e.getLogger)().error(`${e.errorPrefix} Image not found`),t.error=!0),t.loading=!1}function d(t,a,o,i){const r=function(t,a,o){const{svgData:i}=t;if(!i)return"";const r=(0,e.getStyleFromHsl)(a,o);if(i.includes("fill"))return i.replace(l,(()=>r));const n=i.indexOf(">");return`${i.substring(0,n)} fill="${r}"${i.substring(n)}`}(t,o,i.opacity?.value??1),n={color:o,gif:a.gif,data:{...t,svgData:r},loaded:!1,ratio:a.width/a.height,replaceColor:a.replaceColor,source:a.src};return new Promise((e=>{const a=new Blob([r],{type:"image/svg+xml"}),o=URL||window.URL||window.webkitURL||window,i=o.createObjectURL(a),s=new Image;s.addEventListener("load",(()=>{n.loaded=!0,n.element=s,e(n),o.revokeObjectURL(i)})),s.addEventListener("error",(async()=>{o.revokeObjectURL(i);const a={...t,error:!1,loading:!0};await c(a),n.loaded=!0,n.element=a.element,e(n)})),s.src=i}))}class f{constructor(t){this.loadImageShape=async t=>{if(!this._engine.loadImage)throw new Error(`${e.errorPrefix} image shape not initialized`);await this._engine.loadImage({gif:t.gif,name:t.name,replaceColor:t.replaceColor??!1,src:t.src})},this._engine=t}addImage(e){this._engine.images||(this._engine.images=[]),this._engine.images.push(e)}draw(e,t,a,o,i){const r=t.image,n=r?.element;if(r){if(e.globalAlpha=o,r.gif&&r.gifData){const o=new OffscreenCanvas(r.gifData.width,r.gifData.height),n=o.getContext("2d");if(!n)throw new Error("could not create offscreen canvas context");n.imageSmoothingQuality="low",n.imageSmoothingEnabled=!1,n.clearRect(0,0,o.width,o.height),void 0===t.gifLoopCount&&(t.gifLoopCount=r.gifLoopCount??0);let s=t.gifFrame??0;const l={x:.5*-r.gifData.width,y:.5*-r.gifData.height},c=r.gifData.frames[s];if(void 0===t.gifTime&&(t.gifTime=0),!c.bitmap)return;switch(e.scale(a/r.gifData.width,a/r.gifData.height),c.disposalMethod){case 4:case 5:case 6:case 7:case 0:n.drawImage(c.bitmap,c.left,c.top),e.drawImage(o,l.x,l.y),n.clearRect(0,0,o.width,o.height);break;case 1:n.drawImage(c.bitmap,c.left,c.top),e.drawImage(o,l.x,l.y);break;case 2:n.drawImage(c.bitmap,c.left,c.top),e.drawImage(o,l.x,l.y),n.clearRect(0,0,o.width,o.height),0===r.gifData.globalColorTable.length?n.putImageData(r.gifData.frames[0].image,l.x+c.left,l.y+c.top):n.putImageData(r.gifData.backgroundImage,l.x,l.y);break;case 3:{const t=n.getImageData(0,0,o.width,o.height);n.drawImage(c.bitmap,c.left,c.top),e.drawImage(o,l.x,l.y),n.clearRect(0,0,o.width,o.height),n.putImageData(t,0,0)}}if(t.gifTime+=i.value,t.gifTime>c.delayTime){if(t.gifTime-=c.delayTime,++s>=r.gifData.frames.length){if(--t.gifLoopCount<=0)return;s=0,n.clearRect(0,0,o.width,o.height)}t.gifFrame=s}e.scale(r.gifData.width/a,r.gifData.height/a)}else if(n){const t=r.ratio,o={x:-a,y:-a};e.drawImage(n,o.x,o.y,2*a,2*a/t)}e.globalAlpha=1}}getSidesCount(){return 12}async init(e){const t=e.actualOptions;if(t.preload&&this._engine.loadImage)for(const e of t.preload)await this._engine.loadImage(e)}loadShape(e){if("image"!==e.shape&&"images"!==e.shape)return;this._engine.images||(this._engine.images=[]);const t=e.shapeData;this._engine.images.find((e=>e.name===t.name||e.source===t.src))||this.loadImageShape(t).then((()=>{this.loadShape(e)}))}particleInit(e,t){if("image"!==t.shape&&"images"!==t.shape)return;this._engine.images||(this._engine.images=[]);const a=this._engine.images,o=t.shapeData,i=t.getFillColor(),r=a.find((e=>e.name===o.name||e.source===o.src));if(!r)return;const n=o.replaceColor??r.replaceColor;r.loading?setTimeout((()=>{this.particleInit(e,t)})):(async()=>{let e;e=r.svgData&&i?await d(r,o,i,t):{color:i,data:r,element:r.element,gif:r.gif,gifData:r.gifData,gifLoopCount:r.gifLoopCount,loaded:!0,ratio:o.width&&o.height?o.width/o.height:r.ratio??1,replaceColor:n,source:o.src},e.ratio||(e.ratio=1);const a={image:e,fill:o.fill??t.fill,close:o.close??t.close};t.image=a.image,t.fill=a.fill,t.close=a.close})()}}class p{constructor(){this.src="",this.gif=!1}load(e){e&&(void 0!==e.gif&&(this.gif=e.gif),void 0!==e.height&&(this.height=e.height),void 0!==e.name&&(this.name=e.name),void 0!==e.replaceColor&&(this.replaceColor=e.replaceColor),void 0!==e.src&&(this.src=e.src),void 0!==e.width&&(this.width=e.width))}}class m{constructor(e){this.id="imagePreloader",this._engine=e}getPlugin(){return{}}loadOptions(e,t){if(!t||!t.preload)return;e.preload||(e.preload=[]);const a=e.preload;for(const e of t.preload){const t=a.find((t=>t.name===e.name||t.src===e.src));if(t)t.load(e);else{const t=new p;t.load(e),a.push(t)}}}needsPlugin(){return!0}}async function u(t,a=!0){!function(t){t.loadImage||(t.loadImage=async a=>{if(!a.name&&!a.src)throw new Error(`${e.errorPrefix} no image source provided`);if(t.images||(t.images=[]),!t.images.find((e=>e.name===a.name||e.source===a.src)))try{const e={gif:a.gif??!1,name:a.name??a.src,source:a.src,type:a.src.substring(a.src.length-3),error:!1,loading:!0,replaceColor:a.replaceColor,ratio:a.width&&a.height?a.width/a.height:void 0};t.images.push(e);const o=a.gif?g:a.replaceColor?h:c;await o(e)}catch{throw new Error(`${e.errorPrefix} ${a.name??a.src} not found`)}})}(t);const o=new m(t);await t.addPlugin(o,a),await t.addShape(["image","images"],new f(t),a)}})(),i})())); |
@@ -1,8 +0,1 @@ | ||
/*! | ||
* Author : Matteo Bruni | ||
* MIT license: https://opensource.org/licenses/MIT | ||
* Demo / Generator : https://particles.js.org/ | ||
* GitHub : https://www.github.com/matteobruni/tsparticles | ||
* How to use? : Check the GitHub README | ||
* v3.0.0-alpha.1 | ||
*/ | ||
/*! tsParticles Image Shape v3.0.0-beta.0 by Matteo Bruni */ |
import type { IShapeValues } from "@tsparticles/engine"; | ||
export interface IImageShape extends IShapeValues { | ||
gif: boolean; | ||
height: number; | ||
name: string; | ||
replaceColor: boolean; | ||
replace_color: boolean; | ||
src: string; | ||
width: number; | ||
} |
@@ -1,14 +0,14 @@ | ||
import type { Container, IShapeDrawer } from "@tsparticles/engine"; | ||
import type { ContainerImage, IImage, ImageParticle } from "./Utils"; | ||
import { type Container, type IDelta, type IShapeDrawer } from "@tsparticles/engine"; | ||
import type { IImage, ImageParticle } from "./Utils"; | ||
import type { ImageContainer, ImageEngine } from "./types"; | ||
export declare class ImageDrawer implements IShapeDrawer { | ||
private _images; | ||
constructor(); | ||
addImage(container: Container, image: IImage): void; | ||
destroy(): void; | ||
draw(context: CanvasRenderingContext2D, particle: ImageParticle, radius: number, opacity: number): void; | ||
getImages(container: Container): ContainerImage; | ||
private readonly _engine; | ||
constructor(engine: ImageEngine); | ||
addImage(image: IImage): void; | ||
draw(context: CanvasRenderingContext2D, particle: ImageParticle, radius: number, opacity: number, delta: IDelta): void; | ||
getSidesCount(): number; | ||
init(container: ImageContainer): Promise<void>; | ||
loadShape(particle: ImageParticle): void; | ||
particleInit(container: Container, particle: ImageParticle): void; | ||
private loadImageShape; | ||
private readonly loadImageShape; | ||
} |
@@ -1,2 +0,2 @@ | ||
import type { Engine } from "@tsparticles/engine"; | ||
export declare function loadImageShape(engine: Engine): Promise<void>; | ||
import type { ImageEngine } from "./types"; | ||
export declare function loadImageShape(engine: ImageEngine, refresh?: boolean): Promise<void>; |
@@ -1,2 +0,3 @@ | ||
import type { IHsl, Particle } from "@tsparticles/engine"; | ||
import { type IHsl, type Particle } from "@tsparticles/engine"; | ||
import type { GIF } from "./GifUtils/Types/GIF"; | ||
import type { IImageShape } from "./IImageShape"; | ||
@@ -7,3 +8,9 @@ export interface IImage { | ||
error: boolean; | ||
gif: boolean; | ||
gifData?: GIF; | ||
gifLoopCount?: number; | ||
loading: boolean; | ||
name: string; | ||
ratio?: number; | ||
replaceColor?: boolean; | ||
source: string; | ||
@@ -17,2 +24,5 @@ svgData?: string; | ||
element?: HTMLImageElement; | ||
gif: boolean; | ||
gifData?: GIF; | ||
gifLoopCount?: number; | ||
loaded?: boolean; | ||
@@ -23,11 +33,11 @@ ratio: number; | ||
} | ||
export interface ContainerImage { | ||
id: string; | ||
images: IImage[]; | ||
} | ||
export type ImageParticle = Particle & { | ||
gifFrame?: number; | ||
gifLoopCount?: number; | ||
gifTime?: number; | ||
image?: IParticleImage; | ||
}; | ||
export declare function loadImage(image: IImage): Promise<void>; | ||
export declare function loadGifImage(image: IImage): Promise<void>; | ||
export declare function downloadSvgImage(image: IImage): Promise<void>; | ||
export declare function replaceImageColor(image: IImage, imageData: IImageShape, color: IHsl, particle: Particle): Promise<IParticleImage>; |
@@ -7,3 +7,3 @@ (function (factory) { | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "./Utils"], factory); | ||
define(["require", "exports", "@tsparticles/engine", "./Utils"], factory); | ||
} | ||
@@ -14,40 +14,108 @@ })(function (require, exports) { | ||
exports.ImageDrawer = void 0; | ||
const engine_1 = require("@tsparticles/engine"); | ||
const Utils_1 = require("./Utils"); | ||
class ImageDrawer { | ||
constructor() { | ||
this._images = []; | ||
constructor(engine) { | ||
this.loadImageShape = async (imageShape) => { | ||
if (!this._engine.loadImage) { | ||
throw new Error(`${engine_1.errorPrefix} image shape not initialized`); | ||
} | ||
await this._engine.loadImage({ | ||
gif: imageShape.gif, | ||
name: imageShape.name, | ||
replaceColor: imageShape.replaceColor ?? false, | ||
src: imageShape.src, | ||
}); | ||
}; | ||
this._engine = engine; | ||
} | ||
addImage(container, image) { | ||
const containerImages = this.getImages(container); | ||
containerImages === null || containerImages === void 0 ? void 0 : containerImages.images.push(image); | ||
addImage(image) { | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
this._engine.images.push(image); | ||
} | ||
destroy() { | ||
this._images = []; | ||
} | ||
draw(context, particle, radius, opacity) { | ||
var _a; | ||
const image = particle.image, element = image === null || image === void 0 ? void 0 : image.element; | ||
if (!element) { | ||
draw(context, particle, radius, opacity, delta) { | ||
const image = particle.image, element = image?.element; | ||
if (!image) { | ||
return; | ||
} | ||
const ratio = (_a = image === null || image === void 0 ? void 0 : image.ratio) !== null && _a !== void 0 ? _a : 1, pos = { | ||
x: -radius, | ||
y: -radius, | ||
}; | ||
context.globalAlpha = opacity; | ||
context.drawImage(element, pos.x, pos.y, radius * 2, (radius * 2) / ratio); | ||
context.globalAlpha = 1; | ||
} | ||
getImages(container) { | ||
const containerImages = this._images.find((t) => t.id === container.id); | ||
if (!containerImages) { | ||
this._images.push({ | ||
id: container.id, | ||
images: [], | ||
}); | ||
return this.getImages(container); | ||
if (image.gif && image.gifData) { | ||
const offscreenCanvas = new OffscreenCanvas(image.gifData.width, image.gifData.height), offscreenContext = offscreenCanvas.getContext("2d"); | ||
if (!offscreenContext) { | ||
throw new Error("could not create offscreen canvas context"); | ||
} | ||
offscreenContext.imageSmoothingQuality = "low"; | ||
offscreenContext.imageSmoothingEnabled = false; | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
if (particle.gifLoopCount === undefined) { | ||
particle.gifLoopCount = image.gifLoopCount ?? 0; | ||
} | ||
let frameIndex = particle.gifFrame ?? 0; | ||
const pos = { x: -image.gifData.width * 0.5, y: -image.gifData.height * 0.5 }, frame = image.gifData.frames[frameIndex]; | ||
if (particle.gifTime === undefined) { | ||
particle.gifTime = 0; | ||
} | ||
if (!frame.bitmap) { | ||
return; | ||
} | ||
context.scale(radius / image.gifData.width, radius / image.gifData.height); | ||
switch (frame.disposalMethod) { | ||
case 4: | ||
case 5: | ||
case 6: | ||
case 7: | ||
case 0: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
break; | ||
case 1: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
break; | ||
case 2: | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
if (image.gifData.globalColorTable.length === 0) { | ||
offscreenContext.putImageData(image.gifData.frames[0].image, pos.x + frame.left, pos.y + frame.top); | ||
} | ||
else { | ||
offscreenContext.putImageData(image.gifData.backgroundImage, pos.x, pos.y); | ||
} | ||
break; | ||
case 3: | ||
{ | ||
const previousImageData = offscreenContext.getImageData(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
offscreenContext.drawImage(frame.bitmap, frame.left, frame.top); | ||
context.drawImage(offscreenCanvas, pos.x, pos.y); | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
offscreenContext.putImageData(previousImageData, 0, 0); | ||
} | ||
break; | ||
} | ||
particle.gifTime += delta.value; | ||
if (particle.gifTime > frame.delayTime) { | ||
particle.gifTime -= frame.delayTime; | ||
if (++frameIndex >= image.gifData.frames.length) { | ||
if (--particle.gifLoopCount <= 0) { | ||
return; | ||
} | ||
frameIndex = 0; | ||
offscreenContext.clearRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); | ||
} | ||
particle.gifFrame = frameIndex; | ||
} | ||
context.scale(image.gifData.width / radius, image.gifData.height / radius); | ||
} | ||
else { | ||
return containerImages; | ||
else if (element) { | ||
const ratio = image.ratio, pos = { | ||
x: -radius, | ||
y: -radius, | ||
}; | ||
context.drawImage(element, pos.x, pos.y, radius * 2, (radius * 2) / ratio); | ||
} | ||
context.globalAlpha = 1; | ||
} | ||
@@ -57,2 +125,11 @@ getSidesCount() { | ||
} | ||
async init(container) { | ||
const options = container.actualOptions; | ||
if (!options.preload || !this._engine.loadImage) { | ||
return; | ||
} | ||
for (const imageData of options.preload) { | ||
await this._engine.loadImage(imageData); | ||
} | ||
} | ||
loadShape(particle) { | ||
@@ -62,5 +139,8 @@ if (particle.shape !== "image" && particle.shape !== "images") { | ||
} | ||
const container = particle.container, images = this.getImages(container).images, imageData = particle.shapeData, image = images.find((t) => t.source === imageData.src); | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
const imageData = particle.shapeData, image = this._engine.images.find((t) => t.name === imageData.name || t.source === imageData.src); | ||
if (!image) { | ||
this.loadImageShape(container, imageData).then(() => { | ||
this.loadImageShape(imageData).then(() => { | ||
this.loadShape(particle); | ||
@@ -71,10 +151,13 @@ }); | ||
particleInit(container, particle) { | ||
var _a; | ||
if (particle.shape !== "image" && particle.shape !== "images") { | ||
return; | ||
} | ||
const images = this.getImages(container).images, imageData = particle.shapeData, color = particle.getFillColor(), replaceColor = (_a = imageData.replaceColor) !== null && _a !== void 0 ? _a : imageData.replace_color, image = images.find((t) => t.source === imageData.src); | ||
if (!this._engine.images) { | ||
this._engine.images = []; | ||
} | ||
const images = this._engine.images, imageData = particle.shapeData, color = particle.getFillColor(), image = images.find((t) => t.name === imageData.name || t.source === imageData.src); | ||
if (!image) { | ||
return; | ||
} | ||
const replaceColor = imageData.replaceColor ?? image.replaceColor; | ||
if (image.loading) { | ||
@@ -87,3 +170,2 @@ setTimeout(() => { | ||
(async () => { | ||
var _a, _b; | ||
let imageRes; | ||
@@ -98,4 +180,7 @@ if (image.svgData && color) { | ||
element: image.element, | ||
gif: image.gif, | ||
gifData: image.gifData, | ||
gifLoopCount: image.gifLoopCount, | ||
loaded: true, | ||
ratio: imageData.width / imageData.height, | ||
ratio: imageData.width && imageData.height ? imageData.width / imageData.height : image.ratio ?? 1, | ||
replaceColor: replaceColor, | ||
@@ -108,3 +193,3 @@ source: imageData.src, | ||
} | ||
const fill = (_a = imageData.fill) !== null && _a !== void 0 ? _a : particle.fill, close = (_b = imageData.close) !== null && _b !== void 0 ? _b : particle.close, imageShape = { | ||
const fill = imageData.fill ?? particle.fill, close = imageData.close ?? particle.close, imageShape = { | ||
image: imageRes, | ||
@@ -119,25 +204,4 @@ fill, | ||
} | ||
async loadImageShape(container, imageShape) { | ||
var _a; | ||
const source = imageShape.src; | ||
if (!source) { | ||
throw new Error("Error tsParticles - No image.src"); | ||
} | ||
try { | ||
const image = { | ||
source: source, | ||
type: source.substring(source.length - 3), | ||
error: false, | ||
loading: true, | ||
}; | ||
this.addImage(container, image); | ||
const imageFunc = ((_a = imageShape.replaceColor) !== null && _a !== void 0 ? _a : imageShape.replace_color) ? Utils_1.downloadSvgImage : Utils_1.loadImage; | ||
await imageFunc(image); | ||
} | ||
catch (_b) { | ||
throw new Error(`tsParticles error - ${imageShape.src} not found`); | ||
} | ||
} | ||
} | ||
exports.ImageDrawer = ImageDrawer; | ||
}); |
@@ -7,3 +7,3 @@ (function (factory) { | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "./ImageDrawer"], factory); | ||
define(["require", "exports", "./Utils", "./ImageDrawer", "./ImagePreloader", "@tsparticles/engine"], factory); | ||
} | ||
@@ -14,7 +14,47 @@ })(function (require, exports) { | ||
exports.loadImageShape = void 0; | ||
const Utils_1 = require("./Utils"); | ||
const ImageDrawer_1 = require("./ImageDrawer"); | ||
async function loadImageShape(engine) { | ||
await engine.addShape(["image", "images"], new ImageDrawer_1.ImageDrawer()); | ||
const ImagePreloader_1 = require("./ImagePreloader"); | ||
const engine_1 = require("@tsparticles/engine"); | ||
function addLoadImageToEngine(engine) { | ||
if (engine.loadImage) { | ||
return; | ||
} | ||
engine.loadImage = async (data) => { | ||
if (!data.name && !data.src) { | ||
throw new Error(`${engine_1.errorPrefix} no image source provided`); | ||
} | ||
if (!engine.images) { | ||
engine.images = []; | ||
} | ||
if (engine.images.find((t) => t.name === data.name || t.source === data.src)) { | ||
return; | ||
} | ||
try { | ||
const image = { | ||
gif: data.gif ?? false, | ||
name: data.name ?? data.src, | ||
source: data.src, | ||
type: data.src.substring(data.src.length - 3), | ||
error: false, | ||
loading: true, | ||
replaceColor: data.replaceColor, | ||
ratio: data.width && data.height ? data.width / data.height : undefined, | ||
}; | ||
engine.images.push(image); | ||
const imageFunc = data.gif ? Utils_1.loadGifImage : data.replaceColor ? Utils_1.downloadSvgImage : Utils_1.loadImage; | ||
await imageFunc(image); | ||
} | ||
catch { | ||
throw new Error(`${engine_1.errorPrefix} ${data.name ?? data.src} not found`); | ||
} | ||
}; | ||
} | ||
async function loadImageShape(engine, refresh = true) { | ||
addLoadImageToEngine(engine); | ||
const preloader = new ImagePreloader_1.ImagePreloaderPlugin(engine); | ||
await engine.addPlugin(preloader, refresh); | ||
await engine.addShape(["image", "images"], new ImageDrawer_1.ImageDrawer(engine), refresh); | ||
} | ||
exports.loadImageShape = loadImageShape; | ||
}); |
@@ -7,3 +7,3 @@ (function (factory) { | ||
else if (typeof define === "function" && define.amd) { | ||
define(["require", "exports", "@tsparticles/engine"], factory); | ||
define(["require", "exports", "@tsparticles/engine", "./GifUtils/Utils"], factory); | ||
} | ||
@@ -13,4 +13,5 @@ })(function (require, exports) { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.replaceImageColor = exports.downloadSvgImage = exports.loadImage = void 0; | ||
exports.replaceImageColor = exports.downloadSvgImage = exports.loadGifImage = exports.loadImage = void 0; | ||
const engine_1 = require("@tsparticles/engine"); | ||
const Utils_1 = require("./GifUtils/Utils"); | ||
const currentColorRegex = /(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d.]+%?\))|currentcolor/gi; | ||
@@ -42,3 +43,3 @@ function replaceColorSvg(imageShape, color, opacity) { | ||
image.loading = false; | ||
console.error(`Error tsParticles - loading image: ${image.source}`); | ||
(0, engine_1.getLogger)().error(`${engine_1.errorPrefix} loading image: ${image.source}`); | ||
resolve(); | ||
@@ -50,2 +51,21 @@ }); | ||
exports.loadImage = loadImage; | ||
async function loadGifImage(image) { | ||
if (image.type !== "gif") { | ||
await loadImage(image); | ||
return; | ||
} | ||
image.loading = true; | ||
try { | ||
image.gifData = await (0, Utils_1.decodeGIF)(image.source); | ||
image.gifLoopCount = (0, Utils_1.getGIFLoopAmount)(image.gifData) ?? 0; | ||
if (image.gifLoopCount === 0) { | ||
image.gifLoopCount = Infinity; | ||
} | ||
} | ||
catch { | ||
image.error = true; | ||
} | ||
image.loading = false; | ||
} | ||
exports.loadGifImage = loadGifImage; | ||
async function downloadSvgImage(image) { | ||
@@ -59,6 +79,6 @@ if (image.type !== "svg") { | ||
if (!response.ok) { | ||
console.error("Error tsParticles - Image not found"); | ||
(0, engine_1.getLogger)().error(`${engine_1.errorPrefix} Image not found`); | ||
image.error = true; | ||
} | ||
if (!image.error) { | ||
else { | ||
image.svgData = await response.text(); | ||
@@ -70,9 +90,12 @@ } | ||
function replaceImageColor(image, imageData, color, particle) { | ||
var _a, _b, _c; | ||
const svgColoredData = replaceColorSvg(image, color, (_b = (_a = particle.opacity) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : 1), imageRes = { | ||
const svgColoredData = replaceColorSvg(image, color, particle.opacity?.value ?? 1), imageRes = { | ||
color, | ||
data: Object.assign(Object.assign({}, image), { svgData: svgColoredData }), | ||
gif: imageData.gif, | ||
data: { | ||
...image, | ||
svgData: svgColoredData, | ||
}, | ||
loaded: false, | ||
ratio: imageData.width / imageData.height, | ||
replaceColor: (_c = imageData.replaceColor) !== null && _c !== void 0 ? _c : imageData.replace_color, | ||
replaceColor: imageData.replaceColor, | ||
source: imageData.src, | ||
@@ -90,3 +113,7 @@ }; | ||
domUrl.revokeObjectURL(url); | ||
const img2 = Object.assign(Object.assign({}, image), { error: false, loading: true }); | ||
const img2 = { | ||
...image, | ||
error: false, | ||
loading: true, | ||
}; | ||
await loadImage(img2); | ||
@@ -93,0 +120,0 @@ imageRes.loaded = true; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
464818
97
4482
76
13