Interactive Snowfall Effect with Dynamic Starry Sky Background

Category: Animation , Javascript | December 3, 2024
Authormickaellherminez
Last UpdateDecember 3, 2024
LicenseMIT
Tags
Views255 views
Interactive Snowfall Effect with Dynamic Starry Sky Background

Yet another elegant snowfall effect that combines exquisite SVG snowflakes, responsive cursor interactions, and a twinkling star background.

Features:

  • Snowflakes respond to cursor movement.
  • Random initial snowflake placements.
  • A gradient-simulated wintry night sky featuring twinkling stars and occasional shooting stars enhances the atmosphere.
  • Programmatically creates unique snowflake designs. Varies size, complexity, and visual details.

How to use it:

1. Create two div elements within your HTML document. One will contain the message displayed at the beginning, and the other will serve as the container for the entire snowfall effect:

<div id="message-container">
  CSSScript
</div>
<div id="snowfall-container"></div>

2. Add the necessary CSS styles to position the elements and define the appearance of the snowflakes, stars, and animations:

#snowfall-container {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: hidden;
}
.snowflake {
  position: absolute;
  user-select: none;
  transition: transform 0.3s ease, opacity 0.3s ease;
  will-change: transform, opacity;
}
#message-container {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: #e0f7fa;
  font-family: "Mountains of Christmas", cursive;
  font-size: 4rem;
  text-align: center;
  z-index: 10;
  opacity: 0;
  transition: opacity 1s ease;
  text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5), 0 0 5px rgba(224, 247, 250, 0.7);
  background: rgba(0, 0, 0, 0.3);
  padding: 20px;
  border-radius: 10px;
}
.star {
  position: absolute;
  width: 2px;
  height: 2px;
  background: white;
  border-radius: 50%;
  opacity: 0;
  animation: twinkle 5s infinite;
}
@keyframes twinkle {
  0%,
  20%,
  50%,
  70%,
  100% {
    opacity: 0;
  }
  25%,
  55%,
  75% {
    opacity: 1;
  }
}

3. Use JavaScript to create, animate, and manage the snowflakes and background stars:

