PageSourceSearch

https://bulsca.co.uk/js/Snow.js

js bulsca.co.uk collected 2026-10-04 04:02:01 UTC 4,696 bytes, 172 lines download raw bytes

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.