track : function(x, y, top, ampl) {
return {
top : top + 2,
x : x + ampl * Math.sin(top / 20),
y : (top / this.screenHeight < 0.65) ? y + 2 : 1 + y + ampl * Math.cos(top / 25)
};
}
This routine sends snowflakes flying in sine wave manner.
But how does it do that? Please explain.
It uses Math.sin
for x
; and Math.cos
for y
, but other snippets I've seen use them in the opposite way. Why? Why exactly top/20
and top/25
?
The whole code:
<script type="text/javascript">
var snowflakes = { // Namespace
/* Settings */
pics : [
['snow.gif' , 24, 24],
['snow2.gif', 24, 24],
['snow3.gif', 24, 24]
],
track : function(x, y, top, ampl) {
return {
top : top + 2,
x : x + ampl * Math.sin(top / 20),
y : (top / this.screenHeight < 0.65) ? y + 2 : 1 + y + ampl * Math.cos(top / 25)
};
},
quantity : 30,
minSpeed : 20, // 1 - 100, minSpeed <= maxSpeed
maxSpeed : 40, // 1 - 100, maxSpeed >= minSpeed
isMelt : true, // true OR false
/* Properties */
screenWidth : 0,
screenHeight : 0,
archive : [],
timer : null,
/* Methods */
addHandler : function(object, event, handler, useCapture) {
if (object.addEventListener) object.addEventListener(event, handler, useCapture);
else if (object.attachEvent)object.attachEvent('on' + event, handler);
else object['on' + event] = handler;
},
create : function(o, index) {
var rand = Math.random();
this.timer = null;
this.o = o;
this.index = index;
this.ampl = 3 + 7*rand;
this.type = Math.round((o.pics.length - 1) * rand);
this.width = o.pics[this.type][1];
this.height = o.pics[this.type][2];
this.speed = o.minSpeed + (o.maxSpeed - o.minSpeed) * rand;
this.speed = 1000 / this.speed;
this.deviation = o.maxDeviation * rand;
this.x = o.screenWidth * rand - this.width;
this.y = 0 - this.height;
this.top = this.y;
this.img = document.createElement('img');
this.img.src = o.pics[this.type][0];
this.img.style.top = this.y + 'px';
this.img.style.position = 'absolute';
this.img.style.zIndex = 10000;
this.img.style.left = this.x + 'px';
this.img.obj = this;
if (o.isMelt) this.img.onmouseover = function() {
clearTimeout(this.obj.timer);
this.obj.timer = null;
this.parentNode.removeChild(this);
}
document.body.appendChild(this.img);
this.move();
},
init : function() {
this.screenWidth = window.innerWidth ? window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.offsetWidth);
this.screenWidth = navigator.userAgent.toLowerCase().indexOf('gecko') == -1 ? this.screenWidth : document.body.offsetWidth;
this.screenHeight = window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.offsetHeight);
this.screenScroll = (window.scrollY) ? window.scrollY : document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;
this.archive[this.archive.length] = new this.create(this, this.archive.length);
clearTimeout(this.timer);
this.timer = null
this.timer = setTimeout(function(){snowflakes.init()}, 60000 / this.quantity);
}
};
snowflakes.create.prototype = {
move : function() {
var newXY = this.o.track(this.x, this.y, this.top, this.ampl);
this.x = newXY.x;
this.y = newXY.y;
this.top = newXY.top;
if (this.y < this.o.screenHeight + this.o.screenScroll - this.height) {
this.img.style.top = this.y + 'px';
this.x = this.x < this.o.screenWidth - this.width ? this.x : this.o.screenWidth - this.width;
this.img.style.left = this.x + 'px';
var index = this.index;
this.timer = setTimeout(function(){snowflakes.archive[index].move()}, this.speed);
} else {
delete(this.o.archive[this.index]);
this.img.parentNode.removeChild(this.img);
}
}
};
snowflakes.addHandler(window, 'load', function() {snowflakes.init();});
snowflakes.addHandler(window, 'resize', function() {snowflakes.init();});
</script>
track : function(x, y, top, ampl) {
return {
top : top + 2,
x : x + ampl * Math.sin(top / 20),
y : (top / this.screenHeight < 0.65) ? y + 2 : 1 + y + ampl * Math.cos(top / 25)
};
}
This routine sends snowflakes flying in sine wave manner.
But how does it do that? Please explain.
It uses Math.sin
for x
; and Math.cos
for y
, but other snippets I've seen use them in the opposite way. Why? Why exactly top/20
and top/25
?
The whole code:
<script type="text/javascript">
var snowflakes = { // Namespace
/* Settings */
pics : [
['snow.gif' , 24, 24],
['snow2.gif', 24, 24],
['snow3.gif', 24, 24]
],
track : function(x, y, top, ampl) {
return {
top : top + 2,
x : x + ampl * Math.sin(top / 20),
y : (top / this.screenHeight < 0.65) ? y + 2 : 1 + y + ampl * Math.cos(top / 25)
};
},
quantity : 30,
minSpeed : 20, // 1 - 100, minSpeed <= maxSpeed
maxSpeed : 40, // 1 - 100, maxSpeed >= minSpeed
isMelt : true, // true OR false
/* Properties */
screenWidth : 0,
screenHeight : 0,
archive : [],
timer : null,
/* Methods */
addHandler : function(object, event, handler, useCapture) {
if (object.addEventListener) object.addEventListener(event, handler, useCapture);
else if (object.attachEvent)object.attachEvent('on' + event, handler);
else object['on' + event] = handler;
},
create : function(o, index) {
var rand = Math.random();
this.timer = null;
this.o = o;
this.index = index;
this.ampl = 3 + 7*rand;
this.type = Math.round((o.pics.length - 1) * rand);
this.width = o.pics[this.type][1];
this.height = o.pics[this.type][2];
this.speed = o.minSpeed + (o.maxSpeed - o.minSpeed) * rand;
this.speed = 1000 / this.speed;
this.deviation = o.maxDeviation * rand;
this.x = o.screenWidth * rand - this.width;
this.y = 0 - this.height;
this.top = this.y;
this.img = document.createElement('img');
this.img.src = o.pics[this.type][0];
this.img.style.top = this.y + 'px';
this.img.style.position = 'absolute';
this.img.style.zIndex = 10000;
this.img.style.left = this.x + 'px';
this.img.obj = this;
if (o.isMelt) this.img.onmouseover = function() {
clearTimeout(this.obj.timer);
this.obj.timer = null;
this.parentNode.removeChild(this);
}
document.body.appendChild(this.img);
this.move();
},
init : function() {
this.screenWidth = window.innerWidth ? window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.offsetWidth);
this.screenWidth = navigator.userAgent.toLowerCase().indexOf('gecko') == -1 ? this.screenWidth : document.body.offsetWidth;
this.screenHeight = window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.offsetHeight);
this.screenScroll = (window.scrollY) ? window.scrollY : document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop;
this.archive[this.archive.length] = new this.create(this, this.archive.length);
clearTimeout(this.timer);
this.timer = null
this.timer = setTimeout(function(){snowflakes.init()}, 60000 / this.quantity);
}
};
snowflakes.create.prototype = {
move : function() {
var newXY = this.o.track(this.x, this.y, this.top, this.ampl);
this.x = newXY.x;
this.y = newXY.y;
this.top = newXY.top;
if (this.y < this.o.screenHeight + this.o.screenScroll - this.height) {
this.img.style.top = this.y + 'px';
this.x = this.x < this.o.screenWidth - this.width ? this.x : this.o.screenWidth - this.width;
this.img.style.left = this.x + 'px';
var index = this.index;
this.timer = setTimeout(function(){snowflakes.archive[index].move()}, this.speed);
} else {
delete(this.o.archive[this.index]);
this.img.parentNode.removeChild(this.img);
}
}
};
snowflakes.addHandler(window, 'load', function() {snowflakes.init();});
snowflakes.addHandler(window, 'resize', function() {snowflakes.init();});
</script>
Share
Improve this question
edited Aug 26, 2011 at 21:42
DrStrangeLove
asked Aug 26, 2011 at 20:53
DrStrangeLoveDrStrangeLove
11.6k16 gold badges62 silver badges72 bronze badges
2
- Yes, usually x = cos(theta) and y = sin(theta), but that's ok. If you flip x and y axes you still get wavy motion. all good. The top/20 and top/25 will make x & y a little out-of-phase, which is nice for movement. – david van brink Commented Aug 26, 2011 at 21:01
- For an example interactive sine wave, see this. – Mahozad Commented Nov 21, 2024 at 19:14
2 Answers
Reset to default 11The basic sine function is defined as:
f(x) = A sin(wt + p)
where
- A is the amplitude
- w is the frequency
- p is the phase
These factors determine how the graph of f will look like.
The amplitude can be thought of as a scaling factor, the larger A, the larger (absolute values) the peaks and lows of f.
The frequency determines how fast the sine function will run through all its values until it starts over again - sine is a periodic function. The larger k, the faster f will run through one period.
p is the phase, think of it as "shifting" the starting point of the function to the right (positive p) or left (negative). Hard to explain in words, have a look here for graphs.
The function you give in your example is a generalized version of
f: R->R², f(t)=(sin(t), cos(t))
Which is (one of) the parametrizations of the unit circle . If you increase t monotonously and plot x (sin(t)) and y (cos(t)) you will have a point flying on a circle with radius 1.
Your generalized function is
f: R->R², f(t) = (A sin(1/wt), A cos(1/wt)), w > 1
In your case A=ampl, t=top and w=20 for the x coordinate and w=25 for the y coordinate. These slight deviations for w are there make the movement jittery, so that it's no longer a perfect circle, but rather some "distorted" ellipse - snow flakes don't fall in perfect circles, I guess. Additionally this makes the path of the flake appear to be more random than straight perfect circles. It's an illusion though, this is all also very deterministic and still periodic - it's just that x and y movement are "out of phase" so it takes a whole lot longer until one period is completed.
w is chosen > 1 to "slow down" the circular movement. The larger you choose w, the lower the frequency will be and your moving point will complete a full circle much slower.
The larger you choose A, the larger your circle will become.
It just makes the sine wave bigger so the curves can be more easily observed.
Here's a fiddle I tried making. If I change 20 and 25 to 1, the movement gets less interesting. http://jsfiddle.net/AbM9z/1/
It would help to know what values the function is being called with.