From 8e33ff530a223f246da028fd3380f49571a531a7 Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Fri, 25 Feb 2011 15:34:39 -0500 Subject: [PATCH] Add snowstorm --- app/views/layouts/application.html.erb | 2 +- public/javascripts/snowstorm.js | 528 +++++++++++++++++++++++++ 2 files changed, 529 insertions(+), 1 deletion(-) create mode 100644 public/javascripts/snowstorm.js diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index ce0815f..b82dd90 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -6,7 +6,7 @@ <%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" %> <%= javascript_include_tag "http://maps.google.com/maps/api/js?sensor=false" %> <%= javascript_include_tag "maps" %> - <%= javascript_include_tag "geojson/GeoJSON" %> + <%= javascript_include_tag "snowstorm" %> <%= csrf_meta_tag %> diff --git a/public/javascripts/snowstorm.js b/public/javascripts/snowstorm.js new file mode 100644 index 0000000..8f87407 --- /dev/null +++ b/public/javascripts/snowstorm.js @@ -0,0 +1,528 @@ +/** @license + * DHTML Snowstorm! JavaScript-based Snow for web pages + * -------------------------------------------------------- + * Version 1.41.20101113 (Previous rev: 1.4.20091115) + * Copyright (c) 2007, Scott Schiller. All rights reserved. + * Code provided under the BSD License: + * http://schillmania.com/projects/snowstorm/license.txt + */ + +/*global window, document, navigator, clearInterval, setInterval */ +/*jslint white: false, onevar: true, plusplus: false, undef: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */ + +var snowStorm = (function(window, document) { + + // --- common properties --- + + this.flakesMax = 128; // Limit total amount of snow made (falling + sticking) + this.flakesMaxActive = 64; // Limit amount of snow falling at once (less = lower CPU use) + this.animationInterval = 33; // Theoretical "miliseconds per frame" measurement. 20 = fast + smooth, but high CPU use. 50 = more conservative, but slower + this.excludeMobile = true; // Snow is likely to be bad news for mobile phones' CPUs (and batteries.) By default, be nice. + this.flakeBottom = null; // Integer for Y axis snow limit, 0 or null for "full-screen" snow effect + this.followMouse = true; // Snow movement can respond to the user's mouse + this.snowColor = '#fff'; // Don't eat (or use?) yellow snow. + this.snowCharacter = '•'; // • = bullet, · is square on some systems etc. + this.snowStick = true; // Whether or not snow should "stick" at the bottom. When off, will never collect. + this.targetElement = null; // element which snow will be appended to (null = document.body) - can be an element ID eg. 'myDiv', or a DOM node reference + this.useMeltEffect = true; // When recycling fallen snow (or rarely, when falling), have it "melt" and fade out if browser supports it + this.useTwinkleEffect = false; // Allow snow to randomly "flicker" in and out of view while falling + this.usePositionFixed = false; // true = snow does not shift vertically when scrolling. May increase CPU load, disabled by default - if enabled, used only where supported + + // --- less-used bits --- + + this.freezeOnBlur = true; // Only snow when the window is in focus (foreground.) Saves CPU. + this.flakeLeftOffset = 0; // Left margin/gutter space on edge of container (eg. browser window.) Bump up these values if seeing horizontal scrollbars. + this.flakeRightOffset = 0; // Right margin/gutter space on edge of container + this.flakeWidth = 8; // Max pixel width reserved for snow element + this.flakeHeight = 8; // Max pixel height reserved for snow element + this.vMaxX = 5; // Maximum X velocity range for snow + this.vMaxY = 4; // Maximum Y velocity range for snow + this.zIndex = 0; // CSS stacking order applied to each snowflake + + // --- End of user section --- + + var s = this, storm = this, i, + // UA sniffing and backCompat rendering mode checks for fixed position, etc. + isIE = navigator.userAgent.match(/msie/i), + isIE6 = navigator.userAgent.match(/msie 6/i), + isWin98 = navigator.appVersion.match(/windows 98/i), + isMobile = navigator.userAgent.match(/mobile/i), + isBackCompatIE = (isIE && document.compatMode === 'BackCompat'), + noFixed = (isMobile || isBackCompatIE || isIE6), + screenX = null, screenX2 = null, screenY = null, scrollY = null, vRndX = null, vRndY = null, + windOffset = 1, + windMultiplier = 2, + flakeTypes = 6, + fixedForEverything = false, + opacitySupported = (function(){ + try { + document.createElement('div').style.opacity = '0.5'; + } catch(e) { + return false; + } + return true; + }()), + didInit = false, + docFrag = document.createDocumentFragment(); + + this.timers = []; + this.flakes = []; + this.disabled = false; + this.active = false; + this.meltFrameCount = 20; + this.meltFrames = []; + + this.events = (function() { + + var old = (!window.addEventListener && window.attachEvent), slice = Array.prototype.slice, + evt = { + add: (old?'attachEvent':'addEventListener'), + remove: (old?'detachEvent':'removeEventListener') + }; + + function getArgs(oArgs) { + var args = slice.call(oArgs), len = args.length; + if (old) { + args[1] = 'on' + args[1]; // prefix + if (len > 3) { + args.pop(); // no capture + } + } else if (len === 3) { + args.push(false); + } + return args; + } + + function apply(args, sType) { + var element = args.shift(), + method = [evt[sType]]; + if (old) { + element[method](args[0], args[1]); + } else { + element[method].apply(element, args); + } + } + + function addEvent() { + apply(getArgs(arguments), 'add'); + } + + function removeEvent() { + apply(getArgs(arguments), 'remove'); + } + + return { + add: addEvent, + remove: removeEvent + }; + + }()); + + function rnd(n,min) { + if (isNaN(min)) { + min = 0; + } + return (Math.random()*n)+min; + } + + function plusMinus(n) { + return (parseInt(rnd(2),10)===1?n*-1:n); + } + + this.randomizeWind = function() { + vRndX = plusMinus(rnd(s.vMaxX,0.2)); + vRndY = rnd(s.vMaxY,0.2); + if (this.flakes) { + for (var i=0; i=0 && s.vX<0.2) { + s.vX = 0.2; + } else if (s.vX<0 && s.vX>-0.2) { + s.vX = -0.2; + } + if (s.vY>=0 && s.vY<0.2) { + s.vY = 0.2; + } + }; + + this.move = function() { + var vX = s.vX*windOffset, yDiff; + s.x += vX; + s.y += (s.vY*s.vAmp); + if (s.x >= screenX || screenX-s.x < storm.flakeWidth) { // X-axis scroll check + s.x = 0; + } else if (vX < 0 && s.x-storm.flakeLeftOffset < 0-storm.flakeWidth) { + s.x = screenX-storm.flakeWidth-1; // flakeWidth; + } + s.refresh(); + yDiff = screenY+scrollY-s.y; + if (yDiff0.998) { + // ~1/1000 chance of melting mid-air, with each frame + s.melting = true; + s.melt(); + // only incrementally melt one frame + // s.melting = false; + } + if (storm.useTwinkleEffect) { + if (!s.twinkleFrame) { + if (Math.random()>0.9) { + s.twinkleFrame = parseInt(Math.random()*20,10); + } + } else { + s.twinkleFrame--; + s.o.style.visibility = (s.twinkleFrame && s.twinkleFrame%2===0?'hidden':'visible'); + } + } + } + }; + + this.animate = function() { + // main animation loop + // move, check status, die etc. + s.move(); + }; + + this.setVelocities = function() { + s.vX = vRndX+rnd(storm.vMaxX*0.12,0.1); + s.vY = vRndY+rnd(storm.vMaxY*0.12,0.1); + }; + + this.setOpacity = function(o,opacity) { + if (!opacitySupported) { + return false; + } + o.style.opacity = opacity; + }; + + this.melt = function() { + if (!storm.useMeltEffect || !s.melting) { + s.recycle(); + } else { + if (s.meltFrame < s.meltFrameCount) { + s.meltFrame++; + s.setOpacity(s.o,s.meltFrames[s.meltFrame]); + s.o.style.fontSize = s.fontSize-(s.fontSize*(s.meltFrame/s.meltFrameCount))+'px'; + s.o.style.lineHeight = storm.flakeHeight+2+(storm.flakeHeight*0.75*(s.meltFrame/s.meltFrameCount))+'px'; + } else { + s.recycle(); + } + } + }; + + this.recycle = function() { + s.o.style.display = 'none'; + s.o.style.position = (fixedForEverything?'fixed':'absolute'); + s.o.style.bottom = 'auto'; + s.setVelocities(); + s.vCheck(); + s.meltFrame = 0; + s.melting = false; + s.setOpacity(s.o,1); + s.o.style.padding = '0px'; + s.o.style.margin = '0px'; + s.o.style.fontSize = s.fontSize+'px'; + s.o.style.lineHeight = (storm.flakeHeight+2)+'px'; + s.o.style.textAlign = 'center'; + s.o.style.verticalAlign = 'baseline'; + s.x = parseInt(rnd(screenX-storm.flakeWidth-20),10); + s.y = parseInt(rnd(screenY)*-1,10)-storm.flakeHeight; + s.refresh(); + s.o.style.display = 'block'; + s.active = 1; + }; + + this.recycle(); // set up x/y coords etc. + this.refresh(); + + }; + + this.snow = function() { + var active = 0, used = 0, waiting = 0, flake = null, i; + for (i=s.flakes.length; i--;) { + if (s.flakes[i].active === 1) { + s.flakes[i].move(); + active++; + } else if (s.flakes[i].active === 0) { + used++; + } else { + waiting++; + } + if (s.flakes[i].melting) { + s.flakes[i].melt(); + } + } + if (actives.flakesMaxActive) { + s.flakes[s.flakes.length-1].active = -1; + } + } + storm.targetElement.appendChild(docFrag); + }; + + this.timerInit = function() { + s.timers = (!isWin98?[setInterval(s.snow,s.animationInterval)]:[setInterval(s.snow,s.animationInterval*3),setInterval(s.snow,s.animationInterval)]); + }; + + this.init = function() { + for (var i=0; i