Snowfall Animation Background
Enhance your website with the peaceful atmosphere of the Snowfall Animation Background. Create a captivating design with gently falling snowflakes for a serene and wintery experience.
Bundui Components
import { Button } from "@/components/ui/button";
import Snowfall from "@/components/core/backgrounds/snowfall";
export default function SnowfallBackgroundExample() {
return (
<Snowfall className="flex aspect-16/9 items-center justify-center">
<div className="z-10 space-y-4 text-center lg:space-y-6">
<h4 className="text-2xl font-semibold text-black/80 lg:text-3xl dark:text-white/80">
Bundui Components
</h4>
<Button>Discover Excellence</Button>
</div>
</Snowfall>
);
}
Installation
Install the following dependencies:
npm install clsx tailwind-merge next-themes
Copy and paste the following code into your project:
"use client";
import React, { useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { useTheme } from "next-themes";
interface Snowflake {
x: number;
y: number;
radius: number;
speed: number;
opacity: number;
wind: number;
amplitude: number;
frequency: number;
angle: number;
}
export default function SnowfallBackground({
children,
count = 100,
minRadius = 1,
maxRadius = 4,
minSpeed = 0.5,
maxSpeed = 2,
wind = 0.5,
className = "",
}: {
children: React.ReactNode;
count?: number;
minRadius?: number;
maxRadius?: number;
minSpeed?: number;
maxSpeed?: number;
wind?: number;
className?: string;
}) {
const canvasRef = useRef<HTMLCanvasElement>(null);
const { theme } = useTheme();
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const snowflakesRef = useRef<Snowflake[]>([]);
const animationFrameRef = useRef<number>(0);
// Helper function to create a natural distribution
const naturalDistribution = () => {
// Use a weighted random approach to create more realistic distribution
const r = Math.random();
// 30% chance of very little movement (almost straight down)
if (r < 0.3) {
return 0.1 + Math.random() * 0.3; // Very small amplitude (0.1-0.4)
}
// 60% chance of moderate movement
else if (r < 0.9) {
return 0.5 + Math.random() * 1.0; // Moderate amplitude (0.5-1.5)
}
// 10% chance of pronounced drifting
else {
return 1.6 + Math.random() * 1.4; // Larger amplitude (1.6-3.0)
}
};
// Initialize snowflakes
const initSnowflakes = (width: number, height: number) => {
const snowflakes: Snowflake[] = [];
// Adjust count based on screen size for performance
const adjustedCount = Math.min(count, Math.floor((width * height) / 10000));
for (let i = 0; i < adjustedCount; i++) {
snowflakes.push({
x: Math.random() * width,
y: Math.random() * height,
radius: minRadius + Math.random() * (maxRadius - minRadius),
speed: minSpeed + Math.random() * (maxSpeed - minSpeed),
opacity: 0.3 + Math.random() * 0.7,
wind: (Math.random() - 0.5) * wind * 0.5, // Reduced base wind effect
amplitude: naturalDistribution(), // Natural distribution of movement
frequency: 0.001 + Math.random() * 0.008, // Random frequency for the sine wave
angle: Math.random() * Math.PI * 2, // Random starting angle
});
}
snowflakesRef.current = snowflakes;
};
// Update canvas dimensions on resize
useEffect(() => {
const handleResize = () => {
if (canvasRef.current && canvasRef.current.parentElement) {
const { width, height } =
canvasRef.current.parentElement.getBoundingClientRect();
setDimensions({ width, height });
canvasRef.current.width = width;
canvasRef.current.height = height;
initSnowflakes(width, height);
}
};
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
};
}, []);
// Animation loop
useEffect(() => {
if (!canvasRef.current || dimensions.width === 0 || dimensions.height === 0)
return;
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const animate = () => {
ctx.clearRect(0, 0, dimensions.width, dimensions.height);
// Set color based on theme
const snowColor =
theme === "dark" ? "rgba(255, 255, 255," : "rgba(220, 235, 255,";
snowflakesRef.current.forEach((flake) => {
ctx.beginPath();
ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2);
ctx.fillStyle = `${snowColor} ${flake.opacity})`;
ctx.fill();
// Update position with natural side-to-side movement
flake.y += flake.speed;
flake.angle += flake.frequency;
// Add sine wave movement to create natural drifting
flake.x += flake.wind + Math.sin(flake.angle) * flake.amplitude;
// Reset if out of bounds
if (flake.y > dimensions.height) {
flake.y = -flake.radius;
flake.x = Math.random() * dimensions.width;
// Reassign movement properties for variety when recycling
flake.amplitude = naturalDistribution();
flake.frequency = 0.001 + Math.random() * 0.008;
}
if (flake.x > dimensions.width) {
flake.x = 0;
} else if (flake.x < 0) {
flake.x = dimensions.width;
}
});
animationFrameRef.current = requestAnimationFrame(animate);
};
animate();
return () => {
if (animationFrameRef.current) {
cancelAnimationFrame(animationFrameRef.current);
}
};
}, [dimensions, theme]);
return (
<div className={cn("relative w-full", className)}>
<canvas
ref={canvasRef}
className={`absolute inset-0 pointer-events-none ${className}`}
style={{ zIndex: 0 }}
/>
<div className="z-10">{children}</div>
</div>
);
}
Update the import paths to match your project setup.
Usage
import { Button } from "@/components/ui/button";
import Snowfall from "@/components/core/backgrounds/snowfall";
export default function SnowfallBackgroundExample() {
return (
<Snowfall className="flex aspect-16/9 items-center justify-center">
<div className="z-10 space-y-4 text-center lg:space-y-6">
<h4 className="text-2xl font-semibold text-black/80 lg:text-3xl dark:text-white/80">
Bundui Components
</h4>
<Button>Discover Excellence</Button>
</div>
</Snowfall>
);
}
Props
Prop | Type | Default |
---|---|---|
className? | string | - |
wind? | number | 0.5 |
maxSpeed? | number | 2 |
minSpeed? | number | 0.5 |
maxRadius? | number | 4 |
minRadius? | number | 1 |
count? | number | 100 |
children | React.ReactNode | - |
Meteor Shower Animation Background
Beautiful meteor shower animation background built with React and Canvas. Add dynamic falling meteors to your site with smooth, customizable visuals and minimal performance impact.
Fireworks Background
Add animated fireworks to your site background with this festive component. Perfect for celebrations. Built with Tailwind CSS and Framer Motion.