UNPKG

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