Snowfall on Canvas with vanilla JavaScript.
Last active
December 21, 2019 03:07
-
-
Save HarryStevens/9b1b42718358717b3aa70a483f9f8263 to your computer and use it in GitHub Desktop.
Snowfall
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<style> | |
body { | |
margin: 0 auto; | |
} | |
#scene { | |
background-image: linear-gradient(#000fff, #888fff, #aaafff); | |
width: 100%; | |
height: 100vh; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="scene"></div> | |
<script> | |
const canvas = document.createElement("canvas"); | |
document.getElementById("scene").appendChild(canvas); | |
const context = canvas.getContext("2d"); | |
function resize(){ | |
canvas.setAttribute("width", innerWidth); | |
canvas.setAttribute("height", innerHeight); | |
context.fillStyle = "#fff"; | |
context.shadowColor = "#fff"; | |
} | |
resize(); | |
addEventListener("resize", resize); | |
const flakes = [], interval = 4; | |
let last = 0, t = 0, index = 0; | |
function fall(){ | |
requestAnimationFrame(fall); | |
if (++t - last > interval){ | |
new Flake(); | |
last = t; | |
} | |
context.clearRect(0, 0, innerWidth, innerHeight); | |
const removeList = []; // Removing flakes during the loop causes flickering... | |
for (let i = 0, l = flakes.length - 1; i < l; i++){ | |
const flake = flakes[i]; | |
if (flake.y > innerHeight){ | |
removeList.push(flake); | |
} | |
else { | |
[flake.x, flake.y] = pointTranslate([flake.x, flake.y], flake.angle, flake.speed); | |
flake.draw(); | |
} | |
} | |
// ...so we remove them in a separate loop. | |
for (let i = 0, l = removeList.length; i < l; i++){ | |
removeList[i].remove(); | |
} | |
} | |
fall(); | |
function Flake(){ | |
this.beenRemoved = false; | |
this.id = index++; | |
this.y = 0; | |
this.x = randBetween(0, innerWidth); | |
this.r = randBetween(2, 8); | |
this.angle = randBetween(80, 90); | |
this.speed = this.r / 2; | |
flakes.push(this); | |
this.remove = _ => { | |
if (!this.beenRemoved){ | |
flakes.splice(flakes.indexOf(flakes.filter(f => f.id === this.id)[0]), 1); | |
this.beenRemoved = true; | |
} | |
} | |
this.draw = _ => { | |
context.shadowBlur = this.r / 2; | |
context.beginPath(); | |
context.arc(this.x, this.y, this.r, 0, 2 * Math.PI); | |
context.fill(); | |
context.shadowBlur = 2 + this.r / 2; | |
context.beginPath(); | |
context.arc(this.x, this.y, this.r, 0, 2 * Math.PI); | |
context.fill(); | |
} | |
return this; | |
} | |
function randBetween(min, max){ | |
return Math.floor(Math.random() * (max - min + 1) + min); | |
} | |
function pointTranslate(point, angle, distance){ | |
const r = angleToRadians(angle); | |
return [point[0] + distance * Math.cos(r), point[1] + distance * Math.sin(r)]; | |
} | |
function angleToRadians(angle){ | |
return angle / 180 * Math.PI; | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment