-
Notifications
You must be signed in to change notification settings - Fork 2
Snowfall v2 #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Snowfall v2 #62
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,127 @@ | ||||||||||
| <script lang="ts"> | ||||||||||
| import { onDestroy, onMount } from 'svelte'; | ||||||||||
|
|
||||||||||
| let canvas: HTMLCanvasElement; | ||||||||||
| let ctx: CanvasRenderingContext2D | null = null; | ||||||||||
| let width = 0; | ||||||||||
| let height = 0; | ||||||||||
| let dpr = 1; | ||||||||||
| let raf = 0; | ||||||||||
|
|
||||||||||
| interface Flake { | ||||||||||
| x: number; | ||||||||||
| y: number; | ||||||||||
| r: number; | ||||||||||
| vx: number; | ||||||||||
| vy: number; | ||||||||||
| phase: number; | ||||||||||
| opacity: number; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| let flakes: Flake[] = []; | ||||||||||
|
|
||||||||||
| function makeFlake(): Flake { | ||||||||||
| const r = Math.random() * 2.2 + 0.8; // 0.8 - 3.0 px | ||||||||||
| const speed = 0.4 + r * 0.35; // tie speed to size | ||||||||||
| return { | ||||||||||
| x: Math.random() * width, | ||||||||||
| y: -10 - Math.random() * height, | ||||||||||
| r, | ||||||||||
| vx: (Math.random() - 0.5) * 0.4, | ||||||||||
| vy: speed, | ||||||||||
| phase: Math.random() * Math.PI * 2, | ||||||||||
| opacity: 0.6 + Math.random() * 0.4 | ||||||||||
| }; | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function setCanvasSize() { | ||||||||||
| width = window.innerWidth; | ||||||||||
| height = window.innerHeight; | ||||||||||
| canvas.width = Math.floor(width * dpr); | ||||||||||
| canvas.height = Math.floor(height * dpr); | ||||||||||
| canvas.style.width = width + 'px'; | ||||||||||
| canvas.style.height = height + 'px'; | ||||||||||
| ctx && ctx.setTransform(dpr, 0, 0, dpr, 0, 0); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function populateFlakes(target: number) { | ||||||||||
| flakes.length = 0; | ||||||||||
| for (let i = 0; i < target; i++) { | ||||||||||
| const f = makeFlake(); | ||||||||||
| f.y = Math.random() * height; | ||||||||||
| flakes.push(f); | ||||||||||
| } | ||||||||||
| } | ||||||||||
|
|
||||||||||
| function draw() { | ||||||||||
| if (!ctx) return; | ||||||||||
|
||||||||||
| if (!ctx) return; | |
| if (!ctx || width <= 0 || height <= 0) return; |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The density calculation uses magic numbers (220, 60, 15000) without explanation. These values should be extracted as named constants with comments explaining their purpose and how they were chosen, improving code maintainability.
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The handleResize function is called twice during initialization - once directly at line 97 and once again at line 96 via setCanvasSize (which sets dimensions), making the line 97 call redundant since handleResize already calls setCanvasSize at line 86. This causes unnecessary duplication of work during component initialization.
| setCanvasSize(); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The animation loop continues running even when the canvas is hidden due to prefers-reduced-motion. The animation should be paused or stopped when this media query is active to avoid unnecessary CPU usage and respect user preferences for reduced motion.
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cleanup function returned from onMount will not be executed. In Svelte, onMount does not support returning a cleanup function. The cleanup logic should be moved to the onDestroy hook instead, which already exists at lines 106-109. The current implementation has duplication between the onMount return statement and the onDestroy hook.
| return () => { | |
| if (typeof cancelAnimationFrame === 'function') cancelAnimationFrame(raf); | |
| window.removeEventListener('resize', handleResize); | |
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,26 @@ | ||
| <script lang="ts"> | ||
| import '../app.css'; | ||
| import Snowfall from '$lib/components/Snowfall.svelte'; | ||
|
|
||
| let { children } = $props(); | ||
| </script> | ||
|
|
||
| {@render children?.()} | ||
| <Snowfall /> | ||
|
|
||
| <div class="app-content"> | ||
| {@render children?.()} | ||
| </div> | ||
|
|
||
| <style> | ||
| .app-content { | ||
| position: relative; | ||
| z-index: 1; | ||
| min-height: 100dvh; | ||
| } | ||
| :global(html, body) { | ||
| min-height: 100%; | ||
| } | ||
| :global(body) { | ||
| position: relative; | ||
| } | ||
| </style> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The makeFlake function uses magic numbers (2.2, 0.8, 0.4, 0.35, etc.) for snowflake properties without explanation. These should be extracted as named constants to improve code readability and make it easier to adjust the visual effect.