diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2545065 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,30 @@ +# Change log +## 1.1.0 + +- shapes and images can now be used together in an array to mix basic shapes and custom images; + ```js + let mySparticles = new Sparticles( el, { + + shape: ["circle", "image"], + imageUrl: "./snowflake.png" + + }) + ``` + +- fixed some performance / looping errors +- refactored/renamed a lot of prototype methods + +- added the ability to write custom shapes in the off-chance that's + better/easier than using a custom svg-image + ```js + // first make sure the Sparticles is ready in page. + // then you can add a custom offScreenCanvas before initialising. + Sparticles.prototype.offScreenCanvas.doge = function(style, color, canvas) { + // code for custom shape here, access to "this" object + // which is the Sparticles prototype. + }; + // then initialise your sparticles instance with the custom shape + let mySparticles = new Sparticles( el, { shape: "doge" }); + + + ``` diff --git a/README.md b/README.md index d15e34e..ecce24e 100644 --- a/README.md +++ b/README.md @@ -178,16 +178,16 @@ option | type | default **[maxAlpha](#maxAlpha)** | `Number` | `1` | maximum alpha value of every particle **[minSize](#minSize)** | `Number` | `1` | minimum size of every particle **[maxSize](#maxSize)** | `Number` | `10` | maximum size of every particle -**[style](#style)** | `String` | `fill` | fill style of particles (one of; "fill", "stroke" or "both") **[bounce](#bounce)** | `Boolean` | `false` | should the particles bounce off edge of canvas **[drift](#drift)** | `Number` | `1` | the "driftiness" of particles which have a horizontal/vertical direction **[glow](#glow)** | `Number` | `0` | the glow effect size of each particle **[twinkle](#twinkle)** | `Boolean` | `false` | particles to exhibit an alternative alpha transition as "twinkling" +**[style](#style)** | `String` | `fill` | fill style of particles (one of; "fill", "stroke" or "both") +**[shape](#shape)** | `String`/`Array` | `circle` | shape of particles (any of; circle, square, triangle, diamond, line, image) or "random" **[color](#color)** | `String`/`Array` | `random` | css color as string, or array of color strings (can also be "random") **[randomColor](#randomColor)** | `Function` | `randomHsl()` | function for returning a random color when the color is set as "random" **[randomColorCount](#randomColorCount)** | `Function` | `randomHsl()` | function for returning a random color when the color is set as "random" -**[shape](#shape)** | `String`/`Array` | `circle` | shape of particles (any of; circle, square, triangle, diamond, line, image) or "random" -**[imageUrl](#imageUrl)** | `String`/`Array` | | if shape is "image", define an image url (can be data-uri, should be square (1:1 ratio)) +**[imageUrl](#imageUrl)** | `String`/`Array` | | if shape is "image", define an image url (can be data-uri, **should be square (1:1 ratio)**) --- @@ -411,8 +411,9 @@ particle shape from an image when combined with `imageUrl`](#imageUrl). Determine the custom image to be used for all the particles. If an array of urls (`[ "http://my.image/shape.png", "http://my.svg/shape.svg" ]`) is given, then each particle will be assigned a random image as it's shape from the array. +**This image should be a square (1:1)** -This only applies [if the `shape` is set to `"image"`](#shape). +`imageUrl` only has an effect [if a `shape` in the array is; `"image"`](#shape). # methods diff --git a/dist/sparticles.esm.js b/dist/sparticles.esm.js index 44d0142..26b9982 100644 --- a/dist/sparticles.esm.js +++ b/dist/sparticles.esm.js @@ -1,6 +1,6 @@ /**! * Sparticles - Lightweight, High Performance Particles in Canvas - * @version 1.0.3 + * @version 1.1.0 * @license MPL-2.0 * @author simeydotme * @website http://sparticlesjs.dev @@ -209,8 +209,11 @@ var round = function round(value) { var Sparticle = function Sparticle(parent) { if (parent) { this.canvas = parent.canvas; - this.images = parent.images; this.settings = parent.settings; + this.colors = parent.colors; + this.shapes = parent.shapes; + this.images = parent.images; + this.styles = parent.styles; this.ctx = parent.canvas.getContext("2d"); this.setup(); this.init(); @@ -236,9 +239,10 @@ Sparticle.prototype.setup = function () { this.dy = this.getDeltaY(); this.dd = this.getDriftDelta(); this.dr = this.getRotationDelta(); - this.shape = this.getShapeOrImage(); - this.style = this.getStyle(); this.color = this.getColor(); + this.shape = this.getShape(); + this.image = this.getImage(); + this.style = this.getStyle(); this.rotation = _.rotate ? radian(random(0, 360)) : 0; this.vertical = _.direction > 150 && _.direction < 210 || _.direction > 330 && _.direction < 390 || _.direction > -30 && _.direction < 30; this.horizontal = _.direction > 60 && _.direction < 120 || _.direction > 240 && _.direction < 300; @@ -351,27 +355,42 @@ Sparticle.prototype.isTouchingEdge = function () { Sparticle.prototype.getColor = function () { - if (Array.isArray(this.settings.color)) { + if (this.settings.color === "random") { + return randomArray(this.colors); + } else if (Array.isArray(this.settings.color)) { return randomArray(this.settings.color); + } else { + return this.settings.color; } }; /** - * get a random shape or image for the particle from the - * array of shapes set in the options object, or the array - * of images, if the shape is set to "image" - * @returns {String} - random shape or image from shape or image array + * get a random shape for the particle from the + * array of shapes set in the options object + * @returns {String} - random shape from shape array */ -Sparticle.prototype.getShapeOrImage = function () { - var shape = this.settings.shape; +Sparticle.prototype.getShape = function () { + if (this.settings.shape === "random") { + return randomArray(this.shapes); + } else if (Array.isArray(this.settings.shape)) { + return randomArray(this.settings.shape); + } else { + return this.settings.shape; + } +}; +/** + * get the image for the particle from the array + * of possible image urls + * @returns {String} - random imageUrl from imageUrl array + */ + - if (Array.isArray(shape)) { - if (shape[0] === "image" && this.images) { - return randomArray(this.images); - } else { - return randomArray(shape); - } +Sparticle.prototype.getImage = function () { + if (Array.isArray(this.settings.imageUrl)) { + return randomArray(this.settings.imageUrl); + } else { + return this.settings.imageUrl; } }; /** @@ -382,7 +401,7 @@ Sparticle.prototype.getShapeOrImage = function () { Sparticle.prototype.getStyle = function () { - return randomArray(this.settings.style); + return randomArray(this.styles); }; /** * get a random delta (velocity) for the particle @@ -646,10 +665,12 @@ Sparticle.prototype.updateDrift = function () { }; Sparticle.prototype.render = function (canvasses) { - var particleCanvas = canvasses[this.color][this.shape]; + var particleCanvas; - if (this.settings.shape[0] !== "image") { + if (this.shape !== "image") { particleCanvas = canvasses[this.color][this.shape][this.style]; + } else { + particleCanvas = canvasses[this.color][this.shape][this.image]; } var canvasSize = particleCanvas.width; @@ -694,16 +715,16 @@ Sparticle.prototype.renderRotate = function () { * @param {Number} [options.maxAlpha=1] - maximum alpha value of every particle * @param {Number} [options.minSize=1] - minimum size of every particle * @param {Number} [options.maxSize=10] - maximum size of every particle - * @param {String} [options.style=fill] - fill style of particles (one of; "fill", "stroke" or "both") * @param {Boolean} [options.bounce=false] - should the particles bounce off edge of canvas * @param {Number} [options.drift=1] - the "driftiness" of particles which have a horizontal/vertical direction * @param {Number} [options.glow=0] - the glow effect size of each particle * @param {Boolean} [options.twinkle=false] - particles to exhibit an alternative alpha transition as "twinkling" + * @param {String} [options.style=fill] - fill style of particles (one of; "fill", "stroke" or "both") + * @param {(String|String[])} [options.shape=circle] - shape of particles (any of; circle, square, triangle, diamond, line, image) or "random" + * @param {(String|String[])} [options.imageUrl=] - if shape is "image", define an image url (can be data-uri, must be square (1:1 ratio)) * @param {(String|String[])} [options.color=random] - css color as string, or array of color strings (can also be "random") * @param {Function} [options.randomColor=randomHsl(index,total)] - a custom function for setting the random colors when color="random" * @param {Number} [options.randomColorCount=3] - the number of random colors to generate when color is "random" - * @param {(String|String[])} [options.shape=circle] - shape of particles (any of; circle, square, triangle, diamond, line, image) or "random" - * @param {(String|String[])} [options.imageUrl=] - if shape is "image", define an image url (can be data-uri, must be square (1:1 ratio)) * @param {Number} [width] - the width of the canvas element * @param {Number} [height=width] - the height of the canvas element * @returns {Object} - reference to a new Sparticles instance @@ -762,9 +783,10 @@ var Sparticles = function Sparticles(node, options, width, height) { var _this = this; this.sparticles = []; - this.createColorArray(); - this.createShapeArray(); - this.createStyleArray(); + this.colors = this.getColorArray(); + this.shapes = this.getShapeArray(); + this.styles = this.getStyleArray(); + this.imageUrls = this.getImageArray(); this.setupMainCanvas(); this.setupOffscreenCanvasses(function () { _this.createSparticles(); @@ -897,27 +919,19 @@ var Sparticles = function Sparticles(node, options, width, height) { */ -Sparticles.prototype.createColorArray = function () { - var _ = this.settings; - var isArray = Array.isArray(_.color); - var isRandom = false; - - if (!isArray) { - _.color = [_.color]; - } - - isRandom = _.color.some(function (c) { +Sparticles.prototype.getColorArray = function () { + var colors = Array.isArray(this.settings.color) ? this.settings.color : [this.settings.color]; + var isRandom = colors.some(function (c) { return c === "random"; }); if (isRandom) { - // it would be silly to have an array of too many colours. - for (var i = 0; i < _.randomColorCount; i++) { - _.color[i] = _.randomColor(i, _.randomColorCount); + for (var i = 0; i < this.settings.randomColorCount; i++) { + colors[i] = this.settings.randomColor(i, this.settings.randomColorCount); } } - return _.color; + return colors; }; /** * convert the input shape to an array if it isn't already @@ -925,24 +939,26 @@ Sparticles.prototype.createColorArray = function () { */ -Sparticles.prototype.createShapeArray = function () { - var _ = this.settings; - var isArray = Array.isArray(_.shape); - var isRandom = false; - - if (!isArray) { - _.shape = [_.shape]; - } - - isRandom = _.shape.some(function (c) { +Sparticles.prototype.getShapeArray = function () { + var shapes = Array.isArray(this.settings.shape) ? this.settings.shape : [this.settings.shape]; + var isRandom = shapes.some(function (c) { return c === "random"; }); if (isRandom) { - _.shape = ["square", "circle", "star", "diamond"]; + shapes = ["square", "circle", "triangle"]; } - return _.shape; + return shapes; +}; +/** + * convert the imageUrl option to an array if it isn't already + * @returns {Array} - array of image urls for use in rendering + */ + + +Sparticles.prototype.getImageArray = function () { + return Array.isArray(this.settings.imageUrl) ? this.settings.imageUrl : [this.settings.imageUrl]; }; /** * convert the input style to an array @@ -950,14 +966,16 @@ Sparticles.prototype.createShapeArray = function () { */ -Sparticles.prototype.createStyleArray = function () { - if (this.settings.style !== "fill" && this.settings.style !== "stroke") { - this.settings.style = ["fill", "stroke"]; +Sparticles.prototype.getStyleArray = function () { + var styles = this.settings.style; + + if (styles !== "fill" && styles !== "stroke") { + styles = ["fill", "stroke"]; } else { - this.settings.style = [this.settings.style]; + styles = [styles]; } - return this.settings.style; + return styles; }; /** * set up the canvas and bind to a property for @@ -986,62 +1004,64 @@ Sparticles.prototype.setupMainCanvas = function () { Sparticles.prototype.setupOffscreenCanvasses = function (callback) { var _this3 = this; + var colors = this.colors.filter(function (item, index) { + return _this3.colors.indexOf(item) === index; + }); + var shapes = this.shapes.filter(function (item, index) { + return _this3.shapes.indexOf(item) === index; + }); + var styles = this.styles.filter(function (item, index) { + return _this3.styles.indexOf(item) === index; + }); + var imageUrls = this.imageUrls.filter(function (item, index) { + return _this3.imageUrls.indexOf(item) === index; + }); + var imageCount = colors.length * imageUrls.length; + var canvasCount = colors.length * shapes.length * styles.length; + var imagesLoaded = 0; + var canvassesCreated = 0; this.canvasses = this.canvasses || {}; - this.settings.color.forEach(function (color) { + colors.forEach(function (color) { _this3.canvasses[color] = _this3.canvasses[color] || {}; + shapes.forEach(function (shape) { + _this3.canvasses[color][shape] = _this3.canvasses[color][shape] || {}; - if (_this3.settings.shape[0] === "image") { - _this3.loadAndDrawImages(color, callback); - } else { - _this3.settings.shape.forEach(function (shape) { - _this3.canvasses[color][shape] = _this3.canvasses[color][shape] || {}; - - _this3.settings.style.forEach(function (style) { - _this3.canvasses[color][shape][style] = document.createElement("canvas"); - var canvas = _this3.canvasses[color][shape][style]; - var ctx = canvas.getContext("2d"); - - switch (shape) { - case "square": - _this3.drawOffscreenCanvasForSquare(canvas, ctx, color, style); - - if (callback) callback(); - break; + if (shape === "image") { + imageUrls.forEach(function (imageUrl, i) { + var image = new Image(); + var imageCanvas = document.createElement("canvas"); + _this3.canvasses[color][shape][imageUrl] = imageCanvas; - case "line": - _this3.drawOffscreenCanvasForLine(canvas, ctx, color, style); + image.onload = function () { + imagesLoaded++; - if (callback) callback(); - break; + _this3.drawOffscreenCanvasForImage(image, color, imageCanvas); - case "triangle": - _this3.drawOffscreenCanvasForTriangle(canvas, ctx, color, style); + if (callback && imagesLoaded === imageCount) { + callback(); + } + }; - if (callback) callback(); - break; + image.onerror = function () { + console.error("failed to load source image: ", imageUrl); + }; - case "diamond": - _this3.drawOffscreenCanvasForDiamond(canvas, ctx, color, style); - - if (callback) callback(); - break; - - case "star": - _this3.drawOffscreenCanvasForStar(canvas, ctx, color, style); - - if (callback) callback(); - break; + image.src = imageUrl; + }); + } else { + styles.forEach(function (style) { + var canvas = document.createElement("canvas"); + _this3.canvasses[color][shape][style] = canvas; + canvassesCreated++; - case "circle": - default: - _this3.drawOffscreenCanvasForCircle(canvas, ctx, color, style); + _this3.drawOffscreenCanvas(shape, style, color, canvas); - if (callback) callback(); - break; + if (callback && canvassesCreated === canvasCount) { + callback(); } }); - }); - } + } + }); }); }; /** @@ -1109,17 +1129,61 @@ Sparticles.prototype.renderColor = function (ctx, style) { } }; /** - * create, setup and render an offscreen canvas for a - * Square Particle of the given color + * pass-through the needed parameters to the offscreen canvas + * draw function associated with the given shape + * @param {String} shape - shape of the canvas to draw (eg: "circle") + * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context + * @returns {HTMLCanvasElement} - the created offscreen canvas + */ + + +Sparticles.prototype.drawOffscreenCanvas = function (shape, style, color, canvas) { + return this.offScreenCanvas[shape].call(this, style, color, canvas); +}; +/** + * object of shapes to draw + */ + + +Sparticles.prototype.offScreenCanvas = {}; +/** + * create, setup and render an offscreen canvas for a + * Circle Particle of the given color + * @param {String} style - style (either "fill" or "stroke") * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element + * @returns {HTMLCanvasElement} - the created offscreen canvas + */ + +Sparticles.prototype.offScreenCanvas.circle = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); + var size = this.settings.maxSize; + var lineSize = this.getLineSize(size); + var glowSize = this.getGlowSize(size); + var canvasSize = size + lineSize + glowSize; + canvas.width = canvasSize; + canvas.height = canvasSize; + this.renderGlow(ctx, color, size); + this.renderStyle(ctx, color, lineSize, style); + ctx.beginPath(); + ctx.ellipse(canvasSize / 2, canvasSize / 2, size / 2, size / 2, 0, 0, 360); + this.renderColor(ctx, style); + return canvas; +}; +/** + * create, setup and render an offscreen canvas for a + * Square Particle of the given color * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawOffscreenCanvasForSquare = function (canvas, ctx, color, style) { +Sparticles.prototype.offScreenCanvas.square = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); var size = this.settings.maxSize; var lineSize = this.getLineSize(size); var glowSize = this.getGlowSize(size); @@ -1136,15 +1200,15 @@ Sparticles.prototype.drawOffscreenCanvasForSquare = function (canvas, ctx, color /** * create, setup and render an offscreen canvas for a * Line/Curve Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawOffscreenCanvasForLine = function (canvas, ctx, color, style) { +Sparticles.prototype.offScreenCanvas.line = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); var size = this.settings.maxSize * 2; var lineSize = this.getLineSize(size); var glowSize = this.getGlowSize(size); @@ -1166,15 +1230,15 @@ Sparticles.prototype.drawOffscreenCanvasForLine = function (canvas, ctx, color, /** * create, setup and render an offscreen canvas for a * Triangle Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawOffscreenCanvasForTriangle = function (canvas, ctx, color, style) { +Sparticles.prototype.offScreenCanvas.triangle = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); var size = this.settings.maxSize; var lineSize = this.getLineSize(size); var glowSize = this.getGlowSize(size); @@ -1197,15 +1261,15 @@ Sparticles.prototype.drawOffscreenCanvasForTriangle = function (canvas, ctx, col /** * create, setup and render an offscreen canvas for a * Diamond Sparkle Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawOffscreenCanvasForDiamond = function (canvas, ctx, color, style) { +Sparticles.prototype.offScreenCanvas.diamond = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); var size = this.settings.maxSize; var half = size / 2; var lineSize = this.getLineSize(size); @@ -1233,15 +1297,15 @@ Sparticles.prototype.drawOffscreenCanvasForDiamond = function (canvas, ctx, colo /** * create, setup and render an offscreen canvas for a * Star Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawOffscreenCanvasForStar = function (canvas, ctx, color, style) { +Sparticles.prototype.offScreenCanvas.star = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); var size = 52; var lineSize = this.getLineSize(size); var glowSize = this.getGlowSize(size); @@ -1297,85 +1361,19 @@ Sparticles.prototype.drawOffscreenCanvasForStar = function (canvas, ctx, color, this.renderColor(ctx, style); return canvas; }; -/** - * create, setup and render an offscreen canvas for a - * Circle Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with - * @param {String} style - style (either "fill" or "stroke") - * @returns {HTMLCanvasElement} - the created offscreen canvas - */ - - -Sparticles.prototype.drawOffscreenCanvasForCircle = function (canvas, ctx, color, style) { - var size = this.settings.maxSize; - var lineSize = this.getLineSize(size); - var glowSize = this.getGlowSize(size); - var canvasSize = size + lineSize + glowSize; - canvas.width = canvasSize; - canvas.height = canvasSize; - this.renderGlow(ctx, color, size); - this.renderStyle(ctx, color, lineSize, style); - ctx.beginPath(); - ctx.ellipse(canvasSize / 2, canvasSize / 2, size / 2, size / 2, 0, 0, 360); - this.renderColor(ctx, style); - return canvas; -}; -/** - * set up the needed array for referencing the images in the Sparticle() - * instance, then loop through each image and load it before running the callback - * @param {String} color - the color of the image that we're loading - * @param {Function} callback - callback function to run after images load - */ - - -Sparticles.prototype.loadAndDrawImages = function (color, callback) { - var _this4 = this; - - var imgUrls = this.settings.imageUrl; - var imageUrls = Array.isArray(imgUrls) ? imgUrls : [imgUrls]; - var imageCount = imageUrls.length; - var imagesLoaded = 0; - this.images = []; - imageUrls.forEach(function (imageUrl, i) { - _this4.images.push("image" + i); - - _this4.canvasses[color]["image" + i] = document.createElement("canvas"); - var canvas = _this4.canvasses[color]["image" + i]; - var ctx = canvas.getContext("2d"); - var image = new Image(); - - image.onload = function () { - imagesLoaded++; - - _this4.drawImageOffscreenCanvas(image, canvas, ctx, color); - - if (callback && imagesLoaded === imageCount) { - callback(); - } - }; - - image.onerror = function () { - console.error("failed to load source image: ", imageUrl); - }; - - image.src = imageUrl; - }); -}; /** * create, setup and render an offscreen canvas for a * Custom Image Particle of the given color * @param {HTMLImageElement} image - the image element that has loaded - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawImageOffscreenCanvas = function (image, canvas, ctx, color) { +Sparticles.prototype.drawOffscreenCanvasForImage = function (image, color, canvas) { var size = image.width; + var ctx = canvas.getContext("2d"); canvas.width = size; canvas.height = size; ctx.drawImage(image, 0, 0, size, size, 0, 0, size, size); diff --git a/dist/sparticles.js b/dist/sparticles.js index 2daab26..a6d3021 100644 --- a/dist/sparticles.js +++ b/dist/sparticles.js @@ -1,6 +1,6 @@ /**! * Sparticles - Lightweight, High Performance Particles in Canvas - * @version 1.0.3 + * @version 1.1.0 * @license MPL-2.0 * @author simeydotme * @website http://sparticlesjs.dev @@ -212,8 +212,11 @@ var Sparticles = (function () { var Sparticle = function Sparticle(parent) { if (parent) { this.canvas = parent.canvas; - this.images = parent.images; this.settings = parent.settings; + this.colors = parent.colors; + this.shapes = parent.shapes; + this.images = parent.images; + this.styles = parent.styles; this.ctx = parent.canvas.getContext("2d"); this.setup(); this.init(); @@ -239,9 +242,10 @@ var Sparticles = (function () { this.dy = this.getDeltaY(); this.dd = this.getDriftDelta(); this.dr = this.getRotationDelta(); - this.shape = this.getShapeOrImage(); - this.style = this.getStyle(); this.color = this.getColor(); + this.shape = this.getShape(); + this.image = this.getImage(); + this.style = this.getStyle(); this.rotation = _.rotate ? radian(random(0, 360)) : 0; this.vertical = _.direction > 150 && _.direction < 210 || _.direction > 330 && _.direction < 390 || _.direction > -30 && _.direction < 30; this.horizontal = _.direction > 60 && _.direction < 120 || _.direction > 240 && _.direction < 300; @@ -354,27 +358,42 @@ var Sparticles = (function () { Sparticle.prototype.getColor = function () { - if (Array.isArray(this.settings.color)) { + if (this.settings.color === "random") { + return randomArray(this.colors); + } else if (Array.isArray(this.settings.color)) { return randomArray(this.settings.color); + } else { + return this.settings.color; } }; /** - * get a random shape or image for the particle from the - * array of shapes set in the options object, or the array - * of images, if the shape is set to "image" - * @returns {String} - random shape or image from shape or image array + * get a random shape for the particle from the + * array of shapes set in the options object + * @returns {String} - random shape from shape array */ - Sparticle.prototype.getShapeOrImage = function () { - var shape = this.settings.shape; + Sparticle.prototype.getShape = function () { + if (this.settings.shape === "random") { + return randomArray(this.shapes); + } else if (Array.isArray(this.settings.shape)) { + return randomArray(this.settings.shape); + } else { + return this.settings.shape; + } + }; + /** + * get the image for the particle from the array + * of possible image urls + * @returns {String} - random imageUrl from imageUrl array + */ + - if (Array.isArray(shape)) { - if (shape[0] === "image" && this.images) { - return randomArray(this.images); - } else { - return randomArray(shape); - } + Sparticle.prototype.getImage = function () { + if (Array.isArray(this.settings.imageUrl)) { + return randomArray(this.settings.imageUrl); + } else { + return this.settings.imageUrl; } }; /** @@ -385,7 +404,7 @@ var Sparticles = (function () { Sparticle.prototype.getStyle = function () { - return randomArray(this.settings.style); + return randomArray(this.styles); }; /** * get a random delta (velocity) for the particle @@ -649,10 +668,12 @@ var Sparticles = (function () { }; Sparticle.prototype.render = function (canvasses) { - var particleCanvas = canvasses[this.color][this.shape]; + var particleCanvas; - if (this.settings.shape[0] !== "image") { + if (this.shape !== "image") { particleCanvas = canvasses[this.color][this.shape][this.style]; + } else { + particleCanvas = canvasses[this.color][this.shape][this.image]; } var canvasSize = particleCanvas.width; @@ -697,16 +718,16 @@ var Sparticles = (function () { * @param {Number} [options.maxAlpha=1] - maximum alpha value of every particle * @param {Number} [options.minSize=1] - minimum size of every particle * @param {Number} [options.maxSize=10] - maximum size of every particle - * @param {String} [options.style=fill] - fill style of particles (one of; "fill", "stroke" or "both") * @param {Boolean} [options.bounce=false] - should the particles bounce off edge of canvas * @param {Number} [options.drift=1] - the "driftiness" of particles which have a horizontal/vertical direction * @param {Number} [options.glow=0] - the glow effect size of each particle * @param {Boolean} [options.twinkle=false] - particles to exhibit an alternative alpha transition as "twinkling" + * @param {String} [options.style=fill] - fill style of particles (one of; "fill", "stroke" or "both") + * @param {(String|String[])} [options.shape=circle] - shape of particles (any of; circle, square, triangle, diamond, line, image) or "random" + * @param {(String|String[])} [options.imageUrl=] - if shape is "image", define an image url (can be data-uri, must be square (1:1 ratio)) * @param {(String|String[])} [options.color=random] - css color as string, or array of color strings (can also be "random") * @param {Function} [options.randomColor=randomHsl(index,total)] - a custom function for setting the random colors when color="random" * @param {Number} [options.randomColorCount=3] - the number of random colors to generate when color is "random" - * @param {(String|String[])} [options.shape=circle] - shape of particles (any of; circle, square, triangle, diamond, line, image) or "random" - * @param {(String|String[])} [options.imageUrl=] - if shape is "image", define an image url (can be data-uri, must be square (1:1 ratio)) * @param {Number} [width] - the width of the canvas element * @param {Number} [height=width] - the height of the canvas element * @returns {Object} - reference to a new Sparticles instance @@ -765,9 +786,10 @@ var Sparticles = (function () { var _this = this; this.sparticles = []; - this.createColorArray(); - this.createShapeArray(); - this.createStyleArray(); + this.colors = this.getColorArray(); + this.shapes = this.getShapeArray(); + this.styles = this.getStyleArray(); + this.imageUrls = this.getImageArray(); this.setupMainCanvas(); this.setupOffscreenCanvasses(function () { _this.createSparticles(); @@ -900,27 +922,19 @@ var Sparticles = (function () { */ - Sparticles.prototype.createColorArray = function () { - var _ = this.settings; - var isArray = Array.isArray(_.color); - var isRandom = false; - - if (!isArray) { - _.color = [_.color]; - } - - isRandom = _.color.some(function (c) { + Sparticles.prototype.getColorArray = function () { + var colors = Array.isArray(this.settings.color) ? this.settings.color : [this.settings.color]; + var isRandom = colors.some(function (c) { return c === "random"; }); if (isRandom) { - // it would be silly to have an array of too many colours. - for (var i = 0; i < _.randomColorCount; i++) { - _.color[i] = _.randomColor(i, _.randomColorCount); + for (var i = 0; i < this.settings.randomColorCount; i++) { + colors[i] = this.settings.randomColor(i, this.settings.randomColorCount); } } - return _.color; + return colors; }; /** * convert the input shape to an array if it isn't already @@ -928,24 +942,26 @@ var Sparticles = (function () { */ - Sparticles.prototype.createShapeArray = function () { - var _ = this.settings; - var isArray = Array.isArray(_.shape); - var isRandom = false; - - if (!isArray) { - _.shape = [_.shape]; - } - - isRandom = _.shape.some(function (c) { + Sparticles.prototype.getShapeArray = function () { + var shapes = Array.isArray(this.settings.shape) ? this.settings.shape : [this.settings.shape]; + var isRandom = shapes.some(function (c) { return c === "random"; }); if (isRandom) { - _.shape = ["square", "circle", "star", "diamond"]; + shapes = ["square", "circle", "triangle"]; } - return _.shape; + return shapes; + }; + /** + * convert the imageUrl option to an array if it isn't already + * @returns {Array} - array of image urls for use in rendering + */ + + + Sparticles.prototype.getImageArray = function () { + return Array.isArray(this.settings.imageUrl) ? this.settings.imageUrl : [this.settings.imageUrl]; }; /** * convert the input style to an array @@ -953,14 +969,16 @@ var Sparticles = (function () { */ - Sparticles.prototype.createStyleArray = function () { - if (this.settings.style !== "fill" && this.settings.style !== "stroke") { - this.settings.style = ["fill", "stroke"]; + Sparticles.prototype.getStyleArray = function () { + var styles = this.settings.style; + + if (styles !== "fill" && styles !== "stroke") { + styles = ["fill", "stroke"]; } else { - this.settings.style = [this.settings.style]; + styles = [styles]; } - return this.settings.style; + return styles; }; /** * set up the canvas and bind to a property for @@ -989,62 +1007,64 @@ var Sparticles = (function () { Sparticles.prototype.setupOffscreenCanvasses = function (callback) { var _this3 = this; + var colors = this.colors.filter(function (item, index) { + return _this3.colors.indexOf(item) === index; + }); + var shapes = this.shapes.filter(function (item, index) { + return _this3.shapes.indexOf(item) === index; + }); + var styles = this.styles.filter(function (item, index) { + return _this3.styles.indexOf(item) === index; + }); + var imageUrls = this.imageUrls.filter(function (item, index) { + return _this3.imageUrls.indexOf(item) === index; + }); + var imageCount = colors.length * imageUrls.length; + var canvasCount = colors.length * shapes.length * styles.length; + var imagesLoaded = 0; + var canvassesCreated = 0; this.canvasses = this.canvasses || {}; - this.settings.color.forEach(function (color) { + colors.forEach(function (color) { _this3.canvasses[color] = _this3.canvasses[color] || {}; + shapes.forEach(function (shape) { + _this3.canvasses[color][shape] = _this3.canvasses[color][shape] || {}; - if (_this3.settings.shape[0] === "image") { - _this3.loadAndDrawImages(color, callback); - } else { - _this3.settings.shape.forEach(function (shape) { - _this3.canvasses[color][shape] = _this3.canvasses[color][shape] || {}; - - _this3.settings.style.forEach(function (style) { - _this3.canvasses[color][shape][style] = document.createElement("canvas"); - var canvas = _this3.canvasses[color][shape][style]; - var ctx = canvas.getContext("2d"); - - switch (shape) { - case "square": - _this3.drawOffscreenCanvasForSquare(canvas, ctx, color, style); - - if (callback) callback(); - break; + if (shape === "image") { + imageUrls.forEach(function (imageUrl, i) { + var image = new Image(); + var imageCanvas = document.createElement("canvas"); + _this3.canvasses[color][shape][imageUrl] = imageCanvas; - case "line": - _this3.drawOffscreenCanvasForLine(canvas, ctx, color, style); + image.onload = function () { + imagesLoaded++; - if (callback) callback(); - break; + _this3.drawOffscreenCanvasForImage(image, color, imageCanvas); - case "triangle": - _this3.drawOffscreenCanvasForTriangle(canvas, ctx, color, style); + if (callback && imagesLoaded === imageCount) { + callback(); + } + }; - if (callback) callback(); - break; + image.onerror = function () { + console.error("failed to load source image: ", imageUrl); + }; - case "diamond": - _this3.drawOffscreenCanvasForDiamond(canvas, ctx, color, style); - - if (callback) callback(); - break; - - case "star": - _this3.drawOffscreenCanvasForStar(canvas, ctx, color, style); - - if (callback) callback(); - break; + image.src = imageUrl; + }); + } else { + styles.forEach(function (style) { + var canvas = document.createElement("canvas"); + _this3.canvasses[color][shape][style] = canvas; + canvassesCreated++; - case "circle": - default: - _this3.drawOffscreenCanvasForCircle(canvas, ctx, color, style); + _this3.drawOffscreenCanvas(shape, style, color, canvas); - if (callback) callback(); - break; + if (callback && canvassesCreated === canvasCount) { + callback(); } }); - }); - } + } + }); }); }; /** @@ -1112,17 +1132,61 @@ var Sparticles = (function () { } }; /** - * create, setup and render an offscreen canvas for a - * Square Particle of the given color + * pass-through the needed parameters to the offscreen canvas + * draw function associated with the given shape + * @param {String} shape - shape of the canvas to draw (eg: "circle") + * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context + * @returns {HTMLCanvasElement} - the created offscreen canvas + */ + + + Sparticles.prototype.drawOffscreenCanvas = function (shape, style, color, canvas) { + return this.offScreenCanvas[shape].call(this, style, color, canvas); + }; + /** + * object of shapes to draw + */ + + + Sparticles.prototype.offScreenCanvas = {}; + /** + * create, setup and render an offscreen canvas for a + * Circle Particle of the given color + * @param {String} style - style (either "fill" or "stroke") * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element + * @returns {HTMLCanvasElement} - the created offscreen canvas + */ + + Sparticles.prototype.offScreenCanvas.circle = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); + var size = this.settings.maxSize; + var lineSize = this.getLineSize(size); + var glowSize = this.getGlowSize(size); + var canvasSize = size + lineSize + glowSize; + canvas.width = canvasSize; + canvas.height = canvasSize; + this.renderGlow(ctx, color, size); + this.renderStyle(ctx, color, lineSize, style); + ctx.beginPath(); + ctx.ellipse(canvasSize / 2, canvasSize / 2, size / 2, size / 2, 0, 0, 360); + this.renderColor(ctx, style); + return canvas; + }; + /** + * create, setup and render an offscreen canvas for a + * Square Particle of the given color * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ - Sparticles.prototype.drawOffscreenCanvasForSquare = function (canvas, ctx, color, style) { + Sparticles.prototype.offScreenCanvas.square = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); var size = this.settings.maxSize; var lineSize = this.getLineSize(size); var glowSize = this.getGlowSize(size); @@ -1139,15 +1203,15 @@ var Sparticles = (function () { /** * create, setup and render an offscreen canvas for a * Line/Curve Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ - Sparticles.prototype.drawOffscreenCanvasForLine = function (canvas, ctx, color, style) { + Sparticles.prototype.offScreenCanvas.line = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); var size = this.settings.maxSize * 2; var lineSize = this.getLineSize(size); var glowSize = this.getGlowSize(size); @@ -1169,15 +1233,15 @@ var Sparticles = (function () { /** * create, setup and render an offscreen canvas for a * Triangle Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ - Sparticles.prototype.drawOffscreenCanvasForTriangle = function (canvas, ctx, color, style) { + Sparticles.prototype.offScreenCanvas.triangle = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); var size = this.settings.maxSize; var lineSize = this.getLineSize(size); var glowSize = this.getGlowSize(size); @@ -1200,15 +1264,15 @@ var Sparticles = (function () { /** * create, setup and render an offscreen canvas for a * Diamond Sparkle Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ - Sparticles.prototype.drawOffscreenCanvasForDiamond = function (canvas, ctx, color, style) { + Sparticles.prototype.offScreenCanvas.diamond = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); var size = this.settings.maxSize; var half = size / 2; var lineSize = this.getLineSize(size); @@ -1236,15 +1300,15 @@ var Sparticles = (function () { /** * create, setup and render an offscreen canvas for a * Star Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ - Sparticles.prototype.drawOffscreenCanvasForStar = function (canvas, ctx, color, style) { + Sparticles.prototype.offScreenCanvas.star = function (style, color, canvas) { + var ctx = canvas.getContext("2d"); var size = 52; var lineSize = this.getLineSize(size); var glowSize = this.getGlowSize(size); @@ -1300,85 +1364,19 @@ var Sparticles = (function () { this.renderColor(ctx, style); return canvas; }; - /** - * create, setup and render an offscreen canvas for a - * Circle Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with - * @param {String} style - style (either "fill" or "stroke") - * @returns {HTMLCanvasElement} - the created offscreen canvas - */ - - - Sparticles.prototype.drawOffscreenCanvasForCircle = function (canvas, ctx, color, style) { - var size = this.settings.maxSize; - var lineSize = this.getLineSize(size); - var glowSize = this.getGlowSize(size); - var canvasSize = size + lineSize + glowSize; - canvas.width = canvasSize; - canvas.height = canvasSize; - this.renderGlow(ctx, color, size); - this.renderStyle(ctx, color, lineSize, style); - ctx.beginPath(); - ctx.ellipse(canvasSize / 2, canvasSize / 2, size / 2, size / 2, 0, 0, 360); - this.renderColor(ctx, style); - return canvas; - }; - /** - * set up the needed array for referencing the images in the Sparticle() - * instance, then loop through each image and load it before running the callback - * @param {String} color - the color of the image that we're loading - * @param {Function} callback - callback function to run after images load - */ - - - Sparticles.prototype.loadAndDrawImages = function (color, callback) { - var _this4 = this; - - var imgUrls = this.settings.imageUrl; - var imageUrls = Array.isArray(imgUrls) ? imgUrls : [imgUrls]; - var imageCount = imageUrls.length; - var imagesLoaded = 0; - this.images = []; - imageUrls.forEach(function (imageUrl, i) { - _this4.images.push("image" + i); - - _this4.canvasses[color]["image" + i] = document.createElement("canvas"); - var canvas = _this4.canvasses[color]["image" + i]; - var ctx = canvas.getContext("2d"); - var image = new Image(); - - image.onload = function () { - imagesLoaded++; - - _this4.drawImageOffscreenCanvas(image, canvas, ctx, color); - - if (callback && imagesLoaded === imageCount) { - callback(); - } - }; - - image.onerror = function () { - console.error("failed to load source image: ", imageUrl); - }; - - image.src = imageUrl; - }); - }; /** * create, setup and render an offscreen canvas for a * Custom Image Particle of the given color * @param {HTMLImageElement} image - the image element that has loaded - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ - Sparticles.prototype.drawImageOffscreenCanvas = function (image, canvas, ctx, color) { + Sparticles.prototype.drawOffscreenCanvasForImage = function (image, color, canvas) { var size = image.width; + var ctx = canvas.getContext("2d"); canvas.width = size; canvas.height = size; ctx.drawImage(image, 0, 0, size, size, 0, 0, size, size); diff --git a/dist/sparticles.min.js b/dist/sparticles.min.js index b041e89..3907201 100644 --- a/dist/sparticles.min.js +++ b/dist/sparticles.min.js @@ -1,9 +1,9 @@ /**! * Sparticles - Lightweight, High Performance Particles in Canvas - * @version 1.0.3 + * @version 1.1.0 * @license MPL-2.0 * @author simeydotme * @website http://sparticlesjs.dev * @repository https://github.com/simeydotme/sparticles.git */ -var Sparticles=function(){"use strict";function t(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}function e(t,e){var i=Object.keys(t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(t);e&&(s=s.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),i.push.apply(i,s)}return i}function i(i){for(var s=1;s0&&void 0!==arguments[0]?arguments[0]:function(){},e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:60;this.fps=e,this.handler=t;var i=0;this.start=function(){var t=this;if(!this.started){var e=performance.now(),s=1e3/this.fps;i=requestAnimationFrame((function r(n){var a=n-e;i=requestAnimationFrame(r),a>=s-0&&(t.handler(a),e=n-a%s)})),this.started=!0}},this.stop=function(){cancelAnimationFrame(i),this.started=!1}},r=function(t){return[Math.cos(a(t-90)),Math.sin(a(t-90))]},n=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;return Math.max(e,Math.min(i,t))},a=function(t){return t*Math.PI/180},h=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:Math.random();return e<=t?i=t:(0!==t||1!==e)&&e>t&&(i=i*(e-t)+t),i},o=function(t){return t[Math.floor(h(0,t.length))]},c=function(){var t=p(h(0,360)),e=p(h(90,100)),i=p(h(45,85));return"hsl(".concat(t,",").concat(e,"%,").concat(i,"%)")},l=function(t){return t>h()},p=function(t){return.5+t|0},d=function(t){return t?(this.canvas=t.canvas,this.images=t.images,this.settings=t.settings,this.ctx=t.canvas.getContext("2d"),this.setup(),this.init()):console.warn("Invalid parameters given to Sparticle()",arguments),this};d.prototype.setup=function(){var t=this.settings;this.frame=0,this.frameoffset=p(h(0,360)),this.size=p(h(t.minSize,t.maxSize)),this.da=this.getAlphaDelta(),this.dx=this.getDeltaX(),this.dy=this.getDeltaY(),this.dd=this.getDriftDelta(),this.dr=this.getRotationDelta(),this.shape=this.getShapeOrImage(),this.style=this.getStyle(),this.color=this.getColor(),this.rotation=t.rotate?a(h(0,360)):0,this.vertical=t.direction>150&&t.direction<210||t.direction>330&&t.direction<390||t.direction>-30&&t.direction<30,this.horizontal=t.direction>60&&t.direction<120||t.direction>240&&t.direction<300},d.prototype.init=function(){var t=this.settings,e=this.canvas;this.alpha=0,(t.speed>0||0===t.alphaSpeed)&&(this.alpha=h(t.minAlpha,t.maxAlpha)),t.bounce?(this.px=p(h(2,e.width-this.size-2)),this.py=p(h(2,e.height-this.size-2))):(this.px=p(h(2*-this.size,e.width+this.size)),this.py=p(h(2*-this.size,e.height+this.size)))},d.prototype.reset=function(){this.setup(),this.py<0?this.py=this.canvas.height+2*this.size:this.py>this.canvas.height&&(this.py=0-2*this.size),this.px<0?this.px=this.canvas.width+2*this.size:this.px>this.canvas.width&&(this.px=0-2*this.size)},d.prototype.bounce=function(){this.settings.direction;(this.py<=0||this.py+this.size>=this.canvas.height)&&(this.dy=-this.dy,this.horizontal&&(this.dd=-this.dd)),(this.px<=0||this.px+this.size>=this.canvas.width)&&(this.dx=-this.dx,this.vertical&&(this.dd=-this.dd))},d.prototype.isOffCanvas=function(){var t=0-2*this.size,e=this.canvas.height+2*this.size,i=this.canvas.width+2*this.size;return this.pxi||this.pye},d.prototype.isTouchingEdge=function(){var t=this.canvas.height-this.size,e=this.canvas.width-this.size;return this.px<0||this.px>e||this.py<0||this.py>t},d.prototype.getColor=function(){if(Array.isArray(this.settings.color))return o(this.settings.color)},d.prototype.getShapeOrImage=function(){var t=this.settings.shape;if(Array.isArray(t))return"image"===t[0]&&this.images?o(this.images):o(t)},d.prototype.getStyle=function(){return o(this.settings.style)},d.prototype.getDelta=function(){var t=.1*this.settings.speed;return this.settings.speed&&this.settings.parallax?t+this.size*this.settings.parallax/50:t},d.prototype.getDeltaVariance=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=this.settings.speed||10;return t>0?h(-t,t)*e/100:0},d.prototype.getDeltaX=function(){var t=this.getDelta(),e=this.getDeltaVariance(this.settings.xVariance);return r(this.settings.direction)[0]*t+e},d.prototype.getDeltaY=function(){var t=this.getDelta(),e=this.getDeltaVariance(this.settings.yVariance);return r(this.settings.direction)[1]*t+e},d.prototype.getAlphaDelta=function(){var t=this.settings.alphaVariance,e=h(1,t+1);return l(.5)&&(e=-e),e},d.prototype.getDriftDelta=function(){return this.settings.drift?h(this.settings.drift-this.settings.drift/2,this.settings.drift+this.settings.drift/2):0},d.prototype.getRotationDelta=function(){var t=0;return this.settings.rotate&&this.settings.rotation&&(t=a(h(.5,1.5)*this.settings.rotation),l(.5)&&(t=-t)),t},d.prototype.update=function(){return this.frame+=1,this.updatePosition(),this.updateAlpha(),this},d.prototype.updateAlpha=function(){return this.settings.alphaSpeed>0&&(this.settings.twinkle?this.alpha=this.updateTwinkle():this.alpha=this.updateFade()),this.alpha},d.prototype.updateFade=function(){var t=this.da/1e3*this.settings.alphaSpeed*.5,e=this.alpha+t,i=this.da>0&&e>this.settings.maxAlpha,s=this.da<0&&ethis.settings.maxAlpha,s=t=1&&!(arguments[0]instanceof HTMLElement)&&(e=arguments[0],r=arguments[1],n=arguments[2],t=void 0),r&&!n&&(n=r);var a={alphaSpeed:10,alphaVariance:1,bounce:!1,color:"random",randomColor:c,randomColorCount:3,composition:"source-over",count:50,direction:180,drift:1,glow:0,imageUrl:"",maxAlpha:1,maxSize:10,minAlpha:0,minSize:1,parallax:1,rotate:!0,rotation:1,shape:"circle",speed:10,style:"fill",twinkle:!1,xVariance:2,yVariance:2};return this.el=t||document.body,this.settings=i({},a,{},e),this.resizable=!r&&!n,this.width=this.resizable?this.el.clientWidth:r,this.height=this.resizable?this.el.clientHeight:n,this.init=function(){var t=this;return this.sparticles=[],this.createColorArray(),this.createShapeArray(),this.createStyleArray(),this.setupMainCanvas(),this.setupOffscreenCanvasses((function(){t.createSparticles(),t.start()})),window.addEventListener("resize",this),this},this.handleEvent=function(t){var e=this;"resize"===t.type&&(clearTimeout(this.resizeTimer),this.resizeTimer=setTimeout((function(){e.resizable&&(e.width=e.el.clientWidth,e.height=e.el.clientHeight,e.setCanvasSize().resetSparticles())}),200))},this.start=function(){var t=this;return this.loop||(this.loop=new s((function(e){t.drawFrame(e)}))),this.loop.start(),this},this.stop=function(){return this.loop.stop(),this},this.destroy=function(){for(var t in this.stop(),this.el.removeChild(this.canvas),window.removeEventListener("resize",this),this)this.hasOwnProperty(t)&&delete this[t];return this},this.setCanvasSize=function(t,e){return t&&(this.resizable=!1),this.width=t||this.width,this.height=e||this.height,this.canvas.width=this.width,this.canvas.height=this.height,this},this.resetSparticles=this.createSparticles=function(){this.sparticles=[],this.ctx.globalCompositeOperation=this.settings.composition;for(var t=0;te.size})),this.sparticles},this.init()};return u.prototype.createColorArray=function(){var t=this.settings;if(Array.isArray(t.color)||(t.color=[t.color]),t.color.some((function(t){return"random"===t})))for(var e=0;e0&&void 0!==arguments[0]?arguments[0]:function(){},e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:60;this.fps=e,this.handler=t;var i=0;this.start=function(){var t=this;if(!this.started){var e=performance.now(),s=1e3/this.fps;i=requestAnimationFrame((function n(r){var a=r-e;i=requestAnimationFrame(n),a>=s-0&&(t.handler(a),e=r-a%s)})),this.started=!0}},this.stop=function(){cancelAnimationFrame(i),this.started=!1}},n=function(t){return[Math.cos(a(t-90)),Math.sin(a(t-90))]},r=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:1;return Math.max(e,Math.min(i,t))},a=function(t){return t*Math.PI/180},h=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:Math.random();return e<=t?i=t:(0!==t||1!==e)&&e>t&&(i=i*(e-t)+t),i},o=function(t){return t[Math.floor(h(0,t.length))]},l=function(){var t=p(h(0,360)),e=p(h(90,100)),i=p(h(45,85));return"hsl(".concat(t,",").concat(e,"%,").concat(i,"%)")},c=function(t){return t>h()},p=function(t){return.5+t|0},g=function(t){return t?(this.canvas=t.canvas,this.settings=t.settings,this.colors=t.colors,this.shapes=t.shapes,this.images=t.images,this.styles=t.styles,this.ctx=t.canvas.getContext("2d"),this.setup(),this.init()):console.warn("Invalid parameters given to Sparticle()",arguments),this};g.prototype.setup=function(){var t=this.settings;this.frame=0,this.frameoffset=p(h(0,360)),this.size=p(h(t.minSize,t.maxSize)),this.da=this.getAlphaDelta(),this.dx=this.getDeltaX(),this.dy=this.getDeltaY(),this.dd=this.getDriftDelta(),this.dr=this.getRotationDelta(),this.color=this.getColor(),this.shape=this.getShape(),this.image=this.getImage(),this.style=this.getStyle(),this.rotation=t.rotate?a(h(0,360)):0,this.vertical=t.direction>150&&t.direction<210||t.direction>330&&t.direction<390||t.direction>-30&&t.direction<30,this.horizontal=t.direction>60&&t.direction<120||t.direction>240&&t.direction<300},g.prototype.init=function(){var t=this.settings,e=this.canvas;this.alpha=0,(t.speed>0||0===t.alphaSpeed)&&(this.alpha=h(t.minAlpha,t.maxAlpha)),t.bounce?(this.px=p(h(2,e.width-this.size-2)),this.py=p(h(2,e.height-this.size-2))):(this.px=p(h(2*-this.size,e.width+this.size)),this.py=p(h(2*-this.size,e.height+this.size)))},g.prototype.reset=function(){this.setup(),this.py<0?this.py=this.canvas.height+2*this.size:this.py>this.canvas.height&&(this.py=0-2*this.size),this.px<0?this.px=this.canvas.width+2*this.size:this.px>this.canvas.width&&(this.px=0-2*this.size)},g.prototype.bounce=function(){this.settings.direction;(this.py<=0||this.py+this.size>=this.canvas.height)&&(this.dy=-this.dy,this.horizontal&&(this.dd=-this.dd)),(this.px<=0||this.px+this.size>=this.canvas.width)&&(this.dx=-this.dx,this.vertical&&(this.dd=-this.dd))},g.prototype.isOffCanvas=function(){var t=0-2*this.size,e=this.canvas.height+2*this.size,i=this.canvas.width+2*this.size;return this.pxi||this.pye},g.prototype.isTouchingEdge=function(){var t=this.canvas.height-this.size,e=this.canvas.width-this.size;return this.px<0||this.px>e||this.py<0||this.py>t},g.prototype.getColor=function(){return"random"===this.settings.color?o(this.colors):Array.isArray(this.settings.color)?o(this.settings.color):this.settings.color},g.prototype.getShape=function(){return"random"===this.settings.shape?o(this.shapes):Array.isArray(this.settings.shape)?o(this.settings.shape):this.settings.shape},g.prototype.getImage=function(){return Array.isArray(this.settings.imageUrl)?o(this.settings.imageUrl):this.settings.imageUrl},g.prototype.getStyle=function(){return o(this.styles)},g.prototype.getDelta=function(){var t=.1*this.settings.speed;return this.settings.speed&&this.settings.parallax?t+this.size*this.settings.parallax/50:t},g.prototype.getDeltaVariance=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,e=this.settings.speed||10;return t>0?h(-t,t)*e/100:0},g.prototype.getDeltaX=function(){var t=this.getDelta(),e=this.getDeltaVariance(this.settings.xVariance);return n(this.settings.direction)[0]*t+e},g.prototype.getDeltaY=function(){var t=this.getDelta(),e=this.getDeltaVariance(this.settings.yVariance);return n(this.settings.direction)[1]*t+e},g.prototype.getAlphaDelta=function(){var t=this.settings.alphaVariance,e=h(1,t+1);return c(.5)&&(e=-e),e},g.prototype.getDriftDelta=function(){return this.settings.drift?h(this.settings.drift-this.settings.drift/2,this.settings.drift+this.settings.drift/2):0},g.prototype.getRotationDelta=function(){var t=0;return this.settings.rotate&&this.settings.rotation&&(t=a(h(.5,1.5)*this.settings.rotation),c(.5)&&(t=-t)),t},g.prototype.update=function(){return this.frame+=1,this.updatePosition(),this.updateAlpha(),this},g.prototype.updateAlpha=function(){return this.settings.alphaSpeed>0&&(this.settings.twinkle?this.alpha=this.updateTwinkle():this.alpha=this.updateFade()),this.alpha},g.prototype.updateFade=function(){var t=this.da/1e3*this.settings.alphaSpeed*.5,e=this.alpha+t,i=this.da>0&&e>this.settings.maxAlpha,s=this.da<0&&ethis.settings.maxAlpha,s=t=1&&!(arguments[0]instanceof HTMLElement)&&(e=arguments[0],n=arguments[1],r=arguments[2],t=void 0),n&&!r&&(r=n);var a={alphaSpeed:10,alphaVariance:1,bounce:!1,color:"random",randomColor:l,randomColorCount:3,composition:"source-over",count:50,direction:180,drift:1,glow:0,imageUrl:"",maxAlpha:1,maxSize:10,minAlpha:0,minSize:1,parallax:1,rotate:!0,rotation:1,shape:"circle",speed:10,style:"fill",twinkle:!1,xVariance:2,yVariance:2};return this.el=t||document.body,this.settings=i({},a,{},e),this.resizable=!n&&!r,this.width=this.resizable?this.el.clientWidth:n,this.height=this.resizable?this.el.clientHeight:r,this.init=function(){var t=this;return this.sparticles=[],this.colors=this.getColorArray(),this.shapes=this.getShapeArray(),this.styles=this.getStyleArray(),this.imageUrls=this.getImageArray(),this.setupMainCanvas(),this.setupOffscreenCanvasses((function(){t.createSparticles(),t.start()})),window.addEventListener("resize",this),this},this.handleEvent=function(t){var e=this;"resize"===t.type&&(clearTimeout(this.resizeTimer),this.resizeTimer=setTimeout((function(){e.resizable&&(e.width=e.el.clientWidth,e.height=e.el.clientHeight,e.setCanvasSize().resetSparticles())}),200))},this.start=function(){var t=this;return this.loop||(this.loop=new s((function(e){t.drawFrame(e)}))),this.loop.start(),this},this.stop=function(){return this.loop.stop(),this},this.destroy=function(){for(var t in this.stop(),this.el.removeChild(this.canvas),window.removeEventListener("resize",this),this)this.hasOwnProperty(t)&&delete this[t];return this},this.setCanvasSize=function(t,e){return t&&(this.resizable=!1),this.width=t||this.width,this.height=e||this.height,this.canvas.width=this.width,this.canvas.height=this.height,this},this.resetSparticles=this.createSparticles=function(){this.sparticles=[],this.ctx.globalCompositeOperation=this.settings.composition;for(var t=0;te.size})),this.sparticles},this.init()};return u.prototype.getColorArray=function(){var t=Array.isArray(this.settings.color)?this.settings.color:[this.settings.color];if(t.some((function(t){return"random"===t})))for(var e=0;e { - s.createColorArray(); - s.createShapeArray(); - s.createStyleArray(); - s.setupOffscreenCanvasses(function() { - s.createSparticles(); - }); + if( window.mySparticles && window.mySparticles instanceof Sparticles ) { + try { + window.mySparticles.destroy(); + } catch(e) { + document.querySelector("main").removeChild( s.canvas ); + } + } + window.initSparticles(); }; + var rerenderColors = function(v) { - if (colorType.type === "rainbow") { - s.settings.color = "rainbow"; + if (colorType.type === "random") { + options.color = "random"; } else if (colorType.type === "single") { - s.settings.color = colors.color1; + options.color = colors.color1; } else { - s.settings.color = Object.keys(colors).map(i => { + options.color = Object.keys(colors).map(i => { return colors[i]; }); } @@ -123,33 +148,33 @@ window.initGui = function() { const gui = new dat.GUI({ load: options }); const part = gui.addFolder("Particles"); part.open(); - part.add(s.settings, "count", 1, 500, 1).onFinishChange(rerender); - part.add(s.settings, "shape", shapes).onFinishChange(rerender); - part.add(s.settings, "style", styles).onFinishChange(rerender); - part.add(s.settings, "rotate").onFinishChange(rerender); - part.add(s.settings, "bounce").onFinishChange(rerender); + part.add(options, "count", 1, 500, 1).onFinishChange(rerender); + part.add(options, "shape", shapes).onFinishChange(rerender); + part.add(options, "style", styles).onFinishChange(rerender); + part.add(options, "rotate").onFinishChange(rerender); + part.add(options, "bounce").onFinishChange(rerender); const image = part.addFolder("Image"); - // image.add(s.settings, "imageUrl").onFinishChange(rerender); - part.add(s.settings, "minSize", 1, 50, 1).onFinishChange(rerender); - part.add(s.settings, "maxSize", 1, 50, 1).onFinishChange(rerender); + // image.add(options, "imageUrl").onFinishChange(rerender); + part.add(options, "minSize", 1, 50, 1).onFinishChange(rerender); + part.add(options, "maxSize", 1, 50, 1).onFinishChange(rerender); const anim = gui.addFolder("Animation"); - anim.add(s.settings, "direction", 0, 360, 1).onFinishChange(rerender); - anim.add(s.settings, "speed", 0, 100, 0.1).onFinishChange(rerender); - anim.add(s.settings, "rotation", 0, 100, 0.1).onFinishChange(rerender); + anim.add(options, "direction", 0, 360, 1).onFinishChange(rerender); + anim.add(options, "speed", 0, 100, 0.1).onFinishChange(rerender); + anim.add(options, "rotation", 0, 100, 0.1).onFinishChange(rerender); const move = anim.addFolder("Movement"); - move.add(s.settings, "parallax", 0, 10, 0.1).onFinishChange(rerender); - move.add(s.settings, "drift", 0, 30, 0.01).onFinishChange(rerender); - move.add(s.settings, "xVariance", 0, 20, 0.1).onFinishChange(rerender); - move.add(s.settings, "yVariance", 0, 20, 0.1).onFinishChange(rerender); + move.add(options, "parallax", 0, 10, 0.1).onFinishChange(rerender); + move.add(options, "drift", 0, 30, 0.01).onFinishChange(rerender); + move.add(options, "xVariance", 0, 20, 0.1).onFinishChange(rerender); + move.add(options, "yVariance", 0, 20, 0.1).onFinishChange(rerender); const vis = gui.addFolder("Visual"); - vis.add(s.settings, "glow", 0,150).onFinishChange(rerender); - vis.add(s.settings, "composition", composites).onFinishChange(rerender); + vis.add(options, "glow", 0,150).onFinishChange(rerender); + vis.add(options, "composition", composites).onFinishChange(rerender); const alpha = vis.addFolder("Alpha"); - alpha.add(s.settings, "twinkle").onFinishChange(rerender); - alpha.add(s.settings, "minAlpha", -2, 2, 0.1).onFinishChange(rerender); - alpha.add(s.settings, "maxAlpha", -2, 2, 0.1).onFinishChange(rerender); - alpha.add(s.settings, "alphaSpeed", 0, 50, 1).onFinishChange(rerender); - alpha.add(s.settings, "alphaVariance", 0, 20, 1).onFinishChange(rerender); + alpha.add(options, "twinkle").onFinishChange(rerender); + alpha.add(options, "minAlpha", -2, 2, 0.1).onFinishChange(rerender); + alpha.add(options, "maxAlpha", -2, 2, 0.1).onFinishChange(rerender); + alpha.add(options, "alphaSpeed", 0, 50, 1).onFinishChange(rerender); + alpha.add(options, "alphaVariance", 0, 20, 1).onFinishChange(rerender); const color = vis.addFolder("Color"); color.open(); color.add(colorType, "type", colorOptions).onFinishChange(rerenderColors); diff --git a/package.json b/package.json index 5d5000a..89c1a16 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sparticles", - "version": "1.0.3", + "version": "1.1.0", "description": "Lightweight, High Performance Particles in Canvas", "main": "dist/sparticles.esm.js", "files": [ diff --git a/src/sparticle.js b/src/sparticle.js index 14c62f4..6a5a186 100644 --- a/src/sparticle.js +++ b/src/sparticle.js @@ -9,8 +9,11 @@ import { cartesian, clamp, radian, random, randomArray, roll, round } from "./he export const Sparticle = function(parent) { if (parent) { this.canvas = parent.canvas; - this.images = parent.images; this.settings = parent.settings; + this.colors = parent.colors; + this.shapes = parent.shapes; + this.images = parent.images; + this.styles = parent.styles; this.ctx = parent.canvas.getContext("2d"); this.setup(); this.init(); @@ -35,9 +38,10 @@ Sparticle.prototype.setup = function() { this.dy = this.getDeltaY(); this.dd = this.getDriftDelta(); this.dr = this.getRotationDelta(); - this.shape = this.getShapeOrImage(); - this.style = this.getStyle(); this.color = this.getColor(); + this.shape = this.getShape(); + this.image = this.getImage(); + this.style = this.getStyle(); this.rotation = _.rotate ? radian(random(0, 360)) : 0; this.vertical = (_.direction > 150 && _.direction < 210) || @@ -143,25 +147,40 @@ Sparticle.prototype.isTouchingEdge = function() { * @returns {String} - random color from color array */ Sparticle.prototype.getColor = function() { - if (Array.isArray(this.settings.color)) { + if (this.settings.color === "random") { + return randomArray(this.colors); + } else if (Array.isArray(this.settings.color)) { return randomArray(this.settings.color); + } else { + return this.settings.color; } }; /** - * get a random shape or image for the particle from the - * array of shapes set in the options object, or the array - * of images, if the shape is set to "image" - * @returns {String} - random shape or image from shape or image array + * get a random shape for the particle from the + * array of shapes set in the options object + * @returns {String} - random shape from shape array */ -Sparticle.prototype.getShapeOrImage = function() { - const shape = this.settings.shape; - if (Array.isArray(shape)) { - if (shape[0] === "image" && this.images) { - return randomArray(this.images); - } else { - return randomArray(shape); - } +Sparticle.prototype.getShape = function() { + if (this.settings.shape === "random") { + return randomArray(this.shapes); + } else if (Array.isArray(this.settings.shape)) { + return randomArray(this.settings.shape); + } else { + return this.settings.shape; + } +}; + +/** + * get the image for the particle from the array + * of possible image urls + * @returns {String} - random imageUrl from imageUrl array + */ +Sparticle.prototype.getImage = function() { + if (Array.isArray(this.settings.imageUrl)) { + return randomArray(this.settings.imageUrl); + } else { + return this.settings.imageUrl; } }; @@ -171,7 +190,7 @@ Sparticle.prototype.getShapeOrImage = function() { * @returns {String} - either "fill" or "stroke" */ Sparticle.prototype.getStyle = function() { - return randomArray(this.settings.style); + return randomArray(this.styles); }; /** @@ -411,9 +430,11 @@ Sparticle.prototype.updateDrift = function() { }; Sparticle.prototype.render = function(canvasses) { - let particleCanvas = canvasses[this.color][this.shape]; - if (this.settings.shape[0] !== "image") { + let particleCanvas; + if (this.shape !== "image") { particleCanvas = canvasses[this.color][this.shape][this.style]; + } else { + particleCanvas = canvasses[this.color][this.shape][this.image]; } const canvasSize = particleCanvas.width; const scale = this.size / canvasSize; diff --git a/src/sparticles.js b/src/sparticles.js index 2d75214..821b573 100644 --- a/src/sparticles.js +++ b/src/sparticles.js @@ -22,16 +22,16 @@ import { Sparticle } from "./sparticle.js"; * @param {Number} [options.maxAlpha=1] - maximum alpha value of every particle * @param {Number} [options.minSize=1] - minimum size of every particle * @param {Number} [options.maxSize=10] - maximum size of every particle - * @param {String} [options.style=fill] - fill style of particles (one of; "fill", "stroke" or "both") * @param {Boolean} [options.bounce=false] - should the particles bounce off edge of canvas * @param {Number} [options.drift=1] - the "driftiness" of particles which have a horizontal/vertical direction * @param {Number} [options.glow=0] - the glow effect size of each particle * @param {Boolean} [options.twinkle=false] - particles to exhibit an alternative alpha transition as "twinkling" + * @param {String} [options.style=fill] - fill style of particles (one of; "fill", "stroke" or "both") + * @param {(String|String[])} [options.shape=circle] - shape of particles (any of; circle, square, triangle, diamond, line, image) or "random" + * @param {(String|String[])} [options.imageUrl=] - if shape is "image", define an image url (can be data-uri, must be square (1:1 ratio)) * @param {(String|String[])} [options.color=random] - css color as string, or array of color strings (can also be "random") * @param {Function} [options.randomColor=randomHsl(index,total)] - a custom function for setting the random colors when color="random" * @param {Number} [options.randomColorCount=3] - the number of random colors to generate when color is "random" - * @param {(String|String[])} [options.shape=circle] - shape of particles (any of; circle, square, triangle, diamond, line, image) or "random" - * @param {(String|String[])} [options.imageUrl=] - if shape is "image", define an image url (can be data-uri, must be square (1:1 ratio)) * @param {Number} [width] - the width of the canvas element * @param {Number} [height=width] - the height of the canvas element * @returns {Object} - reference to a new Sparticles instance @@ -85,9 +85,10 @@ const Sparticles = function(node, options, width, height) { */ this.init = function() { this.sparticles = []; - this.createColorArray(); - this.createShapeArray(); - this.createStyleArray(); + this.colors = this.getColorArray(); + this.shapes = this.getShapeArray(); + this.styles = this.getStyleArray(); + this.imageUrls = this.getImageArray(); this.setupMainCanvas(); this.setupOffscreenCanvasses(() => { this.createSparticles(); @@ -203,60 +204,54 @@ const Sparticles = function(node, options, width, height) { * convert the input color to an array if it isn't already * @returns {Array} - array of colors for use in rendering */ -Sparticles.prototype.createColorArray = function() { - const _ = this.settings; - const isArray = Array.isArray(_.color); - let isRandom = false; - - if (!isArray) { - _.color = [_.color]; - } - - isRandom = _.color.some(c => c === "random"); +Sparticles.prototype.getColorArray = function() { + let colors = Array.isArray(this.settings.color) ? this.settings.color : [this.settings.color]; + const isRandom = colors.some(c => c === "random"); if (isRandom) { - // it would be silly to have an array of too many colours. - for (let i = 0; i < _.randomColorCount; i++) { - _.color[i] = _.randomColor(i, _.randomColorCount); + for (let i = 0; i < this.settings.randomColorCount; i++) { + colors[i] = this.settings.randomColor(i, this.settings.randomColorCount); } } - return _.color; + return colors; }; /** * convert the input shape to an array if it isn't already * @returns {Array} - array of shapes for use in rendering */ -Sparticles.prototype.createShapeArray = function() { - const _ = this.settings; - const isArray = Array.isArray(_.shape); - let isRandom = false; - - if (!isArray) { - _.shape = [_.shape]; - } - - isRandom = _.shape.some(c => c === "random"); +Sparticles.prototype.getShapeArray = function() { + let shapes = Array.isArray(this.settings.shape) ? this.settings.shape : [this.settings.shape]; + const isRandom = shapes.some(c => c === "random"); if (isRandom) { - _.shape = ["square", "circle", "star", "diamond"]; + shapes = ["square", "circle", "triangle"]; } - return _.shape; + return shapes; +}; + +/** + * convert the imageUrl option to an array if it isn't already + * @returns {Array} - array of image urls for use in rendering + */ +Sparticles.prototype.getImageArray = function() { + return Array.isArray(this.settings.imageUrl) ? this.settings.imageUrl : [this.settings.imageUrl]; }; /** * convert the input style to an array * @returns {Array} - array of styles for use in rendering */ -Sparticles.prototype.createStyleArray = function() { - if (this.settings.style !== "fill" && this.settings.style !== "stroke") { - this.settings.style = ["fill", "stroke"]; +Sparticles.prototype.getStyleArray = function() { + let styles = this.settings.style; + if (styles !== "fill" && styles !== "stroke") { + styles = ["fill", "stroke"]; } else { - this.settings.style = [this.settings.style]; + styles = [styles]; } - return this.settings.style; + return styles; }; /** @@ -281,57 +276,57 @@ Sparticles.prototype.setupMainCanvas = function() { * @returns {HTMLCanvasElement} - the created offscreen canvas */ Sparticles.prototype.setupOffscreenCanvasses = function(callback) { + const colors = this.colors.filter((item, index) => this.colors.indexOf(item) === index); + const shapes = this.shapes.filter((item, index) => this.shapes.indexOf(item) === index); + const styles = this.styles.filter((item, index) => this.styles.indexOf(item) === index); + const imageUrls = this.imageUrls.filter((item, index) => this.imageUrls.indexOf(item) === index); + const imageCount = colors.length * imageUrls.length; + const canvasCount = colors.length * shapes.length * styles.length; + let imagesLoaded = 0; + let canvassesCreated = 0; + this.canvasses = this.canvasses || {}; - this.settings.color.forEach(color => { + colors.forEach(color => { this.canvasses[color] = this.canvasses[color] || {}; - if (this.settings.shape[0] === "image") { - this.loadAndDrawImages(color, callback); - } else { - this.settings.shape.forEach(shape => { - this.canvasses[color][shape] = this.canvasses[color][shape] || {}; - - this.settings.style.forEach(style => { - this.canvasses[color][shape][style] = document.createElement("canvas"); - const canvas = this.canvasses[color][shape][style]; - const ctx = canvas.getContext("2d"); - - switch (shape) { - case "square": - this.drawOffscreenCanvasForSquare(canvas, ctx, color, style); - if (callback) callback(); - break; - - case "line": - this.drawOffscreenCanvasForLine(canvas, ctx, color, style); - if (callback) callback(); - break; - - case "triangle": - this.drawOffscreenCanvasForTriangle(canvas, ctx, color, style); - if (callback) callback(); - break; - - case "diamond": - this.drawOffscreenCanvasForDiamond(canvas, ctx, color, style); - if (callback) callback(); - break; - - case "star": - this.drawOffscreenCanvasForStar(canvas, ctx, color, style); - if (callback) callback(); - break; - - case "circle": - default: - this.drawOffscreenCanvasForCircle(canvas, ctx, color, style); - if (callback) callback(); - break; + shapes.forEach(shape => { + this.canvasses[color][shape] = this.canvasses[color][shape] || {}; + + if (shape === "image") { + imageUrls.forEach((imageUrl, i) => { + let image = new Image(); + const imageCanvas = document.createElement("canvas"); + this.canvasses[color][shape][imageUrl] = imageCanvas; + + image.onload = () => { + imagesLoaded++; + this.drawOffscreenCanvasForImage(image, color, imageCanvas); + if (callback && imagesLoaded === imageCount) { + callback(); + } + }; + + image.onerror = () => { + console.error("failed to load source image: ", imageUrl); + }; + + image.src = imageUrl; + }); + } else { + styles.forEach(style => { + const canvas = document.createElement("canvas"); + this.canvasses[color][shape][style] = canvas; + canvassesCreated++; + + this.drawOffscreenCanvas(shape, style, color, canvas); + + if (callback && canvassesCreated === canvasCount) { + callback(); } }); - }); - } + } + }); }); }; @@ -395,15 +390,57 @@ Sparticles.prototype.renderColor = function(ctx, style) { }; /** - * create, setup and render an offscreen canvas for a - * Square Particle of the given color + * pass-through the needed parameters to the offscreen canvas + * draw function associated with the given shape + * @param {String} shape - shape of the canvas to draw (eg: "circle") + * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context + * @returns {HTMLCanvasElement} - the created offscreen canvas + */ +Sparticles.prototype.drawOffscreenCanvas = function(shape, style, color, canvas) { + return this.offScreenCanvas[shape].call(this, style, color, canvas); +}; + +/** + * object of shapes to draw + */ +Sparticles.prototype.offScreenCanvas = {}; + +/** + * create, setup and render an offscreen canvas for a + * Circle Particle of the given color + * @param {String} style - style (either "fill" or "stroke") * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element + * @returns {HTMLCanvasElement} - the created offscreen canvas + */ +Sparticles.prototype.offScreenCanvas.circle = function(style, color, canvas) { + const ctx = canvas.getContext("2d"); + const size = this.settings.maxSize; + const lineSize = this.getLineSize(size); + const glowSize = this.getGlowSize(size); + const canvasSize = size + lineSize + glowSize; + canvas.width = canvasSize; + canvas.height = canvasSize; + this.renderGlow(ctx, color, size); + this.renderStyle(ctx, color, lineSize, style); + ctx.beginPath(); + ctx.ellipse(canvasSize / 2, canvasSize / 2, size / 2, size / 2, 0, 0, 360); + this.renderColor(ctx, style); + return canvas; +}; + +/** + * create, setup and render an offscreen canvas for a + * Square Particle of the given color * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawOffscreenCanvasForSquare = function(canvas, ctx, color, style) { +Sparticles.prototype.offScreenCanvas.square = function(style, color, canvas) { + const ctx = canvas.getContext("2d"); const size = this.settings.maxSize; const lineSize = this.getLineSize(size); const glowSize = this.getGlowSize(size); @@ -421,13 +458,13 @@ Sparticles.prototype.drawOffscreenCanvasForSquare = function(canvas, ctx, color, /** * create, setup and render an offscreen canvas for a * Line/Curve Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawOffscreenCanvasForLine = function(canvas, ctx, color, style) { +Sparticles.prototype.offScreenCanvas.line = function(style, color, canvas) { + const ctx = canvas.getContext("2d"); const size = this.settings.maxSize * 2; const lineSize = this.getLineSize(size); const glowSize = this.getGlowSize(size); @@ -450,13 +487,13 @@ Sparticles.prototype.drawOffscreenCanvasForLine = function(canvas, ctx, color, s /** * create, setup and render an offscreen canvas for a * Triangle Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawOffscreenCanvasForTriangle = function(canvas, ctx, color, style) { +Sparticles.prototype.offScreenCanvas.triangle = function(style, color, canvas) { + const ctx = canvas.getContext("2d"); const size = this.settings.maxSize; const lineSize = this.getLineSize(size); const glowSize = this.getGlowSize(size); @@ -480,13 +517,13 @@ Sparticles.prototype.drawOffscreenCanvasForTriangle = function(canvas, ctx, colo /** * create, setup and render an offscreen canvas for a * Diamond Sparkle Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawOffscreenCanvasForDiamond = function(canvas, ctx, color, style) { +Sparticles.prototype.offScreenCanvas.diamond = function(style, color, canvas) { + const ctx = canvas.getContext("2d"); const size = this.settings.maxSize; const half = size / 2; const lineSize = this.getLineSize(size); @@ -543,13 +580,13 @@ Sparticles.prototype.drawOffscreenCanvasForDiamond = function(canvas, ctx, color /** * create, setup and render an offscreen canvas for a * Star Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with * @param {String} style - style (either "fill" or "stroke") + * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawOffscreenCanvasForStar = function(canvas, ctx, color, style) { +Sparticles.prototype.offScreenCanvas.star = function(style, color, canvas) { + const ctx = canvas.getContext("2d"); const size = 52; const lineSize = this.getLineSize(size); const glowSize = this.getGlowSize(size); @@ -606,77 +643,17 @@ Sparticles.prototype.drawOffscreenCanvasForStar = function(canvas, ctx, color, s return canvas; }; -/** - * create, setup and render an offscreen canvas for a - * Circle Particle of the given color - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context - * @param {String} color - the color to fill/stroke with - * @param {String} style - style (either "fill" or "stroke") - * @returns {HTMLCanvasElement} - the created offscreen canvas - */ -Sparticles.prototype.drawOffscreenCanvasForCircle = function(canvas, ctx, color, style) { - const size = this.settings.maxSize; - const lineSize = this.getLineSize(size); - const glowSize = this.getGlowSize(size); - const canvasSize = size + lineSize + glowSize; - canvas.width = canvasSize; - canvas.height = canvasSize; - this.renderGlow(ctx, color, size); - this.renderStyle(ctx, color, lineSize, style); - ctx.beginPath(); - ctx.ellipse(canvasSize / 2, canvasSize / 2, size / 2, size / 2, 0, 0, 360); - this.renderColor(ctx, style); - return canvas; -}; - -/** - * set up the needed array for referencing the images in the Sparticle() - * instance, then loop through each image and load it before running the callback - * @param {String} color - the color of the image that we're loading - * @param {Function} callback - callback function to run after images load - */ -Sparticles.prototype.loadAndDrawImages = function(color, callback) { - const imgUrls = this.settings.imageUrl; - const imageUrls = Array.isArray(imgUrls) ? imgUrls : [imgUrls]; - const imageCount = imageUrls.length; - let imagesLoaded = 0; - this.images = []; - - imageUrls.forEach((imageUrl, i) => { - this.images.push("image" + i); - this.canvasses[color]["image" + i] = document.createElement("canvas"); - const canvas = this.canvasses[color]["image" + i]; - const ctx = canvas.getContext("2d"); - const image = new Image(); - - image.onload = () => { - imagesLoaded++; - this.drawImageOffscreenCanvas(image, canvas, ctx, color); - if (callback && imagesLoaded === imageCount) { - callback(); - } - }; - - image.onerror = () => { - console.error("failed to load source image: ", imageUrl); - }; - - image.src = imageUrl; - }); -}; - /** * create, setup and render an offscreen canvas for a * Custom Image Particle of the given color * @param {HTMLImageElement} image - the image element that has loaded - * @param {HTMLCanvasElement} canvas - the canvas element - * @param {CanvasRenderingContext2D} ctx - the canvas context * @param {String} color - the color to fill/stroke with + * @param {HTMLCanvasElement} canvas - the canvas element * @returns {HTMLCanvasElement} - the created offscreen canvas */ -Sparticles.prototype.drawImageOffscreenCanvas = function(image, canvas, ctx, color) { +Sparticles.prototype.drawOffscreenCanvasForImage = function(image, color, canvas) { const size = image.width; + const ctx = canvas.getContext("2d"); canvas.width = size; canvas.height = size; ctx.drawImage(image, 0, 0, size, size, 0, 0, size, size);