Last active
December 28, 2023 06:25
-
-
Save PechenkiUA/05cd66bdc9c28e28b0bbb67c36bb8e92 to your computer and use it in GitHub Desktop.
Snow
This file contains 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
(function (){ | |
document.querySelector('body').insertAdjacentHTML('afterbegin',`<canvas id="particle_canvas" style="position: fixed; left: 0; top: 0; width: 100%; height: 100%;"></canvas>`); | |
const canvas = document.querySelector('#particle_canvas') | |
const ctx = canvas.getContext('2d') | |
let width, height, lastNow | |
let snowflakes | |
const maxSnowflakes = 100 | |
function init() { | |
snowflakes = [] | |
resize() | |
render(lastNow = performance.now()) | |
} | |
function render(now) { | |
requestAnimationFrame(render) | |
const elapsed = now - lastNow | |
lastNow = now | |
ctx.clearRect(0, 0, width, height) | |
if (snowflakes.length < maxSnowflakes) | |
snowflakes.push(new Snowflake()) | |
ctx.fillStyle = ctx.strokeStyle = '#fff' | |
snowflakes.forEach(snowflake => snowflake.update(elapsed, now)) | |
} | |
function pause() { | |
cancelAnimationFrame(render) | |
} | |
function resume() { | |
lastNow = performance.now() | |
requestAnimationFrame(render) | |
} | |
class Snowflake { | |
constructor() { | |
this.spawn() | |
} | |
spawn(anyY = false) { | |
this.x = rand(0, width) | |
this.y = anyY === true | |
? rand(-50, height + 50) | |
: rand(-50, -10) | |
this.xVel = rand(-.05, .05) | |
this.yVel = rand(.02, .1) | |
this.angle = rand(0, Math.PI * 2) | |
this.angleVel = rand(-.001, .001) | |
this.size = rand(7, 12) | |
this.sizeOsc = rand(.01, .5) | |
} | |
update(elapsed, now) { | |
const xForce = rand(-.001, .001); | |
if (Math.abs(this.xVel + xForce) < .075) { | |
this.xVel += xForce | |
} | |
this.x += this.xVel * elapsed | |
this.y += this.yVel * elapsed | |
this.angle += this.xVel * 0.05 * elapsed //this.angleVel * elapsed | |
if ( | |
this.y - this.size > height || | |
this.x + this.size < 0 || | |
this.x - this.size > width | |
) { | |
this.spawn() | |
} | |
this.render() | |
} | |
render() { | |
ctx.save() | |
const { x, y, angle, size } = this | |
ctx.beginPath() | |
ctx.arc(x, y, size * 0.2, 0, Math.PI * 2, false) | |
ctx.fill() | |
ctx.restore() | |
} | |
} | |
// Utils | |
const rand = (min, max) => min + Math.random() * (max - min) | |
function resize() { | |
width = canvas.width = window.innerWidth | |
height = canvas.height = window.innerHeight | |
} | |
window.addEventListener('resize', resize) | |
window.addEventListener('blur', pause) | |
window.addEventListener('focus', resume) | |
init() | |
})(); |
This file contains 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
.snowflake { | |
position: absolute; | |
width: 10px; | |
height: 10px; | |
background: white; | |
border-radius: 50%; | |
filter: drop-shadow(0 0 10px white); | |
} |
This file contains 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
(function (){ | |
document.querySelector('body').insertAdjacentHTML('afterbegin',`<div id="snow" style="position: fixed; left: 0; top: 0; width: 100%; height: 100%;"></div>`); | |
let snowflakes_count = 200; | |
// let base_css = ``; // Put your custom base css here | |
if (typeof total !== 'undefined'){ | |
snowflakes_count = total; | |
} | |
// This function allows you to turn on and off the snow | |
function toggle_snow() { | |
let check_box = document.getElementById("toggle_snow"); | |
if (check_box.checked == true) { | |
document.getElementById('snow').style.display = "block"; | |
} | |
else { | |
document.getElementById('snow').style.display = "none"; | |
} | |
} | |
// Creating snowflakes | |
function spawn_snow(snow_density = 200) { | |
snow_density -= 1; | |
for (let x = 0; x < snow_density; x++) { | |
let board = document.createElement('div'); | |
board.className = "snowflake"; | |
document.getElementById('snow').appendChild(board); | |
} | |
} | |
// Append style for each snowflake to the head | |
function add_css(rule) { | |
let css = document.createElement('style'); | |
css.type = 'text/css'; | |
css.appendChild(document.createTextNode(rule)); // Support for the rest | |
document.getElementsByTagName("head")[0].appendChild(css); | |
} | |
// Math | |
function random_int(value = 100){ | |
return Math.floor(Math.random() * value) + 1; | |
} | |
function random_range(min, max) { | |
min = Math.ceil(min); | |
max = Math.floor(max); | |
return Math.floor(Math.random() * (max - min + 1)) + min; | |
} | |
// Create style for snowflake | |
function spawnSnowCSS(snow_density = 200){ | |
let snowflake_name = "snowflake"; | |
let rule = ``; | |
if (typeof base_css !== 'undefined'){ | |
rule = base_css; | |
} | |
for(let i = 1; i < snow_density; i++){ | |
let random_x = Math.random() * 100; // vw | |
let random_offset = random_range(-100000, 100000) * 0.0001; // vw; | |
let random_x_end = random_x + random_offset; | |
let random_x_end_yoyo = random_x + (random_offset / 2); | |
let random_yoyo_time = random_range(30000, 80000) / 100000; | |
let random_yoyo_y = random_yoyo_time * 100; // vh | |
let random_scale = Math.random(); | |
let fall_duration = random_range(10, 30) * 1; // s | |
let fall_delay = random_int(30) * -1; // s | |
let opacity_ = Math.random(); | |
rule += ` | |
.${snowflake_name}:nth-child(${i}) { | |
opacity: ${opacity_}; | |
transform: translate(${random_x}vw, -10px) scale(${random_scale}); | |
animation: fall-${i} ${fall_duration}s ${fall_delay}s linear infinite; | |
} | |
@keyframes fall-${i} { | |
${random_yoyo_time*100}% { | |
transform: translate(${random_x_end}vw, ${random_yoyo_y}vh) scale(${random_scale}); | |
} | |
to { | |
transform: translate(${random_x_end_yoyo}vw, 100vh) scale(${random_scale}); | |
} | |
} | |
` | |
} | |
add_css(rule); | |
} | |
// Load the rules and execute after the DOM loads | |
window.onload = function() { | |
spawnSnowCSS(snowflakes_count); | |
spawn_snow(snowflakes_count); | |
}; | |
// TODO add progress bar for slower clients | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment