UNPKG

magic-snowflakes

Version:
434 lines (345 loc) 14.5 kB
/*! Snowflakes | © 2018 Denis Seleznev | MIT License | https://github.com/hcodes/snowflakes/ */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.Snowflakes = factory()); }(this, (function () { 'use strict'; var animationPrefix = ''; if (typeof window !== 'undefined') { animationPrefix = Array.prototype.slice.call(window.getComputedStyle(document.documentElement, '')).join(',').search(/,animation/) > -1 ? '' : 'Webkit'; } /** * Set inline style. * * @param {DOMElement} dom * @param {Object} props */ function setStyle(dom, props) { Object.keys(props).forEach(function (originalKey) { var key = originalKey; if (animationPrefix && originalKey.search('animation') > -1) { key = animationPrefix + originalKey[0].toUpperCase() + originalKey.substr(1); } dom.style[key] = props[originalKey]; }); } /** * Show DOM element. * * @param {DOMElement} dom */ function showElement(dom) { setStyle(dom, { display: 'block' }); } /** * Hide DOM element. * * @param {DOMElement} dom */ function hideElement(dom) { setStyle(dom, { display: 'none' }); } /** * Get random number. * * @param {number} from * @param {number} max * * @returns {number} */ function getRandom(from, max) { return from + Math.floor(Math.random() * (max - from)); } /** * Linear interpolation. * * @param {number} x * @param {number} x1 * @param {number} x2 * @param {number} y1 * @param {number} y2 * * @returns {number} */ function interpolation(x, x1, x2, y1, y2) { return y1 + (y2 - y1) * (x - x1) / (x2 - x1); } var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var Flake = function () { /** * @constructor * * @param {DOMElement} container * @param {number} containerHeight * @param {Object} params * @param {number} [params.count] * @param {number} [params.speed] * @param {boolean} [params.rotation] * @param {number} [params.minOpacity] * @param {number} [params.maxOpacity] * @param {number} [params.minSize] * @param {number} [params.maxSize] * @param {number} [params.types] * @param {number} [params.wind] * @param {number} [params.zIndex] */ function Flake(container, containerHeight, params) { classCallCheck(this, Flake); var isEqual = params.minSize === params.maxSize; this.innerSize = isEqual ? 0 : getRandom(0, Flake.maxInnerSize); this.size = Flake.calcSize(this.innerSize, params); var flake = document.createElement('div'), innerFlake = document.createElement('div'), animationProps = this.getAnimationProps(containerHeight, params), styleProps = { animationDelay: animationProps.animationDelay, animationDuration: animationProps.animationDuration, left: Math.random() * 99 + '%', marginTop: -Math.sqrt(2) * this.size + 'px', width: this.size + 'px', height: this.size + 'px' }; if (!isEqual) { styleProps.zIndex = params.zIndex + this.size * 10; styleProps.opacity = interpolation(this.size, params.minSize, params.maxSize, params.minOpacity, params.maxOpacity); } setStyle(flake, styleProps); setStyle(innerFlake, { animationName: 'snowflake_x_' + this.innerSize, animationDelay: Math.random() + 's' }); flake.classList.add('snowflake'); innerFlake.classList.add('snowflake__inner'); if (params.types) { innerFlake.classList.add('snowflake__inner_type_' + getRandom(0, params.types)); } if (params.wind) { innerFlake.classList.add('snowflake__inner_wind'); } if (params.rotation) { innerFlake.classList.add('snowflake__inner_rotation' + (Math.random() > 0.5 ? '' : '_reverse')); } flake.appendChild(innerFlake); this._elem = flake; container.appendChild(flake); } /** * Calc size. * * @param {number} innerSize * @param {Object} params * * @returns {number} */ Flake.calcSize = function calcSize(innerSize, params) { return Math.floor(interpolation(innerSize, 0, Flake.maxInnerSize, params.minSize, params.maxSize)); }; /** * Get animation properties. * * @param {number} containerHeight * @param {Object} params * * @returns {Object} */ Flake.prototype.getAnimationProps = function getAnimationProps(containerHeight, params) { var speedMax = containerHeight / 50 / params.speed, speedMin = speedMax / 3; return { animationDelay: Math.random() * speedMax + 's', animationDuration: interpolation(this.size, params.minSize, params.maxSize, speedMax, speedMin) + 's' }; }; /** * Resize a flake. * * @param {number} containerHeight * @param {Object} params */ Flake.prototype.resize = function resize(containerHeight, params) { var props = this.getAnimationProps(containerHeight, params); setStyle(this._elem, props); }; /** * Destroy a flake. */ Flake.prototype.destroy = function destroy() { delete this._elem; }; return Flake; }(); Flake.maxInnerSize = 20; var 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}.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)}}'; var imagesStyle = ''; var Snowflakes = function () { /** * @constructor * * @param {Object} params * * @param {DOMElem} [params.container=document.body] * @param {number} [params.count=50] * @param {number} [params.color="#5ECDEF"] * @param {number} [params.minOpacity=0.6] * @param {number} [params.maxOpacity=1] * @param {number} [params.minSize=8] * @param {number} [params.maxSize=18] * @param {boolean} [params.rotation=true] * @param {number} [params.speed=1] * @param {boolean} [params.stop=false] * @param {number} [params.types=6] * @param {number} [params.width=width of container] * @param {number} [params.height=height of container] * @param {boolean} [params.wind=true] * @param {number} [params.zIndex=9999] */ function Snowflakes(params) { var _this = this; classCallCheck(this, Snowflakes); this.params = this._setParams(params); this._flakes = []; this._isBody = this.params.container === document.body; var container = this._container = document.createElement('div'); container.classList.add('snowflakes'); this._isBody && container.classList.add('snowflakes_body'); setStyle(container, { zIndex: this.params.zIndex }); this.params.container.appendChild(container); if (this.params.stop) { this.stop(); } if (!Snowflakes._mainStyleNode) { Snowflakes._mainStyleNode = this._injectStyle(mainStyle); Snowflakes._count = (Snowflakes._count || 0) + 1; } this._winHeight = this._getWindowHeight(); this._onResize = function () { _this._winHeight = _this._getWindowHeight(); var height = _this._height(); hideElement(container); _this._flakes.forEach(function (flake) { return flake.resize(height, _this.params); }); _this._updateAnimationStyle(); showElement(container); }; if (imagesStyle) { this._imagesStyleNode = this._injectStyle(imagesStyle.replace(/%7Bcolor%7D/g, encodeURIComponent(this.params.color))); } this._animationStyleNode = this._injectStyle(this._getAnimationStyle()); window.addEventListener('resize', this._onResize, false); for (var i = 0; i < this.params.count; i++) { this._flakes.push(new Flake(container, this._height(), this.params)); } } /** * Destroy flakes. */ Snowflakes.prototype.destroy = function destroy() { this._removeStyle(); this._container && this._container.parentNode.removeChild(this._container); delete this._container; window.removeEventListener('resize', this._onResize, false); this._flakes.forEach(function (flake) { return flake.destroy(); }); delete this._flakes; delete this.params; }; /** * Start CSS animation. */ Snowflakes.prototype.start = function start() { this._container.classList.remove('snowflakes_paused'); }; /** * Stop CSS animation. */ Snowflakes.prototype.stop = function stop() { this._container.classList.add('snowflakes_paused'); }; Snowflakes.prototype._setParams = function _setParams(params) { params = params || {}; var result = {}; [['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) { var name = item[0], defaultValue = item[1]; if (typeof defaultValue === 'boolean') { result[name] = name in params ? params[name] : defaultValue; } else { result[name] = params[name] || defaultValue; } }); return result; }; Snowflakes.prototype._getAnimationStyle = function _getAnimationStyle() { var height = this._height() + this.params.maxSize + 'px'; var css = '@-webkit-keyframes snowflake_y{from{-webkit-transform:translateY(0px)}to{-webkit-transform:translateY(' + height + ');}}\n@keyframes snowflake_y{from{transform:translateY(0px)}to{transform:translateY(' + height + ')}}'; for (var i = 0; i <= Flake.maxInnerSize; i++) { var left = (Flake.calcSize(i, this.params) - this.params.minSize) * 4 + 'px'; css += '@-webkit-keyframes snowflake_x_' + i + '{from{-webkit-transform:translateX(0px)}to{-webkit-transform:translateX(' + left + ');}}\n@keyframes snowflake_x_' + i + '{from{transform:translateX(0px)}to{transform:translateX(' + left + ')}}'; } return css; }; Snowflakes.prototype._updateAnimationStyle = function _updateAnimationStyle() { this._injectStyle(this._getAnimationStyle(), this._animationStyleNode); }; Snowflakes.prototype._injectStyle = function _injectStyle(style, styleNode) { if (!styleNode) { styleNode = document.createElement('style'); document.body.appendChild(styleNode); } if (styleNode.styleSheet) { // IE styleNode.styleSheet.cssText = style; } else if ('textContent' in styleNode) { styleNode.textContent = style; } else { styleNode.innerHTML = style; } return styleNode; }; Snowflakes.prototype._removeStyle = function _removeStyle() { Snowflakes._count--; if (Snowflakes._count <= 0) { Snowflakes._count = 0; if (Snowflakes._mainStyleNode) { Snowflakes._mainStyleNode.parentNode.removeChild(Snowflakes._mainStyleNode); delete Snowflakes._mainStyleNode; } } if (this._animationStyleNode) { this._animationStyleNode.parentNode.removeChild(this._animationStyleNode); delete this._animationStyleNode; } if (this._imagesStyleNode) { this._imagesStyleNode.parentNode.removeChild(this._imagesStyleNode); delete this._imagesStyleNode; } }; Snowflakes.prototype._height = function _height() { return this.params.height || (this._isBody ? this._winHeight : this.params.container.offsetHeight + this.params.maxSize); }; Snowflakes.prototype._getWindowHeight = function _getWindowHeight() { var body = document.body, docElement = document.documentElement; var height = void 0; if (window.innerHeight) { height = window.innerHeight; } else if (docElement && docElement.clientHeight) { height = docElement.clientHeight; } else if (body) { height = body.clientHeight; } return height; }; return Snowflakes; }(); var snowflakes = function (params) { return new Snowflakes(params); }; return snowflakes; })));