@tsparticles/plugin-canvas-mask
Version:
tsParticles canvas mask plugin
137 lines (136 loc) • 5.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.addParticlesFromCanvasPixels = addParticlesFromCanvasPixels;
exports.getCanvasImageData = getCanvasImageData;
exports.getImageData = getImageData;
exports.getTextData = getTextData;
const engine_1 = require("@tsparticles/engine");
const half = 0.5, origin = {
x: 0,
y: 0,
}, defaultWidth = 0;
function addParticlesFromCanvasPixels(container, data, position, scale, override, filter) {
const { height, width } = data, numPixels = height * width, indexArray = shuffle(range(numPixels)), maxParticles = Math.min(numPixels, container.actualOptions.particles.number.value), canvasSize = container.canvas.size;
let selectedPixels = 0;
const positionOffset = {
x: (canvasSize.width * position.x) / engine_1.percentDenominator - width * scale * half,
y: (canvasSize.height * position.y) / engine_1.percentDenominator - height * scale * half,
};
while (selectedPixels < maxParticles && indexArray.length) {
const defaultIndex = 0, nextIndex = indexArray.pop() ?? defaultIndex, pixelPos = {
x: nextIndex % width,
y: Math.floor(nextIndex / width),
}, pixel = data.pixels[pixelPos.y][pixelPos.x], shouldCreateParticle = filter(pixel);
if (!shouldCreateParticle) {
continue;
}
const pos = {
x: pixelPos.x * scale + positionOffset.x,
y: pixelPos.y * scale + positionOffset.y,
}, pOptions = {};
if (override.color) {
pOptions.color = {
value: pixel,
};
}
if (override.opacity) {
pOptions.opacity = {
value: pixel.a,
};
}
container.particles.addParticle(pos, pOptions);
selectedPixels++;
}
}
function getCanvasImageData(ctx, size, offset, clear = true) {
const imageData = ctx.getImageData(origin.x, origin.y, size.width, size.height).data;
if (clear) {
ctx.clearRect(origin.x, origin.y, size.width, size.height);
}
const pixels = [];
for (let i = 0; i < imageData.length; i += offset) {
const idx = i / offset, pos = {
x: idx % size.width,
y: Math.floor(idx / size.width),
};
if (!pixels[pos.y]) {
pixels[pos.y] = [];
}
const indexesOffset = {
r: 0,
g: 1,
b: 2,
a: 3,
}, alphaFactor = 255;
pixels[pos.y][pos.x] = {
r: imageData[i + indexesOffset.r],
g: imageData[i + indexesOffset.g],
b: imageData[i + indexesOffset.b],
a: imageData[i + indexesOffset.a] / alphaFactor,
};
}
return {
pixels,
width: Math.min(...pixels.map(row => row.length)),
height: pixels.length,
};
}
function getImageData(src, offset) {
const image = new Image();
image.crossOrigin = "Anonymous";
const p = new Promise((resolve, reject) => {
image.onerror = reject;
image.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
const context = canvas.getContext("2d");
if (!context) {
return reject(new Error(`${engine_1.errorPrefix} Could not get canvas context`));
}
context.drawImage(image, origin.x, origin.y, image.width, image.height, origin.x, origin.y, canvas.width, canvas.height);
resolve(getCanvasImageData(context, canvas, offset));
};
});
image.src = src;
return p;
}
function getTextData(textOptions, offset) {
const canvas = document.createElement("canvas"), context = canvas.getContext("2d"), { font, text, lines: linesOptions, color } = textOptions;
if (!text || !context) {
return;
}
const lines = text.split(linesOptions.separator), fontSize = (0, engine_1.isNumber)(font.size) ? `${font.size}px` : font.size, linesData = [];
let maxWidth = 0, totalHeight = 0;
for (const line of lines) {
context.font = `${font.style ?? ""} ${font.variant ?? ""} ${font.weight ?? ""} ${fontSize} ${font.family}`;
const measure = context.measureText(line), lineData = {
measure,
text: line,
height: measure.actualBoundingBoxAscent + measure.actualBoundingBoxDescent,
width: measure.width,
};
maxWidth = Math.max(maxWidth || defaultWidth, lineData.width);
totalHeight += lineData.height + linesOptions.spacing;
linesData.push(lineData);
}
canvas.width = maxWidth;
canvas.height = totalHeight;
let currentHeight = 0;
for (const line of linesData) {
context.font = `${font.style ?? ""} ${font.variant ?? ""} ${font.weight ?? ""} ${fontSize} ${font.family}`;
context.fillStyle = color;
context.fillText(line.text, origin.x, currentHeight + line.measure.actualBoundingBoxAscent);
currentHeight += line.height + linesOptions.spacing;
}
return getCanvasImageData(context, canvas, offset);
}
function shuffle(array) {
const lengthOffset = 1, minIndex = 0;
for (let currentIndex = array.length - lengthOffset; currentIndex >= minIndex; currentIndex--) {
const randomIndex = Math.floor((0, engine_1.getRandom)() * currentIndex);
[array[currentIndex], array[randomIndex]] = [array[randomIndex], array[currentIndex]];
}
return array;
}
const range = (n) => [...Array(n).keys()];