Andy Schneider
Andy Schneider

Reputation: 155

Canvas Snow Animation does not work

So a season specific question. I have made a basic animation to simulate some snow. The problem is that the snow only falls once and the screen stays black after that. I have followed a tutorial which put everything inside the window.onload(). THis seems not as good style to me so here is what i came up with:

let sky, ctx;
let W, H;

const maxSnow = 250;
const snow = [];

function init(){
	sky = document.getElementById("sky");
	ctx = sky.getContext("2d");

	sky.width = W = window.innerWidth;
	sky.height = H = window.innerHeight;

	for(let i = 0; i < maxSnow; i++)
	{
		snow.push({
			x: Math.random()*W, //x-coordinate
			y: -50, //y-coordinate
			radius: Math.random()*4+1, //radius
			density: Math.random()*maxSnow //density
		})
	}
}

function draw()
{
	ctx.clearRect(0, 0, W, H);

	ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
	ctx.beginPath();
	for(let i = 0; i < maxSnow; i++)
	{
		var flake = snow[i];
		ctx.moveTo(flake.x, flake.y);
		ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI*2, true);
	}
	ctx.fill();
	update();
}

//Function to move the snowflakes
//angle will be an ongoing incremental flag. Sin and Cos functions will be applied to it to create vertical and horizontal movements of the flakes
var angle = 0;
function update()
{
	angle += 0.01;
	for(var i = 0; i < maxSnow; i++)
	{
		var p = snow[i];
		//Updating X and Y coordinates
		//We will add 1 to the cos function to prevent negative values which will lead flakes to move upwards
		//Every particle has its own density which can be used to make the downward movement different for each flake
		//Lets make it more random by adding in the radius
		p.y += Math.cos(angle+p.density) + 1 + p.radius/2;
		p.x += Math.sin(angle) * 2;

		//Sending flakes back from the top when it exits
		//Lets make it a bit more organic and let flakes enter from the left and right also.
		if(p.x > W+5 || p.x < -5 || p.y > H)
		{
			if(i%3 > 0) //66.67% of the flakes
			{
				snow[i] = {x: Math.random()*W, y: -10, r: p.radius, d: p.density};
			}
			else
			{
				//If the flake is exitting from the right
				if(Math.sin(angle) > 0)
				{
					//Enter from the left
					snow[i] = {x: -5, y: Math.random()*H, r: p.radius, d: p.density};
				}
				else
				{
					//Enter from the right
					snow[i] = {x: W+5, y: Math.random()*H, r: p.radius, d: p.density};
				}
			}
		}
	}
}

window.onload = function(){
	init();
	//animation loop
	setInterval(draw, 33);
}

window.addEventListener('resize', function(){
  sky.width = W = window.innerWidth;
  sky.height = H = window.innerHeight;
}, false);
* {
  margin: 0;
  padding: 0;
}

body {
  overflow: hidden;
  background-color: rgba(0, 0, 0, 1);
}
<html>
  <head>
    <link rel="stylesheet" href="style.css">
  </head>

  <body>
    <canvas id="sky"></canvas>
    <script src="snow.js"></script>
  </body>
</html>

Does anybody see what the problem is and why the particles are only falling once? Thank you.

Upvotes: 2

Views: 205

Answers (1)

Gabriele Petrioli
Gabriele Petrioli

Reputation: 196217

The is because when the snow flakes exit the screen you reset them to a wrong object.

You original object (and all methods) expect x,y,radius and density

But when you update the object you create x,y, r and d.

If you rename r and d to the correct names it works.

let sky, ctx;
let W, H;

const maxSnow = 250;
const snow = [];

function init(){
	sky = document.getElementById("sky");
	ctx = sky.getContext("2d");

	sky.width = W = window.innerWidth;
	sky.height = H = window.innerHeight;

	for(let i = 0; i < maxSnow; i++)
	{
		snow.push({
			x: Math.random()*W, //x-coordinate
			y: -50, //y-coordinate
			radius: Math.random()*4+1, //radius
			density: Math.random()*maxSnow //density
		})
	}
}

function draw()
{
	ctx.clearRect(0, 0, W, H);

	ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
	ctx.beginPath();
	for(let i = 0; i < maxSnow; i++)
	{
		var flake = snow[i];
		ctx.moveTo(flake.x, flake.y);
		ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI*2, true);
	}
	ctx.fill();
	update();
}

//Function to move the snowflakes
//angle will be an ongoing incremental flag. Sin and Cos functions will be applied to it to create vertical and horizontal movements of the flakes
var angle = 0;
function update()
{
	angle += 0.01;
	for(var i = 0; i < maxSnow; i++)
	{
		var p = snow[i];
		//Updating X and Y coordinates
		//We will add 1 to the cos function to prevent negative values which will lead flakes to move upwards
		//Every particle has its own density which can be used to make the downward movement different for each flake
		//Lets make it more random by adding in the radius
		p.y += Math.cos(angle+p.density) + 1 + p.radius/2;
		p.x += Math.sin(angle) * 2;

		//Sending flakes back from the top when it exits
		//Lets make it a bit more organic and let flakes enter from the left and right also.
		if(p.x > W+5 || p.x < -5 || p.y > H)
		{
			if(i%3 > 0) //66.67% of the flakes
			{
				snow[i] = {x: Math.random()*W, y: -10, radius: p.radius, density: p.density};
			}
			else
			{
				//If the flake is exitting from the right
				if(Math.sin(angle) > 0)
				{
					//Enter from the left
					snow[i] = {x: -5, y: Math.random()*H, radius: p.radius, density: p.density};
				}
				else
				{
					//Enter from the right
					snow[i] = {x: W+5, y: Math.random()*H, radius: p.radius, density: p.density};
				}
			}
		}
	}
}

window.onload = function(){
	init();
	//animation loop
	setInterval(draw, 33);
}

window.addEventListener('resize', function(){
  sky.width = W = window.innerWidth;
  sky.height = H = window.innerHeight;
}, false);
* {
  margin: 0;
  padding: 0;
}

body {
  overflow: hidden;
  background-color: rgba(0, 0, 0, 1);
}
<html>
  <head>
    <link rel="stylesheet" href="style.css">
  </head>

  <body>
    <canvas id="sky"></canvas>
    <script src="snow.js"></script>
  </body>
</html>

Upvotes: 3

Related Questions