class SnowfallAnimation {
  constructor() {
    this.container = document.getElementById("snowfall-container");
    this.messageContainer = document.getElementById("message-container");
    this.snowflakes = [];
    this.snowflakeTypes = [
      this.createSnowflakeSVG(30, 6),
      this.createSnowflakeSVG(40, 8),
      this.createSnowflakeSVG(25, 4)
    ];
    this.initializeSnowflakes();
    this.createStars();
  }
  createSnowflakeSVG(size, complexity) {
    const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
    svg.setAttribute("width", size);
    svg.setAttribute("height", size);
    svg.setAttribute("viewBox", `0 0 ${size} ${size}`);
    const centerX = size / 2;
    const centerY = size / 2;
    const radius = size / 2;
    for (let i = 0; i < complexity; i++) {
      const angle = ((Math.PI * 2) / complexity) * i;
      const endX = centerX + radius * Math.cos(angle);
      const endY = centerY + radius * Math.sin(angle);
      const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
      line.setAttribute("x1", centerX);
      line.setAttribute("y1", centerY);
      line.setAttribute("x2", endX);
      line.setAttribute("y2", endY);
      line.setAttribute("stroke", "rgba(255,255,255,0.8)");
      line.setAttribute("stroke-width", "2");
      svg.appendChild(line);
      // Add additional details
      for (let j = 1; j <= 2; j++) {
        const detailAngle = angle + (Math.PI / complexity) * j;
        const detailX = centerX + (radius / 2) * Math.cos(detailAngle);
        const detailY = centerY + (radius / 2) * Math.sin(detailAngle);
        const detailLine = document.createElementNS(
          "http://www.w3.org/2000/svg",
          "line"
        );
        detailLine.setAttribute("x1", (centerX + endX) / 2);
        detailLine.setAttribute("y1", (centerY + endY) / 2);
        detailLine.setAttribute("x2", detailX);
        detailLine.setAttribute("y2", detailY);
        detailLine.setAttribute("stroke", "rgba(255,255,255,0.5)");
        detailLine.setAttribute("stroke-width", "1");
        svg.appendChild(detailLine);
      }
    }
    return svg;
  }
  initializeSnowflakes() {
    const numSnowflakes = window.innerWidth < 768 ? 50 : 100;
    for (let i = 0; i < numSnowflakes; i++) {
      const snowflake = document.createElement("div");
      snowflake.classList.add("snowflake");
      // Randomly select the snowflake SVG
      const svgType = this.snowflakeTypes[
        Math.floor(Math.random() * this.snowflakeTypes.length)
      ];
      snowflake.appendChild(svgType.cloneNode(true));
      // Initial positioning to form the message
      snowflake.style.position = "absolute";
      snowflake.style.left = `${Math.random() * 100}%`;
      snowflake.style.top = `${Math.random() * 100}%`;
      snowflake.style.opacity = `${Math.random() * 0.5 + 0.5}`;
      this.snowflakes.push({
        element: snowflake,
        speed: Math.random() * 3 + 1,
        xOffset: Math.random() * 2 - 1,
        yOffset: Math.random() * 2 - 1
      });
      this.container.appendChild(snowflake);
    }
    this.formMessage();
  }
  formMessage() {
    const messageWidth = this.messageContainer.offsetWidth;
    const messageHeight = this.messageContainer.offsetHeight;
    this.snowflakes.forEach((flake, index) => {
      const rect = this.messageContainer.getBoundingClientRect();
      const xPos = rect.left + Math.random() * rect.width;
      const yPos = rect.top + Math.random() * rect.height;
      flake.element.style.transition = "all 2s cubic-bezier(0.4, 0.0, 0.2, 1)";
      flake.element.style.transform = `translate(${
        xPos - window.innerWidth / 2
      }px, ${yPos - window.innerHeight / 2}px)`;
      flake.element.style.opacity = "1";
    });
    setTimeout(() => {
      this.messageContainer.style.opacity = "1";
      setTimeout(() => {
        this.messageContainer.style.opacity = "0";
        this.startSnowfall();
      }, 3000);
    }, 2000);
  }
  startSnowfall() {
    this.snowflakes.forEach((flake) => {
      flake.element.style.transition = "none";
      this.animateSnowflake(flake);
    });
    // Add cursor interaction
    document.addEventListener("mousemove", (e) => this.handleMouseMove(e));
  }
  animateSnowflake(flake) {
    const animate = () => {
      const rect = flake.element.getBoundingClientRect();
      let newTop = rect.top + flake.speed;
      let newLeft = rect.left + flake.xOffset;
      if (newTop > window.innerHeight) {
        newTop = -50;
        newLeft = Math.random() * window.innerWidth;
      }
      flake.element.style.top = `${newTop}px`;
      flake.element.style.left = `${newLeft}px`;
      requestAnimationFrame(animate);
    };
    animate();
  }
  handleMouseMove(e) {
    this.snowflakes.forEach((flake) => {
      const rect = flake.element.getBoundingClientRect();
      const flakeCenterX = rect.left + rect.width / 2;
      const flakeCenterY = rect.top + rect.height / 2;
      const dx = e.clientX - flakeCenterX;
      const dy = e.clientY - flakeCenterY;
      const distance = Math.sqrt(dx * dx + dy * dy);
      if (distance < 100) {
        const pushFactor = (100 - distance) / 5;
        flake.element.style.transition = "transform 0.5s ease-out";
        flake.element.style.transform = `translate(${dx * pushFactor * -0.01}px, ${
          dy * pushFactor * -0.01
        }px) scale(1.5)`;
        flake.element.querySelector("svg").style.filter =
          "drop-shadow(0 0 5px gold)";
      } else {
        flake.element.style.transition = "transform 0.5s ease-out";
        flake.element.style.transform = "translate(0, 0) scale(1)";
        flake.element.querySelector("svg").style.filter = "none";
      }
    });
  }
  createStars() {
    const numStars = 50;
    for (let i = 0; i < numStars; i++) {
      const star = document.createElement("div");
      star.classList.add("star");
      star.style.left = `${Math.random() * 100}%`;
      star.style.top = `${Math.random() * 50}%`;
      star.style.animationDelay = `${Math.random() * 5}s`;
      star.style.opacity = Math.random() * 0.5 + 0.5;
      this.container.appendChild(star);
    }
    // Add stars
    setInterval(() => {
      const shootingStar = document.createElement("div");
      shootingStar.classList.add("star");
      shootingStar.style.left = `${Math.random() * 100}%`;
      shootingStar.style.top = `${Math.random() * 50}%`;
      shootingStar.style.width = "2px";
      shootingStar.style.height = "2px";
      shootingStar.style.background = "linear-gradient(45deg, white, transparent)";
      shootingStar.style.opacity = "1";
      shootingStar.style.transform = "translateX(-100vw) translateY(100vh)";
      shootingStar.style.transition = "transform 1s linear";
      this.container.appendChild(shootingStar);
      setTimeout(() => {
        shootingStar.style.transform = "translateX(0) translateY(0)";
      }, 50);
      setTimeout(() => {
        shootingStar.remove();
      }, 1050);
    }, 5000);
  }
}
// Initialize
window.addEventListener("load", () => {
  new SnowfallAnimation();
});

You Might Be Interested In:


Leave a Reply