Snow animation
<!-----------------------------------------------------------------------------------------
// XSnow fr JavaScript - von Mag. Dr. Nikolaus Klepp - dr.klepp@gmx.at - www.klepp.info
//-----------------------------------------------------------------------------------------
jsSnow
Copyright (C) 2002 Mag. Dr. Nikolaus Klepp
Copyright (C) 2002 INOUE Hiroyuki <dombly@kc4.so-net.ne.jp>
Copyright (C) 2002 Heiko Feldker <hfeldker@web.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
------------------------------------------------------------------------------------------>
<HTML>
<HEAD>
<!-- THE SANTA & SNOW PROGRAM -->
<script>
//---------------------------------------------------------------------------------------
// XSnow for JavaScript - von Mag. Dr. Nikolaus Klepp - dr.klepp@gmx.at - www.klepp.info
//---------------------------------------------------------------------------------------
/* jsSnow
Copyright (C) 2002 Mag. Dr. Nikolaus Klepp <dr.klepp@gmx.at>
Copyright (C) 2002 INOUE Hiroyuki <dombly@kc4.so-net.ne.jp>
Copyright (C) 2002 Heiko Feldker <hfeldker@web.de>
Release Id: 0.3n1+dombly1
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//---------------------------------------------------------------------------------------
window.onerror = null;
var ns6 = (!document.all && document.getElementById);
var ie4 = (document.all);
var ns4 = (document.layers);
var BV = (parseFloat(navigator.appVersion.indexOf("MSIE")>0 ?
navigator.appVersion.split(";")[1].substr(6) :
navigator.appVersion));
var op = (navigator.userAgent.indexOf('Opera')!=-1 && BV>=4);
var pageWidth = 0; // page dimension & visible offset
var pageHeight = 0;
var pageOffX = 0;
var pageOffY = 0;
// <---- Customizable part ----
var flakeImageDir = './'; // relative path for the snow flake images
var santaImageDir = './'; // relative path for Santa images
var santaSize = '2'; // 1, 2 or 3 (smaller number specifies smaller image)
var infoLayer = 'info'; // set to false if you don't need f/s display
var flakes = 25; // total number of snowflakes
var santa_mass = 5; // santa's effective mass during storms
// specified as the ratio to snow flakes
// exept 0, which means santa won't be pushed by storms
var santa_speed_PperS = 20; // santa speed in pixel/second
var flake_speed_PperS = 30; // flake speed in pixel/second
var storm_speed_PperS = 300; // storm speed in pixel/second
var santa_init_x = -256; // santa's initial X-position
var santa_direction = 0; // santa's movement direction in degree
// (between [0:360]; default is (1,0), 90 is to go up )
var santa_appearance = 0.75; // probability between [0:1] for santa to be shown
var flake_TX = 1.0; // max. sec. of flake's constant X-movement on fluttering
var flake_XperY = 2.0; // fluttering movement's max. vx/vy ratio
var santa_TY = 0.5; // max. sec. of santa's constant Y-movement in his rugged movement
var santa_YperX = 0.5; // santa's rugged movement's max. vy/vx ratio
var storm_duration_S = 10.0; // storm duration in seconds - watch out: about 1-2 seconds for deceleration
var storm_lag_S = 60.0; // no-storm in seconds
var storm_YperX = 1/3.0; // storm's max. vy/vx ratio
var disappear_margin = 32; // pixels from each border where objects disappear
// ---- Customizable part ---->
var refresh_FperS = 20; // initial frames/second, recalculated.
var refresh = 1000/refresh_FperS; // ms/frame
var santa_speed = 0; // santa speed in pixel/frame
var flake_speed = 0; // flake speed in pixel/frame
var storm_speed = 0; // storm speed in pixel/frame
var storm_YperX_current = storm_YperX; // storm direction varies each time
var storm_v_sin = 0; // storm speed's sine
var storm_v_cos = 1; // storm speed's cosine
var storm_direction = 0; // storm X-direction, -1/0=quiet/+1
var storm_id = 0; // ID of storm timer
var storm_blowing = 1; // start with storm=ON
var santa;
var santaX = 0; // X-position of santa
var santaY = 0; // Y-position of santa
var santaSY = 0; // frames till Y-movement changes
var santaVY = 0; // variant Y-movement in pixel/frame
var santaMX = 1; // steady movement's X-ratio
var santaMY = 0; // steady movement's Y-ratio
var santaDX = 0; // X-movement in pixel/frame, caused by storm
var santaDY = 0; // Y-movement in pixel/frame, caused by storm
var flake = new Array(flakes);
var flakeX = new Array(flakes);
var flakeY = new Array(flakes);
var flakeSX = new Array(flakes);
var flakeVX = new Array(flakes);
var flakeVY = new Array(flakes);
var flakeVIS = new Array(flakes);
var flakeDX = 0; // X-movement in pixel/frame, caused by storm
var flakeDY = 0; // Y-movement in pixel/frame, caused by storm
var timer_id = 0; // ID if timer proc.
var timer_sum = refresh; // Inital values for speed calculation
var timer_count = 1; // --''--
var flake_visible = op; // start with visble flakes for Opera, all others start invisible
var flake_id = 0; // timer id of make_flake_visible
//-------------------------------------------------------------------------
// preload images
//-------------------------------------------------------------------------
var kFlakeImages = 7;
var flake_images = new Array(kFlakeImages);
for(i=0;i<kFlakeImages;i++) {
flake_images[i] = new Image();
flake_images[i].src = flakeImageDir+'snow'+i+'.gif';
}
var santa_image = new Image();
santa_image.src = santaImageDir+'sleigh'+santaSize+'.gif';
//-------------------------------------------------------------------------
// calculates optimum framerate & corresponding speed
//-------------------------------------------------------------------------
function rebuild_speed_and_timer() {
var old = refresh_FperS;
refresh = Math.floor(timer_sum/timer_count*2)+10; // ms/Frame + spare
refresh_FperS = Math.floor(1000/refresh); // frames/second
santa_speed = santa_speed_PperS/refresh_FperS; // pixel/second --> pixel/frame
flake_speed = flake_speed_PperS/refresh_FperS; // pixel/second --> pixel/frame
storm_speed = storm_speed_PperS/refresh_FperS; // pixel/second --> pixel/frame
if (timer_id) window.clearInterval(timer_id); // adapt timer
timer_id = window.setInterval('move_snow_and_santa()',refresh);
// FRAMES PER SECOND DISPLAY: REMOVE IF NOT NEEDED
if(infoLayer){
if (old!=refresh_FperS) write_to_layer(infoLayer,refresh_FperS+'f/s');
}
// adapt speed - for smoothness
if (old != refresh_FperS) {
var ratio = old/refresh_FperS;
santaVY *= ratio;
for (i=0; i<flakes; i++) {
flakeSX[i] *= ratio;
flakeVX[i] *= ratio;
flakeVY[i] *= ratio;
}
}
timer_count /= 2; // moving medium
timer_sum /= 2;
}
//-------------------------------------------------------------------------
// make flakes visible: while initalialisation phase flakes are invisble.
// after make_flakes_visible, all new flakes start visible
//-------------------------------------------------------------------------
function make_flake_visible_proc() {
window.clearInterval(flake_id);
flake_visible = true;
}
//-------------------------------------------------------------------------
// storm proc - turn storm on/off
//-------------------------------------------------------------------------
function storm_proc() {
// keep ourself from restarting while running
window.clearInterval(storm_id);
if (storm_blowing == 0) {
// turn storm on
storm_blowing = (Math.random()<0.5) ? -1 : 1 ;
storm_YperX_current = Math.random()*storm_YperX;
// prepare values by trigonometrical functions - too heavy for move_snow()
var storm_theta = Math.atan(storm_YperX_current);
storm_v_cos = Math.cos(storm_theta);
storm_v_sin = Math.sin(storm_theta);
storm_id = window.setInterval('storm_proc()',storm_duration_S*1000.0);
} else {
// turn storm off
storm_blowing *= 0.7;
if ((Math.abs(storm_blowing)<0.05) || (!flake_visible)) {
storm_blowing = 0;
storm_id = window.setInterval('storm_proc()',storm_lag_S*1000.0);
} else {
storm_id = window.setInterval('storm_proc()',500.0);
}
}
// preapare movement vektor for snow, caused by storm
flakeDX = storm_v_cos*storm_speed*storm_blowing;
flakeDY = Math.abs(storm_v_sin*storm_speed*storm_blowing);
// preapare movement vektor for santa, caused by storm & santa_mass
if(santa_mass>0) {
santaDX = flakeDX/santa_mass;
santaDY = flakeDY/santa_mass;
} else {
santaDX=0;
santaDY=0;
}
}
//-------------------------------------------------------------------------
// create all layers & Objects
//-------------------------------------------------------------------------
function init_snow_and_santa() {
// create santa
santa = get_layer_by_name('santa0');
santaX = santa_init_x;
santaY = Math.random()*pageHeight;
santaSY = 0;
if (santa_direction != 0) {
santaMX = Math.cos(santa_direction/180*Math.PI);
santaMY = -Math.sin(santa_direction/180*Math.PI);
}
// create flake
for (var i=0; i<flakes; i++) {
flake[i] = get_layer_by_name('flake'+i);
flakeX[i] = Math.random()*pageWidth;
flakeY[i] = Math.random()*pageHeight;
flakeSX[i] = 0;
flakeVX[i] = 0;
flakeVIS[i] = flake_visible;
flakeVY[i] = 1;
}
}
//-------------------------------------------------------------------------
// get the named layer
//-------------------------------------------------------------------------
function get_layer_by_name(id) {
if (op) { return document.getElementById(id); }
if (ns6) { return document.getElementById(id); }
if (ie4) { return document.all[id]; }
if (ns4) { return document.layers[id]; }
}
//-------------------------------------------------------------------------
// move all objects
//-------------------------------------------------------------------------
function move_snow_and_santa() {
var beginn = new Date().getMilliseconds();
move_santa();
move_snow();
var ende = new Date().getMilliseconds();
var diff = (beginn>ende?1000+ende-beginn:ende-beginn);
timer_sum += diff;
timer_count ++;
if (timer_count>10) {
rebuild_speed_and_timer();
}
}
//-------------------------------------------------------------------------
// santa's private movement
//-------------------------------------------------------------------------
function move_santa() {
var lmgn = -pageWidth*(1-santa_appearance)/santa_appearance;
var rmgn = pageWidth;
var h = pageHeight+santa_image.height;
// santa outside screen ?
if (santaX > rmgn) {
santaX = lmgn;
santaY = Math.random()*pageHeight;
santaSY = 0;
santaVY = 0;
} else if (santaX < lmgn) {
santaX = rmgn;
santaY = Math.random()*pageHeight;
santaSY = 0;
santaVY = 0;
} else if (santaY >= pageHeight) {
santaY -= h;
} else if (santaY < -santa_image.height) {
santaY += h;
} else {
santaX += santaMX * santa_speed + santaDX;
santaY += santaMY * santa_speed + santaDY;
}
// up-down-movement
santaSY --;
if (santaSY <= 0) {
santaSY = Math.random()*refresh_FperS*santa_TY;
santaVY = (2.0*Math.random()-1.0)*santa_YperX*santa_speed;
}
// move santa to new position
move_to(santa,santaX,santaY,(santaY<pageHeight- disappear_margin));
}
//-------------------------------------------------------------------------
// snowflake's private movement
//-------------------------------------------------------------------------
function move_snow() {
for (var i=0; i<flakes; i++) {
// flake outside screen ?
flakeX[i] += flakeVX[i] + flakeDX;
flakeY[i] += flakeVY[i] + flakeDY;
if (flakeY[i]>pageHeight-disappear_margin) {
flakeX[i] = Math.random()*pageWidth;
flakeY[i] = 0;
flakeVY[i] = flake_speed+Math.random()*flake_speed;
if (Math.random()<0.1) flakeVY[i] *= 2.0;
if (flake_visible) flakeVIS[i] = true;
}
// left-right- movement
flakeSX[i] --;
if (flakeSX[i] <= 0) {
flakeSX[i] = Math.random()*refresh_FperS*flake_TX;
flakeVX[i] = (2.0*Math.random()-1.0)*flake_XperY*flake_speed;
}
if (flakeX[i]<-disappear_margin)
flakeX[i] += pageWidth;
if (flakeX[i]>=(pageWidth-disappear_margin))
flakeX[i] -= pageWidth;
// move flake to new position
move_to(flake[i],flakeX[i],flakeY[i],flakeVIS[i]);
}
}
//-------------------------------------------------------------------------
// move a layer
//-------------------------------------------------------------------------
function move_to(obj, x, y, visible) {
if (visible) {
if (op) {
obj.style.left = x+"px";
obj.style.top = y+"px";
// FIX THIS: SHOW DOES NOT WORK OPERA
obj.style.display = "block";
} else if (ie4) {
obj.style.pixelLeft = x;
obj.style.pixelTop = y;
obj.style.visibility= "visible";
} else if (ns4) {
obj.left = x;
obj.top = y;
obj.visibility = "show";
} else if (ns6) {
obj.style.left = x+"px";
obj.style.top = y+"px";
obj.style.display = "block";
}
} else {
if (ie4 || op) { obj.style.visibility = "hidden";}
if (ns4) { obj.visibility = "hide";}
if (ns6) { obj.style.display = "none";}
}
}
//-------------------------------------------------------------------------
// fill a layer with new content
// --- parameter: layer-name, new content
//-------------------------------------------------------------------------
function write_to_layer(layer,txt) {
if (op) {
// FIX THIS: DOES NOT WORK FOR OPERA
document.getElementById(layer).innerHTML = txt;
} else if (ie4) {
document.all[layer].innerHTML = txt;
} else if (ns4) {
document[layer].document.write(txt);
document[layer].document.close();
} else if (ns6) {
var over = document.getElementById(layer);
var range = document.createRange();
range.setStartBefore(over);
var domfrag = range.createContextualFragment(txt);
while (over.hasChildNodes()) {
over.removeChild(over.lastChild);
}
over.appendChild(domfrag);
}
}
//-------------------------------------------------------------------------
// size of page
//-------------------------------------------------------------------------
function get_page_dimension() {
if(op) {
pageOffX = 0;
pageOffY = 25;
pageWidth = innerWidth + pageOffX;
pageHeight = innerHeight + pageOffY;
} else if(ns6) {
pageOffX = scrollX;
pageOffY = scrollY;
pageWidth = innerWidth + pageOffX;
pageHeight = innerHeight + pageOffY;
} else if(ns4) {
pageOffX = window.pageXOffset;
pageOffY = window.pageYOffset;
pageWidth = innerWidth + pageOffX;
pageHeight = innerHeight + pageOffY;
} else if(ie4) {
pageOffX = document.body.scrollLeft;
pageOffY = document.body.scrollTop;
pageWidth = document.body.clientWidth + pageOffX;
pageHeight = document.body.clientHeight + pageOffY;
}
}
//-------------------------------------------------------------------------
// initialize all objects & timer
//-------------------------------------------------------------------------
function start() {
var a = '';
// santa's private layer
a += '<div id="santa0" '
+ 'style="position: absolute; '
+ 'left:-1px; top:-1px; z-index:0;">'
+ '<img src="'+santa_image.src+'"></div>\n';
// each snowflake's private layer
for (var i=0; i<flakes; i++) {
a += '<div id="flake'+i+'" '
+ 'style="position: absolute; '
+ 'left:-1px; top:-1px; z-index:0;">'
+ '<img src="' +flake_images[i % kFlakeImages].src+ '"></div>\n';
}
// write private layers to destination layer - this works for opera,too
document.write(a);
// recalculate page dimension every second
window.setInterval('get_page_dimension()',1000);
get_page_dimension();
// init all objects
init_snow_and_santa();
// place snowflakes, santa & trees
rebuild_speed_and_timer(refresh);
// start the animation
timer_id = window.setInterval('move_snow_and_santa()',refresh);
storm_id = window.setInterval('storm_proc()',1800); // init with visible storm
flake_id = window.setInterval('make_flake_visible_proc()',2000); // after the storm, let snowflakes fall :-)
}
/* file-variable settings for Emacsen editors
Local Variables:
tab-width: 4
End:
*/
</script>
<style TITLE="Contemporary" TYPE="text/css">
body {
font-family: "Trebuchet Ms", Verdana, sans-serif;
font-weight: normal;
font-size: 10pt;
margin: 30px;
color: #c0c0c0;
background-color: #515170;
}
a { color:#ffffff; text-decoration:none; font-weight:bold; }
a:link { color:#ffffff; }
a:visited { color:#ffffff; }
a:active { color:#ffffff; }
a:hover { color:#ffffff; }
</style>
<title>jsSnow - XSnow for Webpages</title>
</HEAD>
<BODY BGCOLOR=#515170 COLOR=#ffffff vlink=#0000ff>
<!-- FRAMES PER SECOND DISPLAY -->
<div id=info style="position:absolute; left:3px; top:3px; color:#8080ff"></div>
<!-- THIS BUILDS THE SANTA & SNOW LAYER, PLUS A NAMED CONTAINER LAYER 'santa_layer' -->
<SCRIPT>start();</SCRIPT>
<!-- REST OF PAGE -->
<DIV style="z-index:1;">
<h2>jsSnow 0.4</h2>
<h3>Hey, what's this? Looks like XSnow!</h3><br>
<img src=tannenbaum.gif style='float:left;'>
Yes, it is. I was looking for a port of my favorite toy, but didn't find any.
So I took the graphics from Rick Jansens Xsnow 1.40
(<a href=http://www.euronet.nl/~rja/>http://www.euronet.nl/~rja/</a>),
went to the kitchen, got me (Nikolaus Klepp, <a href=mailto:dr.klepp@gmx.at>dr.klepp@gmx.at</a>) a mixer, ice ... and here it is:
the first XSnow port to JavaScript!
</body>
</html>
jsSnow-0.4.zip( 10 k)Related examples in the same category