PageSourceSearch

https://flagman.co.jp/common/js/particles.js

js flagman.co.jp collected 2026-10-07 12:19:20 UTC 13,932 bytes, 559 lines download raw bytes

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.