Exact numbers to be tweaked when testing, but i want to have sub-50 snowflakes when on mobile, and 50-100 when on a PC. pixed based breakpoints would suffice
Sorry for the delay! I will get you a snippet later tonight hopefully.
The way I would do it is by using media queries and a custom CSS variable that specifies the number of snowflakes for each window size breakdown. In the JavaScript, I would read the value of the custom variable instead of hardcoding to a number like 50.
Love the approach. Looking forward to the snippet <3
Here is a version that does what I described:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Falling Snow</title>
<style>
body {
background-color: #111;
}
#snowflakeContainer {
position: absolute;
left: 0px;
top: 0px;
display: none;
--snowflakes: 50;
}
.snowflake {
position: fixed;
background-color: #CCC;
user-select: none;
z-index: 1000;
pointer-events: none;
border-radius: 50%;
width: 10px;
height: 10px;
}
@media screen and (min-width: 480px) {
#snowflakeContainer {
--snowflakes: 25;
}
}
@media screen and (min-width: 800px) {
#snowflakeContainer {
--snowflakes: 50;
}
}
@media screen and (min-width: 1600px) {
#snowflakeContainer {
--snowflakes: 100;
}
}
</style>
</head>
<body>
<div id="snowflakeContainer">
<span class="snowflake"></span>
</div>
<script>
// Array to store our Snowflake objects
let snowflakes = [];
// Global variables to store our browser's window size
let browserWidth;
let browserHeight;
// Specify the number of snowflakes you want visible
let snowflakeContainer = document.querySelector("#snowflakeContainer");
let containerStyle = getComputedStyle(snowflakeContainer);
let numberOfSnowflakes = containerStyle.getPropertyValue("--snowflakes");
console.log("Number of snowflakes: " + numberOfSnowflakes);
// Flag to reset the position of the snowflakes
let resetPosition = false;
// Handle accessibility
let enableAnimations = false;
let reduceMotionQuery = matchMedia("(prefers-reduced-motion)");
// Handle animation accessibility preferences
function setAccessibilityState() {
if (reduceMotionQuery.matches) {
enableAnimations = false;
} else {
enableAnimations = true;
}
}
setAccessibilityState();
reduceMotionQuery.addListener(setAccessibilityState);
//
// It all starts here...
//
function setup() {
if (enableAnimations) {
window.addEventListener("DOMContentLoaded", generateSnowflakes, false);
window.addEventListener("resize", setResetFlag, false);
}
}
setup();
//
// Constructor for our Snowflake object
//
class Snowflake {
// set initial snowflake properties
constructor(element, speed, xPos, yPos) {
this.element = element;
this.speed = speed;
this.xPos = xPos;
this.yPos = yPos;
this.scale = 1;
// declare variables used for snowflake's motion
this.counter = 0;
this.sign = Math.random() < 0.5 ? 1 : -1;
// setting an initial opacity and size for our snowflake
this.element.style.opacity = (.1 + Math.random()) / 3;
}
update() {
// using some trigonometry to determine our x and y position
this.counter += this.speed / 5000;
this.xPos += this.sign * this.speed * Math.cos(this.counter) / 40;
this.yPos += Math.sin(this.counter) / 40 + this.speed / 30;
this.scale = .5 + Math.abs(10 * Math.cos(this.counter) / 20);
// setting our snowflake's position
setTransform(Math.round(this.xPos), Math.round(this.yPos), this.scale, this.element);
// if snowflake goes below the browser window, move it back to the top
if (this.yPos > browserHeight) {
this.yPos = -50;
}
}
}
//
// A performant way to set your snowflake's position and size
//
function setTransform(xPos, yPos, scale, el) {
el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0) scale(${scale}, ${scale})`;
}
//
// The function responsible for creating the snowflake
//
function generateSnowflakes() {
// get our snowflake element from the DOM and store it
let originalSnowflake = document.querySelector(".snowflake");
// access our snowflake element's parent container
snowflakeContainer.style.display = "block";
// get our browser's size
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
// create each individual snowflake
for (let i = 0; i < numberOfSnowflakes; i++) {
// clone our original snowflake and add it to snowflakeContainer
let snowflakeClone = originalSnowflake.cloneNode(true);
snowflakeContainer.appendChild(snowflakeClone);
// set our snowflake's initial position and related properties
let initialXPos = getPosition(50, browserWidth);
let initialYPos = getPosition(50, browserHeight);
let speed = 5 + Math.random() * 40;
// create our Snowflake object
let snowflakeObject = new Snowflake(snowflakeClone,
speed,
initialXPos,
initialYPos);
snowflakes.push(snowflakeObject);
}
// remove the original snowflake because we no longer need it visible
snowflakeContainer.removeChild(originalSnowflake);
moveSnowflakes();
}
//
// Responsible for moving each snowflake by calling its update function
//
function moveSnowflakes() {
if (enableAnimations) {
for (let i = 0; i < snowflakes.length; i++) {
let snowflake = snowflakes[i];
snowflake.update();
}
}
// Reset the position of all the snowflakes to a new value
if (resetPosition) {
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
for (let i = 0; i < snowflakes.length; i++) {
let snowflake = snowflakes[i];
snowflake.xPos = getPosition(50, browserWidth);
snowflake.yPos = getPosition(50, browserHeight);
}
resetPosition = false;
}
requestAnimationFrame(moveSnowflakes);
}
//
// This function returns a number between (maximum - offset) and (maximum + offset)
//
function getPosition(offset, size) {
return Math.round(-1 * offset + Math.random() * (size + 2 * offset));
}
//
// Trigger a reset of all the snowflakes' positions
//
function setResetFlag(e) {
resetPosition = true;
}
</script>
</body>
</html>
I made a few minor coding cleanup changes like using classes instead of the older approach. I’ll get the main article updated shortly
You can tweak the screen size breakpoints and number of snowflakes by just fiddling with the media queries! That should minimize your need to modify the JavaScript directly.