1let NUMBER_OF_SNOWFLAKES = 1000; 2let MAX_SNOWFLAKE_SIZE = 3; 3let MAX_SNOWFLAKE_SPEED = 2; 4const SNOWFLAKE_COLOUR = "#ddd"; 5 6MOUSE_X = 0; 7MOUSE_Y = 0; 8 9let snowflakes = []; 10 11letItSnow = (containerId = null) => { 12 const prefersReducedMotion = window.matchMedia( 13 "(prefers-reduced-motion: reduce)" 14 ).matches; 15 if (prefersReducedMotion) { 16 console.log( 17 "Reduced motion preference detected. No snow will be displayed." 18 ); 19 return; 20 } 21 22 const setSnowflakesByScreenWidth = () => { 23 let screenWidth = window.innerWidth; 24 25 if (screenWidth > 1080) { 26 NUMBER_OF_SNOWFLAKES = 500; 27 } else if (screenWidth > 500) { 28 NUMBER_OF_SNOWFLAKES = 250; 29 } else { 30 NUMBER_OF_SNOWFLAKES = 125; 31 } 32 }; 33 34 setSnowflakesByScreenWidth(); 35 36 // Get container or use body 37 const container = containerId ? document.getElementById(containerId) : document.body; 38 if (!container) { 39 console.error(`Container ${containerId} not found`); 40 return; 41 } 42 43 const canvas = document.createElement("canvas"); 44 canvas.style.position = "absolute"; 45 canvas.style.pointerEvents = "none"; 46 canvas.style.top = "0px"; 47 canvas.style.left = "0px"; 48 canvas.style.overflow = "hidden"; 49 canvas.style.zIndex = "1000"; 50 51 // Size canvas to container 52 const updateCanvasSize = () => { 53 if (containerId) { 54 const rect = container.getBoundingClientRect(); 55 canvas.width = rect.width; 56 canvas.height = rect.height; 57 } else { 58 canvas.width = window.innerWidth; 59 canvas.height = window.innerHeight; 60 } 61 }; 62 63 updateCanvasSize(); 64 65 // If using a container, make it position relative if not already positioned 66 if (containerId) { 67 const containerPosition = window.getComputedStyle(container).position; 68 if (containerPosition === 'static') { 69 container.style.position = 'relative'; 70 } 71 } 72 73 container.appendChild(canvas); 74 75 const ctx = canvas.getContext("2d"); 76 77 const createSnowflake = () => ({ 78 x: Math.random() * canvas.width, 79 y: 0, 80 radius: Math.floor(Math.random() * MAX_SNOWFLAKE_SIZE) + 1, 81 color: SNOWFLAKE_COLOUR, 82 speed: Math.random() * MAX_SNOWFLAKE_SPEED + 1, 83 sway: Math.random() - 0.5, 84 }); 85 86 const drawSnowflake = (snowflake) => { 87 ctx.beginPath(); 88 ctx.arc(snowflake.x, snowflake.y, snowflake.radius, 0, Math.PI * 2); 89 ctx.fillStyle = snowflake.color; 90 ctx.fill(); 91 ctx.closePath(); 92 }; 93 94 const updateSnowflake = (snowflake) => { 95 distance = distanceFromMouse(snowflake.x, snowflake.y); 96 97 middle = window.innerWidth / 2; 98 distanceFromMiddle = middle - MOUSE_X; 99 100 swayDir = distanceFromMiddle / middle; 101 102 snowflake.y += snowflake.speed; 103 snowflake.x += snowflake.sway; 104 105 if (snowflake.x > canvas.width) { 106 snowflake.x = 0; 107 } 108 109 if (snowflake.x < 0) { 110 snowflake.x = canvas.width; 111 } 112 113 if (snowflake.y > canvas.height) { 114 Object.assign(snowflake, createSnowflake()); 115 } 116 }; 117 118 const distanceFromMouse = (x, y) => { 119 return Math.sqrt((MOUSE_X - x) ** 2 + (MOUSE_Y - y) ** 2); 120 }; 121 122 const animate = () => { 123 ctx.clearRect(0, 0, canvas.width, canvas.height); 124 125 snowflakes.forEach((snowflake) => { 126 drawSnowflake(snowflake); 127 updateSnowflake(snowflake); 128 }); 129 130 requestAnimationFrame(animate); 131 }; 132 133 animate(); 134 135 if (localStorage.getItem("snow") != null) { 136 snowflakes = JSON.parse(localStorage.getItem("snow")); 137 } 138 139 let interval = setInterval(() => { 140 if (snowflakes.length > NUMBER_OF_SNOWFLAKES) { 141 snowflakes.pop(); 142 } else { 143 snowflakes.push(createSnowflake()); 144 } 145 }, 50); 146 147 window.addEventListener("resize", () => { 148 updateCanvasSize(); 149 setSnowflakesByScreenWidth(); 150 }); 151 152 // Only add scroll listener if not using a container 153 if (!containerId) { 154 window.addEventListener("scroll", () => { 155 canvas.style.top = `${window.scrollY}px`;
156 }); 157 } 158 159 window.addEventListener("mousemove", (e) => { 160 MOUSE_X = e.clientX; 161 MOUSE_Y = e.clientY; 162 }); 163 164 setInterval(() => { 165 localStorage.setItem("snow", JSON.stringify(snowflakes)); 166 }, 500); 167 168 setInterval(() => { 169 MAX_SNOWFLAKE_SIZE = Math.max(2, Math.random() * 5); 170 MAX_SNOWFLAKE_SPEED = Math.max(1, Math.random() * 3); 171 }, 5000); 172};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.