vendor: 13,932 bytes, lines 1-559
1/*! 2 * A lightweight, dependency-free and responsive javascript plugin for particle backgrounds. 3 * 4 * @author Marc Bruederlin <[email protected]> 5 * @version 2.2.3 6 * @license MIT 7 * @see https://github.com/marcbruederlin/particles.js 8 */ 9 10/* exported Particles */ 11var Particles = (function(window, document) { 12 'use strict'; 13 14 var Plugin, Particle = {}; 15 16 function particleCompareFunc(p1, p2) { 17 if (p1.x < p2.x) { 18 return -1; 19 } else if (p1.x > p2.x) { 20 return 1; 21 } else if (p1.y < p2.y) { 22 return -1; 23 } else if (p1.y > p2.y) { 24 return 1; 25 } 26 27 return 0; 28 } 29 30 /** 31 * Represents the plugin. 32 * 33 * @constructor 34 */ 35 Plugin = (function() { 36 function Plugin() { 37 var _ = this; 38 39 _.defaults = { 40 responsive: null, 41 selector: null, 42 maxParticles: 100, 43 sizeVariations: 3, 44 showParticles: true, 45 speed: 0.5, 46 color: '#000000', 47 minDistance: 120, 48 connectParticles: false, 49 }; 50 51 _.element = null; 52 _.context = null; 53 _.ratio = null; 54 _.breakpoints = []; 55 _.activeBreakpoint = null; 56 _.breakpointSettings = []; 57 _.originalSettings = null; 58 _.storage = []; 59 _.usingPolyfill = false; 60 } 61 62 return Plugin; 63 }()); 64 65 /** 66 * Public mehtod to initialize the plugin with user settings. 67 * 68 * @public 69 * @param {object} settings 70 */ 71 Plugin.prototype.init = function(settings) { 72 var _ = this; 73 74 _.options = _._extend(_.defaults, settings); 75 _.originalSettings = JSON.parse(JSON.stringify(_.options)); 76 77 _._animate = _._animate.bind(_); 78 79 _._initializeCanvas(); 80 _._initializeEvents(); 81 _._registerBreakpoints(); 82 _._checkResponsive(); 83 _._initializeStorage(); 84 _._animate(); 85 86 return _; 87 }; 88 89 /** 90 * Public method to destroy the plugin. 91 * 92 * @public 93 */ 94 Plugin.prototype.destroy = function() { 95 var _ = this; 96 97 _.storage = []; 98 _.element.remove(); 99 100 window.removeEventListener('resize', _.listener, false); 101 window.clearTimeout(_._animation); 102 cancelAnimationFrame(_._animation); 103 }; 104 105 /** 106 * Setup the canvas element. 107 * 108 * @private 109 */ 110 Plugin.prototype._initializeCanvas = function() { 111 var _ = this, devicePixelRatio, backingStoreRatio; 112 113 if(!_.options.selector) { 114 console.warn('particles.js: No selector specified! Check https://github.com/marcbruederlin/particles.js#options'); 115 return false; 116 } 117 118 _.element = document.querySelector(_.options.selector); 119 _.context = _.element.getContext('2d'); 120 121 devicePixelRatio = window.devicePixelRatio || 1; 122 backingStoreRatio = _.context.webkitBackingStorePixelRatio || _.context.mozBackingStorePixelRatio || _.context.msBackingStorePixelRatio || 123 _.context.oBackingStorePixelRatio || _.context.backingStorePixelRatio || 1; 124 125 _.ratio = devicePixelRatio / backingStoreRatio; 126 _.element.width = (_.element.offsetParent) ? _.element.offsetParent.clientWidth * _.ratio : _.element.clientWidth * _.ratio; 127 128 if (_.element.offsetParent && _.element.offsetParent.nodeName === 'BODY') { 129 _.element.height = window.innerHeight * _.ratio; 130 } else { 131 _.element.height = (_.element.offsetParent) ? _.element.offsetParent.clientHeight * _.ratio : _.element.clientHeight * _.ratio; 132 } 133 _.element.style.width = '100%'; 134 _.element.style.height = '100%'; 135 136 _.context.scale(_.ratio, _.ratio); 137 }; 138 139 /** 140 * Register event listeners. 141 * 142 * @private 143 */ 144 Plugin.prototype._initializeEvents = function() { 145 var _ = this; 146 147 _.listener = function() { _._resize(); }.bind(this); 148 window.addEventListener('resize', _.listener, false); 149 }; 150 151 /** 152 * Initialize the particle storage. 153 * 154 * @private 155 */ 156 Plugin.prototype._initializeStorage = function() { 157 var _ = this; 158 159 _.storage = []; 160 161 for(var i = _.options.maxParticles; i--;) { 162 _.storage.push(new Particle(_.context, _.options)); 163 } 164 }; 165 166 /** 167 * Register responsive breakpoints if the user declared some. 168 * 169 * @private 170 */ 171 Plugin.prototype._registerBreakpoints = function() { 172 var _ = this, breakpoint, currentBreakpoint, l, responsiveSettings = _.options.responsive || null; 173 174 if(typeof responsiveSettings === 'object' && responsiveSettings !== null && responsiveSettings.length) { 175 for(breakpoint in responsiveSettings) { 176 l = _.breakpoints.length - 1; 177 currentBreakpoint = responsiveSettings[breakpoint].breakpoint; 178 179 if(responsiveSettings.hasOwnProperty(breakpoint)) { 180 while(l >= 0) { 181 if(_.breakpoints[l] && _.breakpoints[l] === currentBreakpoint) { 182 _.breakpoints.splice(l, 1); 183 } 184 185 l--; 186 } 187 188 _.breakpoints.push(currentBreakpoint); 189 _.breakpointSettings[currentBreakpoint] = responsiveSettings[breakpoint].options; 190 } 191 } 192 193 _.breakpoints.sort(function(a, b) { 194 return b-a; 195 }); 196 } 197 }; 198 199 /** 200 * Check if a breakpoint is active and load the breakpoints options. 201 * 202 * @private 203 */ 204 Plugin.prototype._checkResponsive = function() { 205 var _ = this, breakpoint, targetBreakpoint = false, windowWidth = window.innerWidth; 206 207 if(_.options.responsive && _.options.responsive.length && _.options.responsive !== null) { 208 targetBreakpoint = null; 209 210 for(breakpoint in _.breakpoints) { 211 if(_.breakpoints.hasOwnProperty(breakpoint)) { 212 if(windowWidth <= _.breakpoints[breakpoint]) { 213 targetBreakpoint = _.breakpoints[breakpoint]; 214 } 215 } 216 } 217 218 if(targetBreakpoint !== null) { 219 _.activeBreakpoint = targetBreakpoint; 220 _.options = _._extend(_.options, _.breakpointSettings[targetBreakpoint]); 221 } else { 222 if(_.activeBreakpoint !== null) { 223 _.activeBreakpoint = null; 224 targetBreakpoint = null; 225 226 _.options = _._extend(_.options, _.originalSettings); 227 } 228 } 229 } 230 }; 231 232 /** 233 * Rebuild the storage and update the canvas. 234 * 235 * @private 236 */ 237 Plugin.prototype._refresh = function() { 238 var _ = this; 239 240 _._initializeStorage(); 241 _._draw(); 242 }; 243 244 /** 245 * Kick off various things on window resize. 246 * 247 * @private 248 */ 249 Plugin.prototype._resize = function() { 250 var _ = this; 251 252 _.element.width = (_.element.offsetParent) ? _.element.offsetParent.clientWidth * _.ratio : _.element.clientWidth * _.ratio; 253 254 if (_.element.offsetParent && _.element.offsetParent.nodeName === 'BODY') { 255 _.element.height = window.innerHeight * _.ratio; 256 } else { 257 _.element.height = (_.element.offsetParent) ? _.element.offsetParent.clientHeight * _.ratio : _.element.clientHeight * _.ratio; 258 } 259 260 _.context.scale(_.ratio, _.ratio); 261 262 clearTimeout(_.windowDelay); 263 264 _.windowDelay = window.setTimeout(function() { 265 _._checkResponsive(); 266 _._refresh(); 267 }, 50); 268 }; 269 270 /** 271 * Animates the plugin particles by calling the draw method. 272 * 273 * @private 274 */ 275 Plugin.prototype._animate = function() { 276 var _ = this; 277 278 _._draw(); 279 _._animation = window.requestAnimFrame(_._animate); 280 }; 281 282 /** 283 * Restarts the particles animation by calling _animate. 284 * 285 * @public 286 */ 287 Plugin.prototype.resumeAnimation = function() { 288 var _ = this; 289 290 if (!_._animation) { 291 _._animate(); 292 } 293 }; 294 295 /** 296 * Pauses/stops the particle animation. 297 * 298 * @public 299 */ 300 Plugin.prototype.pauseAnimation = function() { 301 var _ = this; 302 303 if (!_._animation) { 304 return; 305 } 306 307 if (_.usingPolyfill) { 308 window.clearTimeout(_._animation); 309 } else { 310 var cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame; 311 cancelAnimationFrame(_._animation); 312 } 313 314 _._animation = null; 315 }; 316 317 /** 318 * Draws the plugin particles. 319 * 320 * @private 321 */ 322 Plugin.prototype._draw = function() { 323 var _ = this, 324 element = _.element, 325 parentWidth = (element.offsetParent) ? element.offsetParent.clientWidth : element.clientWidth, 326 parentHeight = (element.offsetParent) ? element.offsetParent.clientHeight : element.clientHeight, 327 showParticles = _.options.showParticles, 328 storage = _.storage; 329 330 if (element.offsetParent && element.offsetParent.nodeName === 'BODY') { 331 parentHeight = window.innerHeight; 332 } 333 334 _.context.clearRect(0, 0, element.width, element.height); 335 _.context.beginPath(); 336 337 for(var i = storage.length; i--;) { 338 var particle = storage[i]; 339 340 if (showParticles) { 341 particle._draw(); 342 } 343 344 particle._updateCoordinates(parentWidth, parentHeight); 345 } 346 347 if (_.options.connectParticles) { 348 storage.sort(particleCompareFunc); 349 _._updateEdges(); 350 } 351 }; 352 353 /** 354 * Updates the edges. 355 * 356 * @private 357 */ 358 Plugin.prototype._updateEdges = function() { 359 var _ = this, 360 minDistance = _.options.minDistance, 361 sqrt = Math.sqrt, 362 abs = Math.abs, 363 storage = _.storage, 364 storageLength = storage.length; 365 366 for(var i = 0; i < storageLength; i++) { 367 var p1 = storage[i]; 368 369 for(var j = i + 1; j < storageLength; j++) { 370 var p2 = storage[j], 371 distance, r = p1.x - p2.x, dy = p1.y - p2.y; 372 373 distance = sqrt(r * r + dy * dy); 374 375 if (abs(r) > minDistance) { 376 break; 377 } 378 379 if (distance <= minDistance) { 380 _._drawEdge(p1, p2, (1.2 - distance/minDistance)); 381 } 382 } 383 } 384 }; 385 386 /** 387 * Draws an edge between two points. 388 * 389 * @private 390 * @param {Particle} p1 391 * @param {Particle} p2 392 * @param {number} opacity 393 */ 394 Plugin.prototype._drawEdge = function(p1, p2, opacity) { 395 var _ = this, 396 gradient = _.context.createLinearGradient(p1.x, p1.y, p2.x, p2.y); 397 398 var color1 = this._hex2rgb(p1.color); 399 var color2 = this._hex2rgb(p2.color); 400 401 gradient.addColorStop(0, 'rgba(' + color1.r + ',' + color1.g + ',' + color1.b + ',' + opacity + ')'); 402 gradient.addColorStop(1, 'rgba(' + color2.r + ',' + color2.g + ',' + color2.b + ',' + opacity + ')'); 403 404 _.context.beginPath(); 405 _.context.strokeStyle = gradient; 406 _.context.moveTo(p1.x, p1.y); 407 _.context.lineTo(p2.x, p2.y); 408 _.context.stroke(); 409 _.context.fill(); 410 _.context.closePath(); 411 }; 412 413 /** 414 * Merges the keys of two objects. 415 * 416 * @private 417 * @param {object} source 418 * @param {object} obj 419 */ 420 Plugin.prototype._extend = function(source, obj) { 421 Object.keys(obj).forEach(function(key) { 422 source[key] = obj[key]; 423 }); 424 425 return source; 426 }; 427 428 /** 429 * Converts a hex string to a rgb object. 430 * 431 * @private 432 * @param {string} hex 433 * @return {object} 434 */ 435 Plugin.prototype._hex2rgb = function(hex) { 436 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); 437 438 return result ? { 439 r: parseInt(result[1], 16), 440 g: parseInt(result[2], 16), 441 b: parseInt(result[3], 16) 442 } : null; 443 }; 444 445 /** 446 * Represents a single particle. 447 * 448 * @constructor 449 * @param {object} context 450 * @param {object} options 451 */ 452 Particle = function(context, options) { 453 var _ = this, 454 random = Math.random, 455 speed = options.speed, 456 color = (options.color instanceof Array) ? options.color[Math.floor(Math.random() * options.color.length)] : options.color; 457 458 _.context = context; 459 _.options = options; 460 461 var canvas = document.querySelector(options.selector); 462 _.x = (canvas.offsetParent) ? random() * canvas.offsetParent.clientWidth : random() * canvas.clientWidth; 463 464 if (canvas.offsetParent && canvas.offsetParent.nodeName === 'BODY') { 465 _.y = random() * window.innerHeight; 466 } else { 467 _.y = (canvas.offsetParent) ? random() * canvas.offsetParent.clientHeight : random() * canvas.clientHeight; 468 } 469 470 _.vx = random() * speed * 2 - speed; 471 _.vy = random() * speed * 2 - speed; 472 _.radius = random() * random() * options.sizeVariations; 473 _.color = color; 474 475 _._draw(); 476 }; 477 478 /** 479 * The particles draw function (renders the circle). 480 * 481 * @private 482 */ 483 Particle.prototype._draw = function() { 484 var _ = this; 485 486 _.context.save(); 487 _.context.translate(_.x, _.y); 488 _.context.moveTo(0, 0); 489 _.context.beginPath(); 490 _.context.arc(0, 0, _.radius, 0, Math.PI * 2, false); 491 _.context.fillStyle = _.color; 492 _.context.fill(); 493 _.context.restore(); 494 }; 495 496 /** 497 * This updates the particles coordinates. 498 * 499 * @private 500 * @param parentWidth 501 * @param parentHeight 502 */ 503 Particle.prototype._updateCoordinates = function(parentWidth, parentHeight) { 504 var _ = this, 505 506 x = _.x + this.vx, 507 y = _.y + this.vy, 508 radius = _.radius; 509 510 if(x + radius > parentWidth) { 511 x = radius; 512 } else if(x - radius < 0) { 513 x = parentWidth - radius; 514 } 515 516 if(y + radius > parentHeight) { 517 y = radius; 518 } else if(y - radius < 0) { 519 y = parentHeight - radius; 520 } 521 522 _.x = x; 523 _.y = y; 524 }; 525 526 /** 527 * A polyfill for requestAnimFrame. 528 * 529 * @return {function} 530 */ 531 window.requestAnimFrame = (function() { 532 var _ = this, 533 requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame; 534 535 if (requestAnimationFrame) { 536 return requestAnimationFrame; 537 } 538 539 _._usingPolyfill = true; 540 541 return function(callback) { 542 return window.setTimeout(callback, 1000 / 60); 543 }; 544 })(); 545 546 return new Plugin(); 547})(window, document); 548 549(function() { 550 'use strict'; 551 552 if(typeof define === 'function' && define.amd) { 553 define('Particles', function () { return Particles; }); 554 } else if(typeof module !== 'undefined' && module.exports) { 555 module.exports = Particles; 556 } else { 557 window.Particles = Particles; 558 } 559})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.