UNPKG

tsparticles

Version:

Easily create highly customizable particle, confetti and fireworks animations and use them as animated backgrounds for your website. Ready to use components available also for React, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Riot.js, Inferno.

397 lines (396 loc) 17.6 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", "./Utils", "../../Utils", "../../Core", "./Options/Classes/PolygonMask"], factory); } })(function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PolygonMaskInstance = void 0; const Utils_1 = require("./Utils"); const Utils_2 = require("../../Utils"); const Core_1 = require("../../Core"); const PolygonMask_1 = require("./Options/Classes/PolygonMask"); class PolygonMaskInstance { constructor(container) { this.container = container; this.dimension = { height: 0, width: 0, }; this.path2DSupported = !!window.Path2D; this.options = new PolygonMask_1.PolygonMask(); this.polygonMaskMoveRadius = this.options.move.radius * container.retina.pixelRatio; } async initAsync(options) { this.options.load(options === null || options === void 0 ? void 0 : options.polygon); const polygonMaskOptions = this.options; this.polygonMaskMoveRadius = polygonMaskOptions.move.radius * this.container.retina.pixelRatio; if (polygonMaskOptions.enable) { await this.initRawData(); } } resize() { const container = this.container; const options = this.options; if (!(options.enable && options.type !== "none")) { return; } if (this.redrawTimeout) { clearTimeout(this.redrawTimeout); } this.redrawTimeout = window.setTimeout(async () => { await this.initRawData(true); await container.particles.redraw(); }, 250); } stop() { delete this.raw; delete this.paths; } particlesInitialization() { const options = this.options; if (options.enable && options.type === "inline" && (options.inline.arrangement === "one-per-point" || options.inline.arrangement === "per-point")) { this.drawPoints(); return true; } return false; } particlePosition(position) { var _a, _b; const options = this.options; if (!(options.enable && ((_b = (_a = this.raw) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0)) { return; } return (0, Utils_2.deepExtend)({}, position ? position : this.randomPoint()); } particleBounce(particle, delta, direction) { return this.polygonBounce(particle, delta, direction); } clickPositionValid(position) { const options = this.options; return (options.enable && options.type !== "none" && options.type !== "inline" && this.checkInsidePolygon(position)); } draw(context) { var _a; if (!((_a = this.paths) === null || _a === void 0 ? void 0 : _a.length)) { return; } const options = this.options; const polygonDraw = options.draw; if (!(options.enable && polygonDraw.enable)) { return; } const rawData = this.raw; for (const path of this.paths) { const path2d = path.path2d; const path2dSupported = this.path2DSupported; if (!context) { continue; } if (path2dSupported && path2d && this.offset) { (0, Utils_1.drawPolygonMaskPath)(context, path2d, polygonDraw.stroke, this.offset); } else if (rawData) { (0, Utils_1.drawPolygonMask)(context, rawData, polygonDraw.stroke); } } } polygonBounce(particle, _delta, direction) { const options = this.options; if (!this.raw || !options.enable || direction !== "top") { return false; } if (options.type === "inside" || options.type === "outside") { let closest, dx, dy; const pos = particle.getPosition(), radius = particle.getRadius(); for (let i = 0, j = this.raw.length - 1; i < this.raw.length; j = i++) { const pi = this.raw[i], pj = this.raw[j]; closest = (0, Utils_1.calcClosestPtOnSegment)(pi, pj, pos); const dist = (0, Utils_2.getDistances)(pos, closest); [dx, dy] = [dist.dx, dist.dy]; if (dist.distance < radius) { (0, Utils_1.segmentBounce)(pi, pj, particle.velocity); return true; } } if (closest && dx !== undefined && dy !== undefined && !this.checkInsidePolygon(pos)) { const factor = { x: 1, y: 1 }; if (particle.position.x >= closest.x) { factor.x = -1; } if (particle.position.y >= closest.y) { factor.y = -1; } particle.position.x = closest.x + radius * 2 * factor.x; particle.position.y = closest.y + radius * 2 * factor.y; particle.velocity.mult(-1); return true; } } else if (options.type === "inline" && particle.initialPosition) { const dist = (0, Utils_2.getDistance)(particle.initialPosition, particle.getPosition()); if (dist > this.polygonMaskMoveRadius) { particle.velocity.x = particle.velocity.y / 2 - particle.velocity.x; particle.velocity.y = particle.velocity.x / 2 - particle.velocity.y; return true; } } return false; } checkInsidePolygon(position) { var _a, _b; const container = this.container; const options = this.options; if (!options.enable || options.type === "none" || options.type === "inline") { return true; } if (!this.raw) { throw new Error(Core_1.Constants.noPolygonFound); } const canvasSize = container.canvas.size; const x = (_a = position === null || position === void 0 ? void 0 : position.x) !== null && _a !== void 0 ? _a : Math.random() * canvasSize.width; const y = (_b = position === null || position === void 0 ? void 0 : position.y) !== null && _b !== void 0 ? _b : Math.random() * canvasSize.height; let inside = false; for (let i = 0, j = this.raw.length - 1; i < this.raw.length; j = i++) { const pi = this.raw[i]; const pj = this.raw[j]; const intersect = pi.y > y !== pj.y > y && x < ((pj.x - pi.x) * (y - pi.y)) / (pj.y - pi.y) + pi.x; if (intersect) { inside = !inside; } } return options.type === "inside" ? inside : options.type === "outside" ? !inside : false; } parseSvgPath(xml, force) { var _a, _b, _c; const forceDownload = force !== null && force !== void 0 ? force : false; if (this.paths !== undefined && !forceDownload) { return this.raw; } const container = this.container; const options = this.options; const parser = new DOMParser(); const doc = parser.parseFromString(xml, "image/svg+xml"); const svg = doc.getElementsByTagName("svg")[0]; let svgPaths = svg.getElementsByTagName("path"); if (!svgPaths.length) { svgPaths = doc.getElementsByTagName("path"); } this.paths = []; for (let i = 0; i < svgPaths.length; i++) { const path = svgPaths.item(i); if (path) { this.paths.push({ element: path, length: path.getTotalLength(), }); } } const pxRatio = container.retina.pixelRatio; const scale = options.scale / pxRatio; this.dimension.width = parseFloat((_a = svg.getAttribute("width")) !== null && _a !== void 0 ? _a : "0") * scale; this.dimension.height = parseFloat((_b = svg.getAttribute("height")) !== null && _b !== void 0 ? _b : "0") * scale; const position = (_c = options.position) !== null && _c !== void 0 ? _c : { x: 50, y: 50, }; this.offset = { x: (container.canvas.size.width * position.x) / (100 * pxRatio) - this.dimension.width / 2, y: (container.canvas.size.height * position.y) / (100 * pxRatio) - this.dimension.height / 2, }; return (0, Utils_1.parsePaths)(this.paths, scale, this.offset); } async downloadSvgPath(svgUrl, force) { const options = this.options; const url = svgUrl || options.url; const forceDownload = force !== null && force !== void 0 ? force : false; if (!url || (this.paths !== undefined && !forceDownload)) { return this.raw; } const req = await fetch(url); if (!req.ok) { throw new Error("tsParticles Error - Error occurred during polygon mask download"); } return this.parseSvgPath(await req.text(), force); } drawPoints() { if (!this.raw) { return; } for (const item of this.raw) { this.container.particles.addParticle({ x: item.x, y: item.y, }); } } randomPoint() { const container = this.container; const options = this.options; let position; if (options.type === "inline") { switch (options.inline.arrangement) { case "random-point": position = this.getRandomPoint(); break; case "random-length": position = this.getRandomPointByLength(); break; case "equidistant": position = this.getEquidistantPointByIndex(container.particles.count); break; case "one-per-point": case "per-point": default: position = this.getPointByIndex(container.particles.count); } } else { position = { x: Math.random() * container.canvas.size.width, y: Math.random() * container.canvas.size.height, }; } if (this.checkInsidePolygon(position)) { return position; } else { return this.randomPoint(); } } getRandomPoint() { if (!this.raw || !this.raw.length) { throw new Error(Core_1.Constants.noPolygonDataLoaded); } const coords = (0, Utils_2.itemFromArray)(this.raw); return { x: coords.x, y: coords.y, }; } getRandomPointByLength() { var _a, _b, _c; const options = this.options; if (!this.raw || !this.raw.length || !((_a = this.paths) === null || _a === void 0 ? void 0 : _a.length)) { throw new Error(Core_1.Constants.noPolygonDataLoaded); } const path = (0, Utils_2.itemFromArray)(this.paths); const distance = Math.floor(Math.random() * path.length) + 1; const point = path.element.getPointAtLength(distance); return { x: point.x * options.scale + (((_b = this.offset) === null || _b === void 0 ? void 0 : _b.x) || 0), y: point.y * options.scale + (((_c = this.offset) === null || _c === void 0 ? void 0 : _c.y) || 0), }; } getEquidistantPointByIndex(index) { var _a, _b, _c, _d, _e, _f, _g; const options = this.container.actualOptions; const polygonMaskOptions = this.options; if (!this.raw || !this.raw.length || !((_a = this.paths) === null || _a === void 0 ? void 0 : _a.length)) throw new Error(Core_1.Constants.noPolygonDataLoaded); let offset = 0; let point; const totalLength = this.paths.reduce((tot, path) => tot + path.length, 0); const distance = totalLength / options.particles.number.value; for (const path of this.paths) { const pathDistance = distance * index - offset; if (pathDistance <= path.length) { point = path.element.getPointAtLength(pathDistance); break; } else { offset += path.length; } } return { x: ((_b = point === null || point === void 0 ? void 0 : point.x) !== null && _b !== void 0 ? _b : 0) * polygonMaskOptions.scale + ((_d = (_c = this.offset) === null || _c === void 0 ? void 0 : _c.x) !== null && _d !== void 0 ? _d : 0), y: ((_e = point === null || point === void 0 ? void 0 : point.y) !== null && _e !== void 0 ? _e : 0) * polygonMaskOptions.scale + ((_g = (_f = this.offset) === null || _f === void 0 ? void 0 : _f.y) !== null && _g !== void 0 ? _g : 0), }; } getPointByIndex(index) { if (!this.raw || !this.raw.length) { throw new Error(Core_1.Constants.noPolygonDataLoaded); } const coords = this.raw[index % this.raw.length]; return { x: coords.x, y: coords.y, }; } createPath2D() { var _a, _b; const options = this.options; if (!this.path2DSupported || !((_a = this.paths) === null || _a === void 0 ? void 0 : _a.length)) { return; } for (const path of this.paths) { const pathData = (_b = path.element) === null || _b === void 0 ? void 0 : _b.getAttribute("d"); if (pathData) { const path2d = new Path2D(pathData); const matrix = document.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGMatrix(); const finalPath = new Path2D(); const transform = matrix.scale(options.scale); if (finalPath.addPath) { finalPath.addPath(path2d, transform); path.path2d = finalPath; } else { delete path.path2d; } } else { delete path.path2d; } if (path.path2d || !this.raw) { continue; } path.path2d = new Path2D(); path.path2d.moveTo(this.raw[0].x, this.raw[0].y); this.raw.forEach((pos, i) => { var _a; if (i > 0) { (_a = path.path2d) === null || _a === void 0 ? void 0 : _a.lineTo(pos.x, pos.y); } }); path.path2d.closePath(); } } async initRawData(force) { const options = this.options; if (options.url) { this.raw = await this.downloadSvgPath(options.url, force); } else if (options.data) { const data = options.data; let svg; if (typeof data !== "string") { const path = data.path instanceof Array ? data.path.map((t) => `<path d="${t}" />`).join("") : `<path d="${data.path}" />`; const namespaces = 'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"'; svg = `<svg ${namespaces} width="${data.size.width}" height="${data.size.height}">${path}</svg>`; } else { svg = data; } this.raw = this.parseSvgPath(svg, force); } this.createPath2D(); } } exports.PolygonMaskInstance = PolygonMaskInstance; });