2022-12-31 20:50:19 +01:00
/ * * @ l i c e n s e
* DHTML Snowstorm ! JavaScript - based snow for web pages
* Making it snow on the internets since 2003. You ' re welcome .
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
* Version 1.44 . 20131208 ( Previous rev : 1.44 . 20131125 )
* Copyright ( c ) 2007 , Scott Schiller . All rights reserved .
* Code provided under the BSD License
* http : //schillmania.com/projects/snowstorm/license.txt
* /
/*jslint nomen: true, plusplus: true, sloppy: true, vars: true, white: true */
/*global window, document, navigator, clearInterval, setInterval */
class SnowStorm {
2023-04-01 12:05:47 +02:00
constructor ( ) {
2022-12-31 20:50:19 +01:00
// --- common properties ---
2023-04-01 12:05:47 +02:00
this . autoStart = true ; // Whether the snow should start automatically or not.
this . excludeMobile = true ; // Snow is likely to be bad news for mobile phones' CPUs (and batteries.) Enable at your own risk.
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 = 50 ; // Theoretical "miliseconds per frame" measurement. 20 = fast + smooth, but high CPU use. 50 = more conservative, but slower
this . useGPU = true ; // Enable transform-based hardware acceleration, reduce CPU load.
this . className = null ; // CSS class name for further customization on snow elements
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 = false ; // 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
this . usePixelPosition = false ; // Whether to use pixel values for snow top/left vs. percentages. Auto-enabled if body is position:relative or targetElement is specified.
2022-12-31 20:50:19 +01:00
// --- less-used bits ---
2023-04-01 12:05:47 +02:00
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
2022-12-31 20:50:19 +01:00
// --- "No user-serviceable parts inside" past this point, yadda yadda ---
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
var storm = this ,
2023-04-01 12:05:47 +02:00
features ,
// UA sniffing and backCompat rendering mode checks for fixed position, etc.
isIE = navigator . userAgent . match ( /msie/i ) ,
isIE6 = navigator . userAgent . match ( /msie 6/i ) ,
isMobile = navigator . userAgent . match ( /mobile|opera m(ob|in)/i ) ,
isBackCompatIE = isIE && document . compatMode === "BackCompat" ,
noFixed = isBackCompatIE || isIE6 ,
screenX = null ,
screenX2 = null ,
screenY = null ,
scrollY = null ,
docHeight = null ,
vRndX = null ,
vRndY = null ,
windOffset = 1 ,
windMultiplier = 2 ,
flakeTypes = 6 ,
fixedForEverything = false ,
targetElementIsRelative = false ,
opacitySupported = ( function ( ) {
try {
document . createElement ( "div" ) . style . opacity = "0.5" ;
} catch ( e ) {
return false ;
}
return true ;
} ) ( ) ,
didInit = false ,
docFrag = document . createDocumentFragment ( ) ;
features = ( function ( ) {
2022-12-31 20:50:19 +01:00
var getAnimationFrame ;
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
/ * *
* hat tip : paul irish
* http : //paulirish.com/2011/requestanimationframe-for-smart-animating/
* https : //gist.github.com/838785
* /
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
function timeoutShim ( callback ) {
2023-04-01 12:05:47 +02:00
window . setTimeout ( callback , 1000 / ( storm . animationInterval || 20 ) ) ;
}
var _animationFrame =
window . requestAnimationFrame ||
window . webkitRequestAnimationFrame ||
window . mozRequestAnimationFrame ||
window . oRequestAnimationFrame ||
window . msRequestAnimationFrame ||
timeoutShim ;
2022-12-31 20:50:19 +01:00
// apply to window, avoid "illegal invocation" errors in Chrome
2023-04-01 12:05:47 +02:00
getAnimationFrame = _animationFrame
? function ( ) {
return _animationFrame . apply ( window , arguments ) ;
}
: null ;
2022-12-31 20:50:19 +01:00
var testDiv ;
2023-04-01 12:05:47 +02:00
testDiv = document . createElement ( "div" ) ;
2022-12-31 20:50:19 +01:00
function has ( prop ) {
// test for feature support
var result = testDiv . style [ prop ] ;
2023-04-01 12:05:47 +02:00
return result !== undefined ? prop : null ;
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
// note local scope.
var localFeatures = {
transform : {
2023-04-01 12:05:47 +02:00
ie : has ( "-ms-transform" ) ,
moz : has ( "MozTransform" ) ,
opera : has ( "OTransform" ) ,
webkit : has ( "webkitTransform" ) ,
w3 : has ( "transform" ) ,
prop : null , // the normalized property value
2022-12-31 20:50:19 +01:00
} ,
2023-04-01 12:05:47 +02:00
getAnimationFrame : getAnimationFrame ,
2022-12-31 20:50:19 +01:00
} ;
2023-04-01 12:05:47 +02:00
localFeatures . transform . prop =
localFeatures . transform . w3 ||
2022-12-31 20:50:19 +01:00
localFeatures . transform . moz ||
localFeatures . transform . webkit ||
localFeatures . transform . ie ||
2023-04-01 12:05:47 +02:00
localFeatures . transform . opera ;
2022-12-31 20:50:19 +01:00
testDiv = null ;
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
return localFeatures ;
2023-04-01 12:05:47 +02:00
} ) ( ) ;
2022-12-31 20:50:19 +01:00
this . timer = null ;
this . flakes = [ ] ;
this . disabled = false ;
this . active = false ;
this . meltFrameCount = 20 ;
this . meltFrames = [ ] ;
2023-04-01 12:05:47 +02:00
this . setXY = function ( o , x , y ) {
2022-12-31 20:50:19 +01:00
if ( ! o ) {
return false ;
}
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
if ( storm . usePixelPosition || targetElementIsRelative ) {
2023-04-01 12:05:47 +02:00
o . style . left = x - storm . flakeWidth + "px" ;
o . style . top = y - storm . flakeHeight + "px" ;
2022-12-31 20:50:19 +01:00
} else if ( noFixed ) {
2023-04-01 12:05:47 +02:00
o . style . right = 100 - ( x / screenX ) * 100 + "%" ;
2022-12-31 20:50:19 +01:00
// avoid creating vertical scrollbars
2023-04-01 12:05:47 +02:00
o . style . top = Math . min ( y , docHeight - storm . flakeHeight ) + "px" ;
2022-12-31 20:50:19 +01:00
} else {
if ( ! storm . flakeBottom ) {
// if not using a fixed bottom coordinate...
2023-04-01 12:05:47 +02:00
o . style . right = 100 - ( x / screenX ) * 100 + "%" ;
o . style . bottom = 100 - ( y / screenY ) * 100 + "%" ;
2022-12-31 20:50:19 +01:00
} else {
// absolute top.
2023-04-01 12:05:47 +02:00
o . style . right = 100 - ( x / screenX ) * 100 + "%" ;
o . style . top = Math . min ( y , docHeight - storm . flakeHeight ) + "px" ;
2022-12-31 20:50:19 +01:00
}
}
} ;
2023-04-01 12:05:47 +02:00
this . events = ( function ( ) {
var old = ! window . addEventListener && window . attachEvent ,
slice = Array . prototype . slice ,
evt = {
add : old ? "attachEvent" : "addEventListener" ,
remove : old ? "detachEvent" : "removeEventListener" ,
} ;
2022-12-31 20:50:19 +01:00
function getArgs ( oArgs ) {
2023-04-01 12:05:47 +02:00
var args = slice . call ( oArgs ) ,
len = args . length ;
2022-12-31 20:50:19 +01:00
if ( old ) {
2023-04-01 12:05:47 +02:00
args [ 1 ] = "on" + args [ 1 ] ; // prefix
2022-12-31 20:50:19 +01:00
if ( len > 3 ) {
args . pop ( ) ; // no capture
}
} else if ( len === 3 ) {
args . push ( false ) ;
}
return args ;
}
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
function apply ( args , sType ) {
var element = args . shift ( ) ,
2023-04-01 12:05:47 +02:00
method = [ evt [ sType ] ] ;
2022-12-31 20:50:19 +01:00
if ( old ) {
element [ method ] ( args [ 0 ] , args [ 1 ] ) ;
} else {
element [ method ] . apply ( element , args ) ;
}
}
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
function addEvent ( ) {
2023-04-01 12:05:47 +02:00
apply ( getArgs ( arguments ) , "add" ) ;
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
function removeEvent ( ) {
2023-04-01 12:05:47 +02:00
apply ( getArgs ( arguments ) , "remove" ) ;
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
return {
add : addEvent ,
2023-04-01 12:05:47 +02:00
remove : removeEvent ,
2022-12-31 20:50:19 +01:00
} ;
2023-04-01 12:05:47 +02:00
} ) ( ) ;
function rnd ( n , min ) {
2022-12-31 20:50:19 +01:00
if ( isNaN ( min ) ) {
min = 0 ;
}
2023-04-01 12:05:47 +02:00
return Math . random ( ) * n + min ;
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
function plusMinus ( n ) {
2023-04-01 12:05:47 +02:00
return parseInt ( rnd ( 2 ) , 10 ) === 1 ? n * - 1 : n ;
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
this . randomizeWind = function ( ) {
2022-12-31 20:50:19 +01:00
var i ;
2023-04-01 12:05:47 +02:00
vRndX = plusMinus ( rnd ( storm . vMaxX , 0.2 ) ) ;
vRndY = rnd ( storm . vMaxY , 0.2 ) ;
2022-12-31 20:50:19 +01:00
if ( this . flakes ) {
2023-04-01 12:05:47 +02:00
for ( i = 0 ; i < this . flakes . length ; i ++ ) {
2022-12-31 20:50:19 +01:00
if ( this . flakes [ i ] . active ) {
this . flakes [ i ] . setVelocities ( ) ;
}
}
}
} ;
2023-04-01 12:05:47 +02:00
this . scrollHandler = function ( ) {
2022-12-31 20:50:19 +01:00
var i ;
// "attach" snowflakes to bottom of window if no absolute bottom value was given
2023-04-01 12:05:47 +02:00
scrollY = storm . flakeBottom
? 0
: parseInt (
window . scrollY ||
document . documentElement . scrollTop ||
( noFixed ? document . body . scrollTop : 0 ) ,
10
) ;
2022-12-31 20:50:19 +01:00
if ( isNaN ( scrollY ) ) {
scrollY = 0 ; // Netscape 6 scroll fix
}
if ( ! fixedForEverything && ! storm . flakeBottom && storm . flakes ) {
2023-04-01 12:05:47 +02:00
for ( i = 0 ; i < storm . flakes . length ; i ++ ) {
2022-12-31 20:50:19 +01:00
if ( storm . flakes [ i ] . active === 0 ) {
storm . flakes [ i ] . stick ( ) ;
}
}
}
} ;
2023-04-01 12:05:47 +02:00
this . resizeHandler = function ( ) {
2022-12-31 20:50:19 +01:00
if ( window . innerWidth || window . innerHeight ) {
screenX = window . innerWidth - 16 - storm . flakeRightOffset ;
2023-04-01 12:05:47 +02:00
screenY = storm . flakeBottom || window . innerHeight ;
2022-12-31 20:50:19 +01:00
} else {
2023-04-01 12:05:47 +02:00
screenX =
( document . documentElement . clientWidth ||
document . body . clientWidth ||
document . body . scrollWidth ) -
( ! isIE ? 8 : 0 ) -
storm . flakeRightOffset ;
screenY =
storm . flakeBottom ||
document . documentElement . clientHeight ||
document . body . clientHeight ||
document . body . scrollHeight ;
2022-12-31 20:50:19 +01:00
}
docHeight = document . body . offsetHeight ;
2023-04-01 12:05:47 +02:00
screenX2 = parseInt ( screenX / 2 , 10 ) ;
2022-12-31 20:50:19 +01:00
} ;
2023-04-01 12:05:47 +02:00
this . resizeHandlerAlt = function ( ) {
2022-12-31 20:50:19 +01:00
screenX = storm . targetElement . offsetWidth - storm . flakeRightOffset ;
screenY = storm . flakeBottom || storm . targetElement . offsetHeight ;
2023-04-01 12:05:47 +02:00
screenX2 = parseInt ( screenX / 2 , 10 ) ;
2022-12-31 20:50:19 +01:00
docHeight = document . body . offsetHeight ;
} ;
2023-04-01 12:05:47 +02:00
this . freeze = function ( ) {
2022-12-31 20:50:19 +01:00
// pause animation
if ( ! storm . disabled ) {
storm . disabled = 1 ;
} else {
return false ;
}
storm . timer = null ;
} ;
2023-04-01 12:05:47 +02:00
this . resume = function ( ) {
2022-12-31 20:50:19 +01:00
if ( storm . disabled ) {
2023-04-01 12:05:47 +02:00
storm . disabled = 0 ;
2022-12-31 20:50:19 +01:00
} else {
return false ;
}
storm . timerInit ( ) ;
} ;
2023-04-01 12:05:47 +02:00
this . toggleSnow = function ( ) {
2022-12-31 20:50:19 +01:00
if ( ! storm . flakes . length ) {
// first run
storm . start ( ) ;
} else {
storm . active = ! storm . active ;
if ( storm . active ) {
storm . show ( ) ;
storm . resume ( ) ;
} else {
storm . stop ( ) ;
storm . freeze ( ) ;
}
}
} ;
2023-04-01 12:05:47 +02:00
this . stop = function ( ) {
2022-12-31 20:50:19 +01:00
var i ;
this . freeze ( ) ;
2023-04-01 12:05:47 +02:00
for ( i = 0 ; i < this . flakes . length ; i ++ ) {
this . flakes [ i ] . o . style . display = "none" ;
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
storm . events . remove ( window , "scroll" , storm . scrollHandler ) ;
storm . events . remove ( window , "resize" , storm . resizeHandler ) ;
2022-12-31 20:50:19 +01:00
if ( storm . freezeOnBlur ) {
if ( isIE ) {
2023-04-01 12:05:47 +02:00
storm . events . remove ( document , "focusout" , storm . freeze ) ;
storm . events . remove ( document , "focusin" , storm . resume ) ;
2022-12-31 20:50:19 +01:00
} else {
2023-04-01 12:05:47 +02:00
storm . events . remove ( window , "blur" , storm . freeze ) ;
storm . events . remove ( window , "focus" , storm . resume ) ;
2022-12-31 20:50:19 +01:00
}
}
} ;
2023-04-01 12:05:47 +02:00
this . show = function ( ) {
2022-12-31 20:50:19 +01:00
var i ;
2023-04-01 12:05:47 +02:00
for ( i = 0 ; i < this . flakes . length ; i ++ ) {
this . flakes [ i ] . o . style . display = "block" ;
2022-12-31 20:50:19 +01:00
}
} ;
2023-04-01 12:05:47 +02:00
this . SnowFlake = function ( type , x , y ) {
2022-12-31 20:50:19 +01:00
var s = this ;
this . type = type ;
2023-04-01 12:05:47 +02:00
this . x = x || parseInt ( rnd ( screenX - 20 ) , 10 ) ;
this . y = ! isNaN ( y ) ? y : - rnd ( screenY ) - 12 ;
2022-12-31 20:50:19 +01:00
this . vX = null ;
this . vY = null ;
2023-04-01 12:05:47 +02:00
this . vAmpTypes = [ 1 , 1.2 , 1.4 , 1.6 , 1.8 ] ; // "amplification" for vX/vY (based on flake size/type)
2022-12-31 20:50:19 +01:00
this . vAmp = this . vAmpTypes [ this . type ] || 1 ;
this . melting = false ;
this . meltFrameCount = storm . meltFrameCount ;
this . meltFrames = storm . meltFrames ;
this . meltFrame = 0 ;
this . twinkleFrame = 0 ;
this . active = 1 ;
2023-04-01 12:05:47 +02:00
this . fontSize = 10 + ( this . type / 5 ) * 10 ;
this . o = document . createElement ( "div" ) ;
2022-12-31 20:50:19 +01:00
this . o . innerHTML = storm . snowCharacter ;
if ( storm . className ) {
2023-04-01 12:05:47 +02:00
this . o . setAttribute ( "class" , storm . className ) ;
2022-12-31 20:50:19 +01:00
}
this . o . style . color = storm . snowColor ;
2023-04-01 12:05:47 +02:00
this . o . style . position = fixedForEverything ? "fixed" : "absolute" ;
2022-12-31 20:50:19 +01:00
if ( storm . useGPU && features . transform . prop ) {
// GPU-accelerated snow.
2023-04-01 12:05:47 +02:00
this . o . style [ features . transform . prop ] = "translate3d(0px, 0px, 0px)" ;
}
this . o . style . width = storm . flakeWidth + "px" ;
this . o . style . height = storm . flakeHeight + "px" ;
this . o . style . fontFamily = "arial,verdana" ;
this . o . style . cursor = "default" ;
this . o . style . overflow = "hidden" ;
this . o . style . fontWeight = "normal" ;
2022-12-31 20:50:19 +01:00
this . o . style . zIndex = storm . zIndex ;
docFrag . appendChild ( this . o ) ;
2023-04-01 12:05:47 +02:00
this . refresh = function ( ) {
2022-12-31 20:50:19 +01:00
if ( isNaN ( s . x ) || isNaN ( s . y ) ) {
// safety check
return false ;
}
storm . setXY ( s . o , s . x , s . y ) ;
} ;
2023-04-01 12:05:47 +02:00
this . stick = function ( ) {
if (
noFixed ||
( storm . targetElement !== document . documentElement &&
storm . targetElement !== document . body )
) {
s . o . style . top = screenY + scrollY - storm . flakeHeight + "px" ;
2022-12-31 20:50:19 +01:00
} else if ( storm . flakeBottom ) {
2023-04-01 12:05:47 +02:00
s . o . style . top = storm . flakeBottom + "px" ;
2022-12-31 20:50:19 +01:00
} else {
2023-04-01 12:05:47 +02:00
s . o . style . display = "none" ;
s . o . style . bottom = "0%" ;
s . o . style . position = "fixed" ;
s . o . style . display = "block" ;
2022-12-31 20:50:19 +01:00
}
} ;
2023-04-01 12:05:47 +02:00
this . vCheck = function ( ) {
if ( s . vX >= 0 && s . vX < 0.2 ) {
2022-12-31 20:50:19 +01:00
s . vX = 0.2 ;
2023-04-01 12:05:47 +02:00
} else if ( s . vX < 0 && s . vX > - 0.2 ) {
2022-12-31 20:50:19 +01:00
s . vX = - 0.2 ;
}
2023-04-01 12:05:47 +02:00
if ( s . vY >= 0 && s . vY < 0.2 ) {
2022-12-31 20:50:19 +01:00
s . vY = 0.2 ;
}
} ;
2023-04-01 12:05:47 +02:00
this . move = function ( ) {
var vX = s . vX * windOffset ,
yDiff ;
2022-12-31 20:50:19 +01:00
s . x += vX ;
2023-04-01 12:05:47 +02:00
s . y += s . vY * s . vAmp ;
if ( s . x >= screenX || screenX - s . x < storm . flakeWidth ) {
// X-axis scroll check
2022-12-31 20:50:19 +01:00
s . x = 0 ;
2023-04-01 12:05:47 +02:00
} else if ( vX < 0 && s . x - storm . flakeLeftOffset < - storm . flakeWidth ) {
s . x = screenX - storm . flakeWidth - 1 ; // flakeWidth;
2022-12-31 20:50:19 +01:00
}
s . refresh ( ) ;
2023-04-01 12:05:47 +02:00
yDiff = screenY + scrollY - s . y + storm . flakeHeight ;
if ( yDiff < storm . flakeHeight ) {
2022-12-31 20:50:19 +01:00
s . active = 0 ;
if ( storm . snowStick ) {
s . stick ( ) ;
} else {
s . recycle ( ) ;
}
} else {
2023-04-01 12:05:47 +02:00
if (
storm . useMeltEffect &&
s . active &&
s . type < 3 &&
! s . melting &&
Math . random ( ) > 0.998
) {
2022-12-31 20:50:19 +01:00
// ~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 < 0 ) {
if ( Math . random ( ) > 0.97 ) {
s . twinkleFrame = parseInt ( Math . random ( ) * 8 , 10 ) ;
}
} else {
s . twinkleFrame -- ;
if ( ! opacitySupported ) {
2023-04-01 12:05:47 +02:00
s . o . style . visibility =
s . twinkleFrame && s . twinkleFrame % 2 === 0
? "hidden"
: "visible" ;
2022-12-31 20:50:19 +01:00
} else {
2023-04-01 12:05:47 +02:00
s . o . style . opacity =
s . twinkleFrame && s . twinkleFrame % 2 === 0 ? 0 : 1 ;
2022-12-31 20:50:19 +01:00
}
}
}
}
} ;
2023-04-01 12:05:47 +02:00
this . animate = function ( ) {
2022-12-31 20:50:19 +01:00
// main animation loop
// move, check status, die etc.
s . move ( ) ;
} ;
2023-04-01 12:05:47 +02:00
this . setVelocities = function ( ) {
s . vX = vRndX + rnd ( storm . vMaxX * 0.12 , 0.1 ) ;
s . vY = vRndY + rnd ( storm . vMaxY * 0.12 , 0.1 ) ;
2022-12-31 20:50:19 +01:00
} ;
2023-04-01 12:05:47 +02:00
this . setOpacity = function ( o , opacity ) {
2022-12-31 20:50:19 +01:00
if ( ! opacitySupported ) {
return false ;
}
o . style . opacity = opacity ;
} ;
2023-04-01 12:05:47 +02:00
this . melt = function ( ) {
2022-12-31 20:50:19 +01:00
if ( ! storm . useMeltEffect || ! s . melting ) {
s . recycle ( ) ;
} else {
if ( s . meltFrame < s . meltFrameCount ) {
2023-04-01 12:05:47 +02:00
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" ;
2022-12-31 20:50:19 +01:00
s . meltFrame ++ ;
} else {
s . recycle ( ) ;
}
}
} ;
2023-04-01 12:05:47 +02:00
this . recycle = function ( ) {
s . o . style . display = "none" ;
s . o . style . position = fixedForEverything ? "fixed" : "absolute" ;
s . o . style . bottom = "auto" ;
2022-12-31 20:50:19 +01:00
s . setVelocities ( ) ;
s . vCheck ( ) ;
s . meltFrame = 0 ;
s . melting = false ;
2023-04-01 12:05:47 +02:00
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 ;
2022-12-31 20:50:19 +01:00
s . refresh ( ) ;
2023-04-01 12:05:47 +02:00
s . o . style . display = "block" ;
2022-12-31 20:50:19 +01:00
s . active = 1 ;
} ;
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
this . recycle ( ) ; // set up x/y coords etc.
this . refresh ( ) ;
} ;
2023-04-01 12:05:47 +02:00
this . snow = function ( ) {
var active = 0 ,
flake = null ,
i ,
j ;
for ( i = 0 , j = storm . flakes . length ; i < j ; i ++ ) {
2022-12-31 20:50:19 +01:00
if ( storm . flakes [ i ] . active === 1 ) {
storm . flakes [ i ] . move ( ) ;
active ++ ;
}
if ( storm . flakes [ i ] . melting ) {
storm . flakes [ i ] . melt ( ) ;
}
}
2023-04-01 12:05:47 +02:00
if ( active < storm . flakesMaxActive ) {
flake = storm . flakes [ parseInt ( rnd ( storm . flakes . length ) , 10 ) ] ;
2022-12-31 20:50:19 +01:00
if ( flake . active === 0 ) {
flake . melting = true ;
}
}
if ( storm . timer ) {
features . getAnimationFrame ( storm . snow ) ;
}
} ;
2023-04-01 12:05:47 +02:00
this . mouseMove = function ( e ) {
2022-12-31 20:50:19 +01:00
if ( ! storm . followMouse ) {
return true ;
}
2023-04-01 12:05:47 +02:00
var x = parseInt ( e . clientX , 10 ) ;
if ( x < screenX2 ) {
windOffset = - windMultiplier + ( x / screenX2 ) * windMultiplier ;
2022-12-31 20:50:19 +01:00
} else {
x -= screenX2 ;
2023-04-01 12:05:47 +02:00
windOffset = ( x / screenX2 ) * windMultiplier ;
2022-12-31 20:50:19 +01:00
}
} ;
2023-04-01 12:05:47 +02:00
this . createSnow = function ( limit , allowInactive ) {
2022-12-31 20:50:19 +01:00
var i ;
2023-04-01 12:05:47 +02:00
for ( i = 0 ; i < limit ; i ++ ) {
storm . flakes [ storm . flakes . length ] = new storm . SnowFlake (
parseInt ( rnd ( flakeTypes ) , 10 )
) ;
if ( allowInactive || i > storm . flakesMaxActive ) {
storm . flakes [ storm . flakes . length - 1 ] . active = - 1 ;
2022-12-31 20:50:19 +01:00
}
}
storm . targetElement . appendChild ( docFrag ) ;
} ;
2023-04-01 12:05:47 +02:00
this . timerInit = function ( ) {
2022-12-31 20:50:19 +01:00
storm . timer = true ;
storm . snow ( ) ;
} ;
2023-04-01 12:05:47 +02:00
this . init = function ( ) {
2022-12-31 20:50:19 +01:00
var i ;
2023-04-01 12:05:47 +02:00
for ( i = 0 ; i < storm . meltFrameCount ; i ++ ) {
storm . meltFrames . push ( 1 - i / storm . meltFrameCount ) ;
2022-12-31 20:50:19 +01:00
}
storm . randomizeWind ( ) ;
storm . createSnow ( storm . flakesMax ) ; // create initial batch
2023-04-01 12:05:47 +02:00
storm . events . add ( window , "resize" , storm . resizeHandler ) ;
storm . events . add ( window , "scroll" , storm . scrollHandler ) ;
2022-12-31 20:50:19 +01:00
if ( storm . freezeOnBlur ) {
if ( isIE ) {
2023-04-01 12:05:47 +02:00
storm . events . add ( document , "focusout" , storm . freeze ) ;
storm . events . add ( document , "focusin" , storm . resume ) ;
2022-12-31 20:50:19 +01:00
} else {
2023-04-01 12:05:47 +02:00
storm . events . add ( window , "blur" , storm . freeze ) ;
storm . events . add ( window , "focus" , storm . resume ) ;
2022-12-31 20:50:19 +01:00
}
}
storm . resizeHandler ( ) ;
storm . scrollHandler ( ) ;
if ( storm . followMouse ) {
2023-04-01 12:05:47 +02:00
storm . events . add (
isIE ? document : window ,
"mousemove" ,
storm . mouseMove
) ;
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
storm . animationInterval = Math . max ( 20 , storm . animationInterval ) ;
2022-12-31 20:50:19 +01:00
storm . timerInit ( ) ;
} ;
2023-04-01 12:05:47 +02:00
this . start = function ( bFromOnLoad ) {
2022-12-31 20:50:19 +01:00
if ( ! didInit ) {
didInit = true ;
} else if ( bFromOnLoad ) {
// already loaded and running
return true ;
}
2023-04-01 12:05:47 +02:00
if ( typeof storm . targetElement === "string" ) {
2022-12-31 20:50:19 +01:00
var targetID = storm . targetElement ;
storm . targetElement = document . getElementById ( targetID ) ;
if ( ! storm . targetElement ) {
2023-04-01 12:05:47 +02:00
throw new Error (
'Snowstorm: Unable to get targetElement "' + targetID + '"'
) ;
2022-12-31 20:50:19 +01:00
}
}
if ( ! storm . targetElement ) {
2023-04-01 12:05:47 +02:00
storm . targetElement = document . body || document . documentElement ;
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
if (
storm . targetElement !== document . documentElement &&
storm . targetElement !== document . body
) {
2022-12-31 20:50:19 +01:00
// re-map handler to get element instead of screen dimensions
storm . resizeHandler = storm . resizeHandlerAlt ;
//and force-enable pixel positioning
storm . usePixelPosition = true ;
}
storm . resizeHandler ( ) ; // get bounding box elements
2023-04-01 12:05:47 +02:00
storm . usePositionFixed =
storm . usePositionFixed && ! noFixed && ! storm . flakeBottom ; // whether or not position:fixed is to be used
2022-12-31 20:50:19 +01:00
if ( window . getComputedStyle ) {
// attempt to determine if body or user-specified snow parent element is relatlively-positioned.
try {
2023-04-01 12:05:47 +02:00
targetElementIsRelative =
window
. getComputedStyle ( storm . targetElement , null )
. getPropertyValue ( "position" ) === "relative" ;
} catch ( e ) {
2022-12-31 20:50:19 +01:00
// oh well
targetElementIsRelative = false ;
}
}
fixedForEverything = storm . usePositionFixed ;
if ( screenX && screenY && ! storm . disabled ) {
storm . init ( ) ;
storm . active = true ;
}
} ;
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
function doDelayedStart ( ) {
2023-04-01 12:05:47 +02:00
window . setTimeout ( function ( ) {
2022-12-31 20:50:19 +01:00
storm . start ( true ) ;
} , 20 ) ;
// event cleanup
2023-04-01 12:05:47 +02:00
storm . events . remove (
isIE ? document : window ,
"mousemove" ,
doDelayedStart
) ;
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
function doStart ( ) {
if ( ! storm . excludeMobile || ! isMobile ) {
doDelayedStart ( ) ;
}
// event cleanup
2023-04-01 12:05:47 +02:00
storm . events . remove ( window , "load" , doStart ) ;
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
// hooks for starting the snow
if ( storm . autoStart ) {
2023-04-01 12:05:47 +02:00
storm . events . add ( window , "load" , doStart , false ) ;
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
2022-12-31 20:50:19 +01:00
return this ;
2023-04-01 12:05:47 +02:00
}
2022-12-31 20:50:19 +01:00
}
2023-04-01 12:05:47 +02:00
const snow _container = document . querySelector ( ".snow-container" ) ;
if ( snow _container ) {
new SnowStorm ( ) ;
}