UNPKG

16.1 kBJavaScriptView Raw
1/*! Snowflakes | © 2019 Denis Seleznev | MIT License | https://github.com/hcodes/snowflakes/ */
2(function (global, factory) {
3 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4 typeof define === 'function' && define.amd ? define(factory) :
5 (global.Snowflakes = factory());
6}(this, (function () { 'use strict';
7
8function _classCallCheck(instance, Constructor) {
9 if (!(instance instanceof Constructor)) {
10 throw new TypeError("Cannot call a class as a function");
11 }
12}
13
14function _defineProperties(target, props) {
15 for (var i = 0; i < props.length; i++) {
16 var descriptor = props[i];
17 descriptor.enumerable = descriptor.enumerable || false;
18 descriptor.configurable = true;
19 if ("value" in descriptor) descriptor.writable = true;
20 Object.defineProperty(target, descriptor.key, descriptor);
21 }
22}
23
24function _createClass(Constructor, protoProps, staticProps) {
25 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
26 if (staticProps) _defineProperties(Constructor, staticProps);
27 return Constructor;
28}
29
30function _defineProperty(obj, key, value) {
31 if (key in obj) {
32 Object.defineProperty(obj, key, {
33 value: value,
34 enumerable: true,
35 configurable: true,
36 writable: true
37 });
38 } else {
39 obj[key] = value;
40 }
41
42 return obj;
43}
44
45function _slicedToArray(arr, i) {
46 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
47}
48
49function _arrayWithHoles(arr) {
50 if (Array.isArray(arr)) return arr;
51}
52
53function _iterableToArrayLimit(arr, i) {
54 if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) {
55 return;
56 }
57
58 var _arr = [];
59 var _n = true;
60 var _d = false;
61 var _e = undefined;
62
63 try {
64 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
65 _arr.push(_s.value);
66
67 if (i && _arr.length === i) break;
68 }
69 } catch (err) {
70 _d = true;
71 _e = err;
72 } finally {
73 try {
74 if (!_n && _i["return"] != null) _i["return"]();
75 } finally {
76 if (_d) throw _e;
77 }
78 }
79
80 return _arr;
81}
82
83function _nonIterableRest() {
84 throw new TypeError("Invalid attempt to destructure non-iterable instance");
85}
86
87var animationPrefix = '';
88
89if (typeof window !== 'undefined') {
90 animationPrefix = Array.prototype.slice.call(window.getComputedStyle(document.documentElement, '')).join(',').search(/,animation/) > -1 ? '' : 'Webkit';
91}
92/**
93 * Set inline style.
94 *
95 * @param {DOMElement} dom
96 * @param {Object} props
97 */
98
99
100function setStyle(dom, props) {
101 Object.keys(props).forEach(function (originalKey) {
102 var key = originalKey;
103
104 if (animationPrefix && originalKey.search('animation') > -1) {
105 key = animationPrefix + originalKey[0].toUpperCase() + originalKey.substr(1);
106 }
107
108 dom.style[key] = props[originalKey];
109 });
110}
111/**
112 * Show DOM element.
113 *
114 * @param {DOMElement} dom
115 */
116
117function showElement(dom) {
118 setStyle(dom, {
119 display: 'block'
120 });
121}
122/**
123 * Hide DOM element.
124 *
125 * @param {DOMElement} dom
126 */
127
128function hideElement(dom) {
129 setStyle(dom, {
130 display: 'none'
131 });
132}
133/**
134 * Get window height.
135 *
136 * @returns {number}
137 */
138
139function getWindowHeight() {
140 var body = document.body,
141 docElement = document.documentElement;
142 var height;
143
144 if (window.innerHeight) {
145 height = window.innerHeight;
146 } else if (docElement && docElement.clientHeight) {
147 height = docElement.clientHeight;
148 } else if (body) {
149 height = body.clientHeight;
150 }
151
152 return height;
153}
154/**
155 * Get window height.
156 *
157 * @param {string} style
158 * @param {DOMNode} styleNode
159 *
160 * @returns {DOMNode}
161 */
162
163function injectStyle(style, styleNode) {
164 if (!styleNode) {
165 styleNode = document.createElement('style');
166 document.body.appendChild(styleNode);
167 }
168
169 if (styleNode.styleSheet) {
170 // IE
171 styleNode.styleSheet.cssText = style;
172 } else if ('textContent' in styleNode) {
173 styleNode.textContent = style;
174 } else {
175 styleNode.innerHTML = style;
176 }
177
178 return styleNode;
179}
180/**
181 * Remove DOM node.
182 *
183 * @param {DOMNode} node
184 *
185 */
186
187function removeNode(node) {
188 if (node && node.parentNode) {
189 node.parentNode.removeChild(node);
190 }
191}
192/**
193 * A DOM node is body.
194 *
195 * @param {DOMNode} node
196 *
197 * @returns {boolean}
198 */
199
200function isBody(node) {
201 return node === document.body;
202}
203/**
204 * Add classname for a node.
205 *
206 * @param {DOMNode} node
207 * @param {string} classname
208 */
209
210function addClass(node, classname) {
211 node.classList.add(classname);
212}
213/**
214 * Remove classname for a node.
215 *
216 * @param {DOMNode} node
217 * @param {string} classname
218 */
219
220function removeClass(node, classname) {
221 node.classList.remove(classname);
222}
223
224/**
225 * Get random number.
226 *
227 * @param {number} from
228 * @param {number} max
229 *
230 * @returns {number}
231 */
232function getRandom(from, max) {
233 return from + Math.floor(Math.random() * (max - from));
234}
235/**
236 * Linear interpolation.
237 *
238 * @param {number} x
239 * @param {number} x1
240 * @param {number} x2
241 * @param {number} y1
242 * @param {number} y2
243 *
244 * @returns {number}
245 */
246
247function interpolation(x, x1, x2, y1, y2) {
248 return y1 + (y2 - y1) * (x - x1) / (x2 - x1);
249}
250
251var Flake =
252/*#__PURE__*/
253function () {
254 /**
255 * @constructor
256 *
257 * @param {DOMElement} container
258 * @param {number} containerHeight
259 * @param {Object} params
260 * @param {number} [params.count]
261 * @param {number} [params.speed]
262 * @param {boolean} [params.rotation]
263 * @param {number} [params.minOpacity]
264 * @param {number} [params.maxOpacity]
265 * @param {number} [params.minSize]
266 * @param {number} [params.maxSize]
267 * @param {number} [params.types]
268 * @param {number} [params.wind]
269 * @param {number} [params.zIndex]
270 */
271 function Flake(container, containerHeight, params) {
272 _classCallCheck(this, Flake);
273
274 var isEqual = params.minSize === params.maxSize;
275 this.innerSize = isEqual ? 0 : getRandom(0, Flake.maxInnerSize);
276 this.size = Flake.calcSize(this.innerSize, params);
277 var flake = document.createElement('div'),
278 innerFlake = document.createElement('div'),
279 animationProps = this.getAnimationProps(containerHeight, params),
280 styleProps = {
281 animationDelay: animationProps.animationDelay,
282 animationDuration: animationProps.animationDuration,
283 left: Math.random() * 99 + '%',
284 marginTop: -Math.sqrt(2) * this.size + 'px',
285 width: this.size + 'px',
286 height: this.size + 'px'
287 };
288
289 if (!isEqual) {
290 styleProps.zIndex = params.zIndex + this.size * 10;
291 styleProps.opacity = interpolation(this.size, params.minSize, params.maxSize, params.minOpacity, params.maxOpacity);
292 }
293
294 setStyle(flake, styleProps);
295 setStyle(innerFlake, {
296 animationName: 'snowflake_x_' + this.innerSize,
297 animationDelay: Math.random() + 's'
298 });
299 addClass(flake, 'snowflake');
300 addClass(innerFlake, 'snowflake__inner');
301
302 if (params.types) {
303 addClass(innerFlake, 'snowflake__inner_type_' + getRandom(0, params.types));
304 }
305
306 if (params.wind) {
307 addClass(innerFlake, 'snowflake__inner_wind');
308 }
309
310 if (params.rotation) {
311 addClass(innerFlake, 'snowflake__inner_rotation' + (Math.random() > 0.5 ? '' : '_reverse'));
312 }
313
314 flake.appendChild(innerFlake);
315 this._elem = flake;
316 container.appendChild(flake);
317 }
318 /**
319 * Calc size.
320 *
321 * @param {number} innerSize
322 * @param {Object} params
323 *
324 * @returns {number}
325 */
326
327
328 _createClass(Flake, [{
329 key: "getAnimationProps",
330
331 /**
332 * Get animation properties.
333 *
334 * @param {number} containerHeight
335 * @param {Object} params
336 *
337 * @returns {Object}
338 */
339 value: function getAnimationProps(containerHeight, params) {
340 var speedMax = containerHeight / 50 / params.speed,
341 speedMin = speedMax / 3;
342 return {
343 animationDelay: Math.random() * speedMax + 's',
344 animationDuration: interpolation(this.size, params.minSize, params.maxSize, speedMax, speedMin) + 's'
345 };
346 }
347 /**
348 * Resize a flake.
349 *
350 * @param {number} containerHeight
351 * @param {Object} params
352 */
353
354 }, {
355 key: "resize",
356 value: function resize(containerHeight, params) {
357 var props = this.getAnimationProps(containerHeight, params);
358 setStyle(this._elem, props);
359 }
360 /**
361 * Destroy a flake.
362 */
363
364 }, {
365 key: "destroy",
366 value: function destroy() {
367 delete this._elem;
368 }
369 }], [{
370 key: "calcSize",
371 value: function calcSize(innerSize, params) {
372 return Math.floor(interpolation(innerSize, 0, Flake.maxInnerSize, params.minSize, params.maxSize));
373 }
374 }]);
375
376 return Flake;
377}();
378
379_defineProperty(Flake, "maxInnerSize", 20);
380
381var mainStyle = '.snowflake{position:absolute;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;pointer-events:none;-webkit-animation:snowflake_y 10s linear infinite;animation:snowflake_y 10s linear infinite;will-change:transform}.snowflake__inner,.snowflake__inner:before{position:absolute;top:0;right:0;bottom:0;left:0}.snowflake__inner:before{content:"";background-size:100% 100%}.snowflake__inner_wind{-webkit-animation:snowflake_x_8 1s ease-in-out infinite alternate;animation:snowflake_x_8 1s ease-in-out infinite alternate}.snowflake__inner_rotation:before{-webkit-animation:snowflake_rotation 2s linear infinite;animation:snowflake_rotation 2s linear infinite}.snowflake__inner_rotation_reverse:before{-webkit-animation:snowflake_rotation_reverse 2s linear infinite;animation:snowflake_rotation_reverse 2s linear infinite}.snowflakes{pointer-events:none}.snowflakes_paused .snowflake,.snowflakes_paused .snowflake__inner,.snowflakes_paused .snowflake__inner:before{-webkit-animation-play-state:paused;animation-play-state:paused}.snowflakes_body{position:fixed;top:0;left:0;width:100%;height:1px}@-webkit-keyframes snowflake_rotation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes snowflake_rotation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes snowflake_rotation_reverse{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(-1turn);transform:rotate(-1turn)}}@keyframes snowflake_rotation_reverse{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(-1turn);transform:rotate(-1turn)}}';
382var imagesStyle = '';
383
384var Snowflakes =
385/*#__PURE__*/
386function () {
387 /**
388 * @constructor
389 *
390 * @param {Object} params
391 *
392 * @param {DOMElem} [params.container=document.body]
393 * @param {number} [params.count=50]
394 * @param {number} [params.color="#5ECDEF"]
395 * @param {number} [params.minOpacity=0.6]
396 * @param {number} [params.maxOpacity=1]
397 * @param {number} [params.minSize=8]
398 * @param {number} [params.maxSize=18]
399 * @param {boolean} [params.rotation=true]
400 * @param {number} [params.speed=1]
401 * @param {boolean} [params.stop=false]
402 * @param {number} [params.types=6]
403 * @param {number} [params.width=width of container]
404 * @param {number} [params.height=height of container]
405 * @param {boolean} [params.wind=true]
406 * @param {number} [params.zIndex=9999]
407 */
408 function Snowflakes(params) {
409 var _this = this;
410
411 _classCallCheck(this, Snowflakes);
412
413 this.params = this._setParams(params);
414 this._flakes = [];
415 this._isBody = isBody(this.params.container);
416 var container = this._container = document.createElement('div');
417 addClass(container, 'snowflakes');
418 this._isBody && addClass(container, 'snowflakes_body');
419 setStyle(container, {
420 zIndex: this.params.zIndex
421 });
422 this.params.container.appendChild(container);
423
424 if (this.params.stop) {
425 this.stop();
426 }
427
428 if (!Snowflakes._mainStyleNode) {
429 Snowflakes._mainStyleNode = injectStyle(mainStyle);
430 Snowflakes._count = (Snowflakes._count || 0) + 1;
431 }
432
433 this._winHeight = getWindowHeight();
434
435 this._onResize = function () {
436 _this._winHeight = getWindowHeight();
437
438 var height = _this._height();
439
440 hideElement(container);
441
442 _this._flakes.forEach(function (flake) {
443 return flake.resize(height, _this.params);
444 });
445
446 _this._updateAnimationStyle();
447
448 showElement(container);
449 };
450
451 if (imagesStyle) {
452 this._imagesStyleNode = injectStyle(imagesStyle.replace(/\{color\}/g, encodeURIComponent(this.params.color)));
453 }
454
455 this._animationStyleNode = injectStyle(this._getAnimationStyle());
456 window.addEventListener('resize', this._onResize, false);
457
458 for (var i = 0; i < this.params.count; i++) {
459 this._flakes.push(new Flake(container, this._height(), this.params));
460 }
461 }
462 /**
463 * Destroy flakes.
464 */
465
466
467 _createClass(Snowflakes, [{
468 key: "destroy",
469 value: function destroy() {
470 this._removeStyle();
471
472 removeNode(this._container);
473 delete this._container;
474 window.removeEventListener('resize', this._onResize, false);
475
476 this._flakes.forEach(function (flake) {
477 return flake.destroy();
478 });
479
480 delete this._flakes;
481 delete this.params;
482 }
483 /**
484 * Start CSS animation.
485 */
486
487 }, {
488 key: "start",
489 value: function start() {
490 removeClass(this._container, 'snowflakes_paused');
491 }
492 /**
493 * Stop CSS animation.
494 */
495
496 }, {
497 key: "stop",
498 value: function stop() {
499 addClass(this._container, 'snowflakes_paused');
500 }
501 }, {
502 key: "_setParams",
503 value: function _setParams(params) {
504 params = params || {};
505 var result = {};
506 [['color', '#5ECDEF'], ['container', document.body], ['count', 50], ['speed', 1], ['stop', false], ['rotation', true], ['minOpacity', 0.6], ['maxOpacity', 1], ['minSize', 8], ['maxSize', 18], ['types', 6], ['width'], ['height'], ['wind', true], ['zIndex', 9999]].forEach(function (item) {
507 var _item = _slicedToArray(item, 2),
508 name = _item[0],
509 defaultValue = _item[1];
510
511 if (typeof defaultValue === 'boolean') {
512 result[name] = name in params ? params[name] : defaultValue;
513 } else {
514 result[name] = params[name] || defaultValue;
515 }
516 });
517 return result;
518 }
519 }, {
520 key: "_getAnimationStyle",
521 value: function _getAnimationStyle() {
522 var height = this._height() + this.params.maxSize + 'px';
523 var css = "@-webkit-keyframes snowflake_y{from{-webkit-transform:translateY(0px)}to{-webkit-transform:translateY(".concat(height, ");}}\n@keyframes snowflake_y{from{transform:translateY(0px)}to{transform:translateY(").concat(height, ")}}");
524
525 for (var i = 0; i <= Flake.maxInnerSize; i++) {
526 var left = (Flake.calcSize(i, this.params) - this.params.minSize) * 4 + 'px';
527 css += "@-webkit-keyframes snowflake_x_".concat(i, "{from{-webkit-transform:translateX(0px)}to{-webkit-transform:translateX(").concat(left, ");}}\n@keyframes snowflake_x_").concat(i, "{from{transform:translateX(0px)}to{transform:translateX(").concat(left, ")}}");
528 }
529
530 return css;
531 }
532 }, {
533 key: "_updateAnimationStyle",
534 value: function _updateAnimationStyle() {
535 injectStyle(this._getAnimationStyle(), this._animationStyleNode);
536 }
537 }, {
538 key: "_removeStyle",
539 value: function _removeStyle() {
540 Snowflakes._count--;
541
542 if (Snowflakes._count <= 0) {
543 Snowflakes._count = 0;
544 removeNode(Snowflakes._mainStyleNode);
545 delete Snowflakes._mainStyleNode;
546 }
547
548 removeNode(this._animationStyleNode);
549 delete this._animationStyleNode;
550 removeNode(this._imagesStyleNode);
551 delete this._imagesStyleNode;
552 }
553 }, {
554 key: "_height",
555 value: function _height() {
556 return this.params.height || (this._isBody ? this._winHeight : this.params.container.offsetHeight + this.params.maxSize);
557 }
558 }]);
559
560 return Snowflakes;
561}();
562
563function index (params) {
564 return new Snowflakes(params);
565}
566
567return index;
568
569})));