UNPKG

@tsparticles/plugin-canvas-mask

Version:

tsParticles canvas mask plugin

147 lines (146 loc) 6.51 kB
(function (factory) { if (typeof module === "object" && typeof module.exports === "object") { var v = factory(require, exports); if (v !== undefined) module.exports = v; } else if (typeof define === "function" && define.amd) { define(["require", "exports", "@tsparticles/engine"], factory); } })(function (require, exports) { "use strict"; 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()]; });