UNPKG

tsparticles

Version:

Easily create highly customizable particle 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.

459 lines (458 loc) 18.9 kB
"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.PolygonMaskInstance = void 0; const Enums_1 = require("./Enums"); const Utils_1 = require("../../Utils"); const PolygonMask_1 = require("./Options/Classes/PolygonMask"); function polygonBounce(particle) { particle.velocity.x = particle.velocity.y / 2 - particle.velocity.x; particle.velocity.y = particle.velocity.x / 2 - particle.velocity.y; } function drawPolygonMask(context, rawData, stroke) { const color = Utils_1.ColorUtils.colorToRgb(stroke.color); if (!color) { return; } context.beginPath(); context.moveTo(rawData[0].x, rawData[0].y); for (const item of rawData) { context.lineTo(item.x, item.y); } context.closePath(); context.strokeStyle = Utils_1.ColorUtils.getStyleFromRgb(color); context.lineWidth = stroke.width; context.stroke(); } function drawPolygonMaskPath(context, path, stroke, position) { context.translate(position.x, position.y); const color = Utils_1.ColorUtils.colorToRgb(stroke.color); if (!color) { return; } context.strokeStyle = Utils_1.ColorUtils.getStyleFromRgb(color, stroke.opacity); context.lineWidth = stroke.width; context.stroke(path); } function parsePaths(paths, scale, offset) { const res = []; for (const path of paths) { const segments = path.element.pathSegList; const len = segments.numberOfItems; const p = { x: 0, y: 0, }; for (let i = 0; i < len; i++) { const segment = segments.getItem(i); const svgPathSeg = window.SVGPathSeg; switch (segment.pathSegType) { case svgPathSeg.PATHSEG_MOVETO_ABS: case svgPathSeg.PATHSEG_LINETO_ABS: case svgPathSeg.PATHSEG_CURVETO_CUBIC_ABS: case svgPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: case svgPathSeg.PATHSEG_ARC_ABS: case svgPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: case svgPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: { const absSeg = segment; p.x = absSeg.x; p.y = absSeg.y; break; } case svgPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS: p.x = segment.x; break; case svgPathSeg.PATHSEG_LINETO_VERTICAL_ABS: p.y = segment.y; break; case svgPathSeg.PATHSEG_LINETO_REL: case svgPathSeg.PATHSEG_MOVETO_REL: case svgPathSeg.PATHSEG_CURVETO_CUBIC_REL: case svgPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: case svgPathSeg.PATHSEG_ARC_REL: case svgPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: case svgPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: { const relSeg = segment; p.x += relSeg.x; p.y += relSeg.y; break; } case svgPathSeg.PATHSEG_LINETO_HORIZONTAL_REL: p.x += segment.x; break; case svgPathSeg.PATHSEG_LINETO_VERTICAL_REL: p.y += segment.y; break; case svgPathSeg.PATHSEG_UNKNOWN: case svgPathSeg.PATHSEG_CLOSEPATH: continue; } res.push({ x: p.x * scale + offset.x, y: p.y * scale + offset.y, }); } } return res; } 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; } initAsync(options) { return __awaiter(this, void 0, void 0, function* () { 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) { yield this.initRawData(); } }); } resize() { const container = this.container; const options = this.options; if (!(options.enable && options.type !== Enums_1.Type.none)) { return; } if (this.redrawTimeout) { clearTimeout(this.redrawTimeout); } this.redrawTimeout = window.setTimeout(() => __awaiter(this, void 0, void 0, function* () { yield this.initRawData(true); container.particles.redraw(); }), 250); } stop() { delete this.raw; delete this.paths; } particlesInitialization() { const options = this.options; if (options.enable && options.type === Enums_1.Type.inline && (options.inline.arrangement === Enums_1.InlineArrangement.onePerPoint || options.inline.arrangement === Enums_1.InlineArrangement.perPoint)) { 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 Utils_1.Utils.deepExtend({}, position ? position : this.randomPoint()); } particleBounce(particle) { const options = this.options; if (options.enable && options.type !== Enums_1.Type.none && options.type !== Enums_1.Type.inline) { if (!this.checkInsidePolygon(particle.getPosition())) { polygonBounce(particle); return true; } } else if (options.enable && options.type === Enums_1.Type.inline && particle.initialPosition) { const dist = Utils_1.NumberUtils.getDistance(particle.initialPosition, particle.getPosition()); if (dist > this.polygonMaskMoveRadius) { polygonBounce(particle); return true; } } return false; } clickPositionValid(position) { const options = this.options; return (options.enable && options.type !== Enums_1.Type.none && options.type !== Enums_1.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) { drawPolygonMaskPath(context, path2d, polygonDraw.stroke, this.offset); } else if (rawData) { drawPolygonMask(context, rawData, polygonDraw.stroke); } } } checkInsidePolygon(position) { var _a, _b; const container = this.container; const options = this.options; if (!options.enable || options.type === Enums_1.Type.none || options.type === Enums_1.Type.inline) { return true; } if (!this.raw) { throw new Error(Utils_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 === Enums_1.Type.inside ? inside : options.type === Enums_1.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 parsePaths(this.paths, scale, this.offset); } downloadSvgPath(svgUrl, force) { return __awaiter(this, void 0, void 0, function* () { 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 = yield fetch(url); if (!req.ok) { throw new Error("tsParticles Error - Error occurred during polygon mask download"); } return this.parseSvgPath(yield 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 === Enums_1.Type.inline) { switch (options.inline.arrangement) { case Enums_1.InlineArrangement.randomPoint: position = this.getRandomPoint(); break; case Enums_1.InlineArrangement.randomLength: position = this.getRandomPointByLength(); break; case Enums_1.InlineArrangement.equidistant: position = this.getEquidistantPointByIndex(container.particles.count); break; case Enums_1.InlineArrangement.onePerPoint: case Enums_1.InlineArrangement.perPoint: 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(Utils_1.Constants.noPolygonDataLoaded); } const coords = Utils_1.Utils.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(Utils_1.Constants.noPolygonDataLoaded); } const path = Utils_1.Utils.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(Utils_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(Utils_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(); } } initRawData(force) { return __awaiter(this, void 0, void 0, function* () { const options = this.options; if (options.url) { this.raw = yield 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;