├── snowfall.css ├── README.md ├── LICENSE └── snowfall.js /snowfall.css: -------------------------------------------------------------------------------- 1 | .snowflake { 2 | position: absolute; 3 | color: #fff; 4 | line-height: 1; 5 | pointer-events: none; 6 | -webkit-user-select: none; 7 | -moz-user-select: none; 8 | -ms-user-select: none; 9 | user-select: none; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | 14 | .snowflake-solid { 15 | -webkit-border-radius: 50%; 16 | -moz-border-radius: 50%; 17 | border-radius: 50%; 18 | background: #fff; 19 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Snowfall.js 2 | =========== 3 | 4 | Let It Snow... on any page with CSS3 transforms and transitions in modern browsers. 5 | 6 | ## Demo ## 7 | http://cople.github.io/Snowfall.js/ 8 | 9 | ## Usage ## 10 | ```javascript 11 | var snow = new Snowfall(); 12 | ``` 13 | or 14 | ```javascript 15 | var snow = new Snowfall({ 16 | type: "image", 17 | conetnt: ["img/flake1.png", "img/flake2.png"], 18 | maxSize: 40 19 | }); 20 | ``` 21 | 22 | ## Functions ## 23 | ```javascript 24 | snow.config({/* options */}); 25 | snow.play(); 26 | snow.stop(); 27 | ``` 28 | 29 | ## Options ## 30 | ```javascript 31 | var defaultOptions = { 32 | minSize: 10, 33 | maxSize: 30, 34 | type: "text", 35 | content: "❄", 36 | fadeOut: true, 37 | autoplay: true, 38 | interval: 200 39 | }; 40 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Cople 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /snowfall.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Snowfall.js v1.1 (http://cople.github.io/Snowfall.js/) 3 | */ 4 | (function(window, document, undefined) { 5 | 6 | "use strict"; 7 | 8 | var winWidth = window.innerWidth, 9 | winHeight = window.innerHeight, 10 | defaultOptions = { 11 | minSize: 10, 12 | maxSize: 30, 13 | type: "text", 14 | content: "❄", 15 | fadeOut: true, 16 | autoplay: true, 17 | interval: 200 18 | }; 19 | 20 | function cssPrefix(propertyName) { 21 | var capitalizePropertyName = propertyName.charAt(0).toUpperCase() + propertyName.slice(1), 22 | tempDiv = document.createElement("div"), 23 | style = tempDiv.style, 24 | vendorPrefixes = ["Webkit", "Moz", "ms", "O"]; 25 | 26 | if (propertyName in style) return propertyName; 27 | 28 | for (var i = 0, l = vendorPrefixes.length; i < l; i++) { 29 | var name = vendorPrefixes[i] + capitalizePropertyName; 30 | if (name in style) return name; 31 | }; 32 | 33 | return null; 34 | }; 35 | 36 | var cssPrefixedNames = { 37 | "transform": cssPrefix("transform"), 38 | "transition": cssPrefix("transition") 39 | }, 40 | transitionendEventName = { 41 | "WebkitTransition": "webkitTransitionEnd", 42 | "OTransition": "oTransitionEnd", 43 | "Moztransition": "transitionend", 44 | "transition": "transitionend" 45 | }[cssPrefixedNames.transition]; 46 | 47 | function random(min, max, deviation) { 48 | if (deviation) { 49 | deviation *= max; 50 | max = max + deviation; 51 | min = max - deviation; 52 | } else { 53 | min = min || 0; 54 | }; 55 | return parseInt(Math.random() * (max - min + 1) + min); 56 | }; 57 | 58 | function extend(target, source) { 59 | for (var prop in source) { 60 | target[prop] = source[prop]; 61 | }; 62 | return target; 63 | }; 64 | 65 | function setStyle(element, rules) { 66 | for (var name in rules) { 67 | element.style[cssPrefixedNames[name] || name] = rules[name]; 68 | }; 69 | }; 70 | 71 | var hidden, visibilityChange; 72 | if (typeof document.hidden !== "undefined") { 73 | hidden = "hidden"; 74 | visibilityChange = "visibilitychange"; 75 | } else if (typeof document.mozHidden !== "undefined") { 76 | hidden = "mozHidden"; 77 | visibilityChange = "mozvisibilitychange"; 78 | } else if (typeof document.msHidden !== "undefined") { 79 | hidden = "msHidden"; 80 | visibilityChange = "msvisibilitychange"; 81 | } else if (typeof document.webkitHidden !== "undefined") { 82 | hidden = "webkitHidden"; 83 | visibilityChange = "webkitvisibilitychange"; 84 | }; 85 | 86 | window.addEventListener("resize", function() { 87 | winHeight = window.innerHeight; 88 | winWidth = window.innerWidth; 89 | }, false); 90 | 91 | function Snowfall(newOptions) { 92 | 93 | var _ = this, 94 | queue = [], 95 | options = defaultOptions, 96 | $snowfield = document.createElement("div"), 97 | isImage, cntLength, $snowflake, timer; 98 | 99 | _.config = function(newOptions) { 100 | extend(options, newOptions); 101 | 102 | isImage = options.type == "image"; 103 | cntLength = options.content.length; 104 | 105 | $snowflake = isImage ? new Image() : document.createElement("div"); 106 | $snowflake.className = "snowflake snowflake-" + options.type; 107 | $snowflake.dataset.type = options.type; 108 | }; 109 | 110 | _.config(newOptions); 111 | 112 | function Snowflake() { 113 | var _$snowflake = $snowflake.cloneNode(); 114 | if (options.type != "solid") { 115 | _$snowflake[isImage ? "src" : "innerHTML"] = typeof options.content == "string" ? options.content : options.content[cntLength == 0 ? 0 : Math.floor(Math.random() * cntLength)]; 116 | }; 117 | 118 | return _$snowflake; 119 | }; 120 | 121 | function snowAnimate() { 122 | var size = random(options.minSize, options.maxSize), 123 | top = -2 * size, 124 | left = random(0, winWidth - size), 125 | opacity = random(5, 10) / 10, 126 | angle = random(null, winHeight * 0.8, 1), 127 | translateX = random(-100, 100), 128 | translateY = winHeight + size * 2, 129 | duration = random(null, winHeight * 20, 0.2), 130 | _$snowflake; 131 | 132 | if (queue.length) { 133 | _$snowflake = queue.shift(); 134 | if (_$snowflake.dataset.type != options.type) _$snowflake = new Snowflake(); 135 | } else { 136 | _$snowflake = new Snowflake(); 137 | }; 138 | 139 | var styleRules = { 140 | "top": top + "px", 141 | "left": left + "px", 142 | "opacity": opacity, 143 | "transform": "none", 144 | "transition": duration + "ms linear" 145 | }; 146 | 147 | switch (options.type) { 148 | case "solid": 149 | styleRules.width = styleRules.height = size + "px"; 150 | break; 151 | case "text": 152 | styleRules["font-size"] = size + "px"; 153 | break; 154 | case "image": 155 | styleRules.width = size + "px"; 156 | break; 157 | }; 158 | 159 | setStyle(_$snowflake, styleRules); 160 | 161 | $snowfield.appendChild(_$snowflake); 162 | 163 | setTimeout(function() { 164 | setStyle(_$snowflake, { 165 | "transform": "translate(" + translateX + "px," + translateY + "px) rotate(" + angle + "deg)", 166 | "opacity": options.fadeOut ? 0 : opacity 167 | }); 168 | }, 100); 169 | }; 170 | 171 | _.playing = 0; 172 | 173 | _.play = function() { 174 | if (_.playing) return; 175 | timer = setInterval(snowAnimate, options.interval); 176 | _.playing = 1; 177 | }; 178 | 179 | _.stop = function() { 180 | if (!_.playing) return; 181 | clearInterval(timer); 182 | timer = null; 183 | _.playing = 0; 184 | }; 185 | 186 | document.addEventListener(visibilityChange, function() { 187 | document[hidden] ? _.stop() : _.play(); 188 | }, false); 189 | 190 | $snowfield.addEventListener(transitionendEventName, function(e) { 191 | var snowflake = e.target || e.srcElement; 192 | $snowfield.removeChild(snowflake); 193 | queue.push(snowflake); 194 | }, false); 195 | 196 | $snowfield.className = "snowfield"; 197 | document.body.appendChild($snowfield); 198 | 199 | options.autoplay && _.play(); 200 | 201 | return _; 202 | }; 203 | 204 | window.Snowfall = Snowfall; 205 | 206 | })(window, document); --------------------------------------------------------------------------------