JSFiddle - React, Tailwind, and code Playground

by Vee76

HTML

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>Snowfall Effect</title>

</head>

<body>

    <canvas id="snowCanvas"></canvas>
</body>

</html>

CSS

body {

    margin: 0;

    padding: 0;

    overflow: hidden;

}

body::before {

    content: '';

    position: fixed;

    top: 0;

    left: 0;

    width: 100%;

    height: 100%;

    background: url('https://images4.alphacoders.com/103/103374.jpg') no-repeat center center/cover;

    z-index: -1;

}

JavaScript

const canvas = document.getElementById("snowCanvas");
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

let snowflakes = [];
let wind = 0;

class Snowflake {
  constructor() {
    this.reset();
  }
  reset() {
    this.x = Math.random() * canvas.width;
    this.y = Math.random() * canvas.height;
    this.size = Math.random() * 3 + 2;
    this.speedY = Math.random() * 1.5 + 1;
    this.speedX = wind + (Math.random() - 0.5) * 0.5;
    this.opacity = Math.random();
  }
  update() {
    this.y += this.speedY;
    this.x += this.speedX;

    if (this.y > canvas.height) this.y = 0;
    if (this.x > canvas.width) this.x = 0;
    if (this.x < 0) this.x = canvas.width;
  }
  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
    ctx.fillStyle = `rgba(255, 255, 255, ${this.opacity})`;
    ctx.fill();
    ctx.closePath();
  }
}

function createSnowflakes() {
  for (let i = 0; i < 150; i++) {
    snowflakes.push(new Snowflake());
  }
}

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  snowflakes.forEach((flake) => {
    flake.update();
    flake.draw();
  });
  requestAnimationFrame(animate);
}

function changeWind() {
  wind = (Math.random() - 0.5) * 2;
  setTimeout(changeWind, 5000);
}

createSnowflakes();
animate();
changeWind();