@hdcodedev/snowfall 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +10 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,22 +35,22 @@ var import_react2 = require("react");
|
|
|
35
35
|
var import_react = require("react");
|
|
36
36
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
37
37
|
var DEFAULT_PHYSICS = {
|
|
38
|
-
MAX_FLAKES:
|
|
39
|
-
MELT_SPEED:
|
|
40
|
-
WIND_STRENGTH:
|
|
38
|
+
MAX_FLAKES: 1e3,
|
|
39
|
+
MELT_SPEED: 1e-5,
|
|
40
|
+
WIND_STRENGTH: 1.5,
|
|
41
41
|
ACCUMULATION: {
|
|
42
|
-
SIDE_RATE: 1
|
|
43
|
-
TOP_RATE:
|
|
44
|
-
BOTTOM_RATE:
|
|
42
|
+
SIDE_RATE: 1,
|
|
43
|
+
TOP_RATE: 5,
|
|
44
|
+
BOTTOM_RATE: 5
|
|
45
45
|
},
|
|
46
46
|
MAX_DEPTH: {
|
|
47
|
-
TOP:
|
|
48
|
-
BOTTOM:
|
|
49
|
-
SIDE:
|
|
47
|
+
TOP: 100,
|
|
48
|
+
BOTTOM: 50,
|
|
49
|
+
SIDE: 20
|
|
50
50
|
},
|
|
51
51
|
FLAKE_SIZE: {
|
|
52
52
|
MIN: 0.5,
|
|
53
|
-
MAX:
|
|
53
|
+
MAX: 1.6
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
var SnowfallContext = (0, import_react.createContext)(void 0);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/Snowfall.tsx","../src/SnowfallProvider.tsx","../src/utils/snowfall/constants.ts","../src/utils/snowfall/dom.ts","../src/utils/snowfall/physics.ts","../src/utils/snowfall/draw.ts"],"sourcesContent":["'use client';\n\nexport { default as Snowfall } from './Snowfall';\nexport { SnowfallProvider, useSnowfall, DEFAULT_PHYSICS } from './SnowfallProvider';\nexport type { PhysicsConfig } from './SnowfallProvider';\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from './utils/snowfall/types';\nimport { getElementRects } from './utils/snowfall/dom';\nimport { createSnowflake, initializeAccumulation, meltAndSmoothAccumulation, updateSnowflakes } from './utils/snowfall/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflake } from './utils/snowfall/draw';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const fixedCanvasRef = useRef<HTMLCanvasElement>(null);\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n const animationIdRef = useRef<number>(0);\n\n useEffect(() => {\n setIsMounted(true);\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n const fixedCanvas = fixedCanvasRef.current;\n if (!canvas || !fixedCanvas) return;\n\n const ctx = canvas.getContext('2d');\n const fixedCtx = fixedCanvas.getContext('2d');\n if (!ctx || !fixedCtx) return;\n\n const resizeCanvas = () => {\n if (canvasRef.current && fixedCanvasRef.current) {\n const newHeight = Math.max(document.documentElement.scrollHeight, window.innerHeight);\n const newWidth = Math.max(document.documentElement.scrollWidth, window.innerWidth);\n\n if (canvasRef.current.height !== newHeight || canvasRef.current.width !== newWidth) {\n canvasRef.current.width = newWidth;\n canvasRef.current.height = newHeight;\n }\n\n // Fixed canvas matches viewport\n if (fixedCanvasRef.current.width !== window.innerWidth || fixedCanvasRef.current.height !== window.innerHeight) {\n fixedCanvasRef.current.width = window.innerWidth;\n fixedCanvasRef.current.height = window.innerHeight;\n }\n }\n };\n resizeCanvas();\n\n const resizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n resizeObserver.observe(document.body);\n\n snowflakesRef.current = [];\n\n const initAccumulationWrapper = () => {\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n };\n initAccumulationWrapper();\n\n setIsVisible(true);\n\n let lastTime = 0;\n\n const animate = (currentTime: number) => {\n if (lastTime === 0) {\n lastTime = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const deltaTime = Math.min(currentTime - lastTime, 50);\n lastTime = currentTime;\n const dt = deltaTime / 16.67;\n\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n fixedCtx.clearRect(0, 0, fixedCanvas.width, fixedCanvas.height);\n\n const snowflakes = snowflakesRef.current;\n const elementRects = getElementRects(accumulationRef.current);\n\n // Physics Update: Melt and Smooth\n meltAndSmoothAccumulation(elementRects, physicsConfigRef.current, dt);\n\n // Physics Update: Snowflakes & Collisions\n updateSnowflakes(snowflakes, elementRects, physicsConfigRef.current, dt, canvas.width, canvas.height);\n\n // Draw Snowflakes\n for (const flake of snowflakes) {\n drawSnowflake(ctx, flake);\n }\n\n // Spawn new snowflakes\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(canvas.width, physicsConfigRef.current, isBackground));\n }\n\n // Draw Accumulation\n drawAccumulations(ctx, fixedCtx, elementRects);\n drawSideAccumulations(ctx, fixedCtx, elementRects);\n\n animationIdRef.current = requestAnimationFrame(animate);\n };\n\n animationIdRef.current = requestAnimationFrame(animate);\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n };\n\n window.addEventListener('resize', handleResize);\n const checkInterval = setInterval(initAccumulationWrapper, 3000);\n\n return () => {\n cancelAnimationFrame(animationIdRef.current);\n window.removeEventListener('resize', handleResize);\n clearInterval(checkInterval);\n resizeObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n }}\n aria-hidden=\"true\"\n />\n <canvas\n ref={fixedCanvasRef}\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n }}\n aria-hidden=\"true\"\n />\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 500,\n MELT_SPEED: 0.00005,\n WIND_STRENGTH: 0.8,\n ACCUMULATION: {\n SIDE_RATE: 1.2,\n TOP_RATE: 1.9,\n BOTTOM_RATE: 1.2,\n },\n MAX_DEPTH: {\n TOP: 50,\n BOTTOM: 25,\n SIDE: 8,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 2.5,\n }\n};\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children }: { children: ReactNode }) {\n const [isEnabled, setIsEnabled] = useState(true);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\nconst BOTTOM_TAGS = [TAG_HEADER, TAG_FOOTER];\nconst BOTTOM_ROLES = [ROLE_BANNER, ROLE_CONTENTINFO];\n\nconst AUTO_DETECT_TAGS = ['header', 'footer', 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = ['[role=\"banner\"]', '[role=\"contentinfo\"]', '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element): boolean => {\n // Explicit opt-out\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n // Heuristics\n const styles = window.getComputedStyle(el);\n const rect = el.getBoundingClientRect();\n\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = parseFloat(styles.borderWidth) > 0 || styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasBorderRadius = parseFloat(styles.borderRadius) > 0;\n\n return hasBackground || hasBorder || hasBoxShadow || hasBorderRadius;\n};\n\nexport const getAccumulationSurfaces = (): { el: Element; type: SnowfallSurface; isFixed: boolean }[] => {\n const surfaces: { el: Element; type: SnowfallSurface; isFixed: boolean }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n candidates.forEach(el => {\n if (seen.has(el)) return;\n\n const rect = el.getBoundingClientRect();\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) return;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n\n const styles = window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) return;\n\n // Skip really small elements unless manually forced\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) return;\n\n // HEURISTIC: Skip full-page wrappers\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n\n\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole ||\n manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) return;\n\n let isFixed = false;\n let currentEl: Element | null = el;\n while (currentEl && currentEl !== document.body) {\n const style = window.getComputedStyle(currentEl);\n if (style.position === 'fixed' || style.position === 'sticky') {\n isFixed = true;\n break;\n }\n currentEl = currentEl.parentElement;\n }\n\n if (shouldAccumulate(el)) {\n // Determine type: manual override takes precedence\n let type: SnowfallSurface = getElementType(el);\n\n if (manualOverride === VAL_BOTTOM) {\n type = VAL_BOTTOM;\n } else if (manualOverride === VAL_TOP) {\n type = VAL_TOP;\n }\n\n surfaces.push({ el, type, isFixed });\n seen.add(el);\n }\n });\n\n console.log(`[Snowfall] Auto-detection found ${surfaces.length} surfaces`);\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n const rect = el.getBoundingClientRect();\n // Convert viewport coordinates to absolute page coordinates\n const absoluteRect = {\n left: rect.left + window.scrollX,\n right: rect.right + window.scrollX,\n top: rect.top + window.scrollY,\n bottom: rect.bottom + window.scrollY,\n width: rect.width,\n height: rect.height,\n x: rect.x, // Note: these are strictly viewport relative in DOMRect usually, \n // but we just need consistent absolute coords for physics\n y: rect.y,\n toJSON: rect.toJSON\n };\n // We cast because we constructed a compatible object, though strictly DOMRect has readonly properties\n elementRects.push({ el, rect: absoluteRect as unknown as DOMRect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../../SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM } from './constants';\n\nexport const createSnowflake = (\n canvasWidth: number,\n config: PhysicsConfig,\n isBackground: boolean = false\n): Snowflake => {\n if (isBackground) {\n const sizeRatio = Math.random();\n const radius = config.FLAKE_SIZE.MIN * 0.6 + sizeRatio * (config.FLAKE_SIZE.MAX - config.FLAKE_SIZE.MIN) * 0.4;\n return {\n x: Math.random() * canvasWidth,\n y: window.scrollY - 5,\n radius,\n speed: radius * 0.3 + Math.random() * 0.2 + 0.2,\n wind: (Math.random() - 0.5) * (config.WIND_STRENGTH * 0.625),\n opacity: Math.random() * 0.2 + 0.2,\n wobble: Math.random() * Math.PI * 2,\n wobbleSpeed: Math.random() * 0.015 + 0.005,\n sizeRatio,\n isBackground: true\n };\n } else {\n const sizeRatio = Math.random();\n const radius = config.FLAKE_SIZE.MIN + sizeRatio * (config.FLAKE_SIZE.MAX - config.FLAKE_SIZE.MIN);\n return {\n x: Math.random() * canvasWidth,\n y: window.scrollY - 5,\n radius,\n speed: radius * 0.5 + Math.random() * 0.3 + 0.5,\n wind: (Math.random() - 0.5) * config.WIND_STRENGTH,\n opacity: Math.random() * 0.3 + 0.5,\n wobble: Math.random() * Math.PI * 2,\n wobbleSpeed: Math.random() * 0.02 + 0.01,\n sizeRatio,\n isBackground: false\n };\n }\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n const elements = getAccumulationSurfaces();\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type, isFixed }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n existing.isFixed = isFixed;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el); // Potentially slow in loop, strictly necessary?\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n // Smooth maxHeights\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n borderRadius,\n type,\n isFixed,\n });\n });\n};\n\nconst accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig\n) => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n sideArray[y] = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n }\n }\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n canvasWidth: number,\n canvasHeight: number\n) => {\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n flake.wobble += flake.wobbleSpeed * dt;\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n\n let landed = false;\n\n for (const { rect, acc } of elementRects) {\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Side collisions\n if (!landed && acc.maxSideHeight > 0 && !isBottom) {\n const isInVerticalBounds = flake.y >= rect.top && flake.y <= rect.bottom;\n\n if (isInVerticalBounds) {\n const localY = Math.floor(flake.y - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (flake.x >= rect.left - 5 && flake.x < rect.left + 3) {\n if (!isCorner) {\n accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (!landed && flake.x > rect.right - 3 && flake.x <= rect.right + 5) {\n if (!isCorner) {\n accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (landed) break;\n }\n }\n\n // Top accumulation\n if (flake.x >= rect.left && flake.x <= rect.right) {\n const localX = Math.floor(flake.x - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flake.y >= surfaceY && flake.y < surfaceY + 10 && currentHeight < maxHeight) {\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n if (isBottom) {\n landed = true;\n break;\n }\n }\n\n if (!isBottom) {\n landed = true;\n break;\n }\n }\n }\n }\n\n if (landed || flake.y > canvasHeight + 10 || flake.x < -20 || flake.x > canvasWidth + 20) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.rightSide[i] > 0) acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n }\n }\n};\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM } from './constants';\n\nexport const drawSnowflake = (ctx: CanvasRenderingContext2D, flake: Snowflake) => {\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2);\n ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity})`;\n ctx.fill();\n\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius * 1.5, 0, Math.PI * 2);\n ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity * 0.2})`;\n ctx.fill();\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n fixedCtx: CanvasRenderingContext2D | null,\n elementRects: ElementSurface[]\n) => {\n // Setup styles for both contexts\n const setupCtx = (c: CanvasRenderingContext2D) => {\n c.fillStyle = 'rgba(255, 255, 255, 0.95)';\n c.shadowColor = 'rgba(200, 230, 255, 0.6)';\n c.shadowBlur = 4;\n c.shadowOffsetY = -1;\n };\n setupCtx(ctx);\n if (fixedCtx) setupCtx(fixedCtx);\n\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (const { rect, acc } of elementRects) {\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const useFixed = acc.isFixed && fixedCtx;\n const targetCtx = useFixed ? fixedCtx! : ctx;\n\n // If using fixed context, we need to convert absolute coordinates (rect) back to viewport coordinates\n const dx = useFixed ? -scrollX : 0;\n const dy = useFixed ? -scrollY : 0;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n const borderRadius = acc.borderRadius;\n\n const getCurveOffset = (xPos: number) => {\n if (borderRadius <= 0 || isBottom) return 0;\n let offset = 0;\n if (xPos < borderRadius) {\n const dist = borderRadius - xPos;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (xPos > rect.width - borderRadius) {\n const dist = xPos - (rect.width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n return offset;\n };\n\n targetCtx.beginPath();\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = rect.left + x + dx;\n const py = baseY - height + getCurveOffset(x) + dy;\n if (first) {\n targetCtx.moveTo(px, py);\n first = false;\n } else {\n targetCtx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = rect.left + x + dx;\n const py = baseY - height + getCurveOffset(x) + dy;\n targetCtx.lineTo(px, py);\n }\n\n for (let x = len - 1; x >= 0; x -= step) {\n const px = rect.left + x + dx;\n const py = baseY + getCurveOffset(x) + dy;\n targetCtx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = rect.left + startX + dx;\n const startPy = baseY + getCurveOffset(startX) + dy;\n targetCtx.lineTo(startPx, startPy);\n\n targetCtx.closePath();\n targetCtx.fill();\n }\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n if (fixedCtx) {\n fixedCtx.shadowBlur = 0;\n fixedCtx.shadowOffsetY = 0;\n }\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n fixedCtx: CanvasRenderingContext2D | null,\n elementRects: ElementSurface[]\n) => {\n const setupCtx = (c: CanvasRenderingContext2D) => {\n c.fillStyle = 'rgba(255, 255, 255, 0.95)';\n c.shadowColor = 'rgba(200, 230, 255, 0.6)';\n c.shadowBlur = 3;\n };\n setupCtx(ctx);\n if (fixedCtx) setupCtx(fixedCtx);\n\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (const { rect, acc } of elementRects) {\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftSide.some(h => h > 0.3);\n const hasRightSnow = acc.rightSide.some(h => h > 0.3);\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n const useFixed = acc.isFixed && fixedCtx;\n const targetCtx = useFixed ? fixedCtx! : ctx;\n const dx = useFixed ? -scrollX : 0;\n const dy = useFixed ? -scrollY : 0;\n\n const drawSide = (sideArray: number[], isLeft: boolean) => {\n targetCtx.beginPath();\n const baseX = isLeft ? rect.left : rect.right;\n targetCtx.moveTo(baseX + dx, rect.top + dy);\n\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const py = rect.top + y + dy;\n const px = (isLeft ? baseX - width : baseX + width) + dx;\n const ny = rect.top + nextY + dy;\n const nx = (isLeft ? baseX - nextWidth : baseX + nextWidth) + dx;\n\n targetCtx.lineTo(px, py);\n targetCtx.lineTo(nx, ny);\n }\n targetCtx.lineTo(baseX + dx, rect.bottom + dy);\n targetCtx.closePath();\n targetCtx.fill();\n };\n\n if (hasLeftSnow) drawSide(acc.leftSide, true);\n if (hasRightSnow) drawSide(acc.rightSide, false);\n }\n\n ctx.shadowBlur = 0;\n if (fixedCtx) fixedCtx.shadowBlur = 0;\n};\n"],"mappings":"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","names":["import_react","shouldAccumulate","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Snowfall.tsx","../src/SnowfallProvider.tsx","../src/utils/snowfall/constants.ts","../src/utils/snowfall/dom.ts","../src/utils/snowfall/physics.ts","../src/utils/snowfall/draw.ts"],"sourcesContent":["'use client';\n\nexport { default as Snowfall } from './Snowfall';\nexport { SnowfallProvider, useSnowfall, DEFAULT_PHYSICS } from './SnowfallProvider';\nexport type { PhysicsConfig } from './SnowfallProvider';\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from './utils/snowfall/types';\nimport { getElementRects } from './utils/snowfall/dom';\nimport { createSnowflake, initializeAccumulation, meltAndSmoothAccumulation, updateSnowflakes } from './utils/snowfall/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflake } from './utils/snowfall/draw';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const fixedCanvasRef = useRef<HTMLCanvasElement>(null);\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n const animationIdRef = useRef<number>(0);\n\n useEffect(() => {\n setIsMounted(true);\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n const fixedCanvas = fixedCanvasRef.current;\n if (!canvas || !fixedCanvas) return;\n\n const ctx = canvas.getContext('2d');\n const fixedCtx = fixedCanvas.getContext('2d');\n if (!ctx || !fixedCtx) return;\n\n const resizeCanvas = () => {\n if (canvasRef.current && fixedCanvasRef.current) {\n const newHeight = Math.max(document.documentElement.scrollHeight, window.innerHeight);\n const newWidth = Math.max(document.documentElement.scrollWidth, window.innerWidth);\n\n if (canvasRef.current.height !== newHeight || canvasRef.current.width !== newWidth) {\n canvasRef.current.width = newWidth;\n canvasRef.current.height = newHeight;\n }\n\n // Fixed canvas matches viewport\n if (fixedCanvasRef.current.width !== window.innerWidth || fixedCanvasRef.current.height !== window.innerHeight) {\n fixedCanvasRef.current.width = window.innerWidth;\n fixedCanvasRef.current.height = window.innerHeight;\n }\n }\n };\n resizeCanvas();\n\n const resizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n resizeObserver.observe(document.body);\n\n snowflakesRef.current = [];\n\n const initAccumulationWrapper = () => {\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n };\n initAccumulationWrapper();\n\n setIsVisible(true);\n\n let lastTime = 0;\n\n const animate = (currentTime: number) => {\n if (lastTime === 0) {\n lastTime = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const deltaTime = Math.min(currentTime - lastTime, 50);\n lastTime = currentTime;\n const dt = deltaTime / 16.67;\n\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n fixedCtx.clearRect(0, 0, fixedCanvas.width, fixedCanvas.height);\n\n const snowflakes = snowflakesRef.current;\n const elementRects = getElementRects(accumulationRef.current);\n\n // Physics Update: Melt and Smooth\n meltAndSmoothAccumulation(elementRects, physicsConfigRef.current, dt);\n\n // Physics Update: Snowflakes & Collisions\n updateSnowflakes(snowflakes, elementRects, physicsConfigRef.current, dt, canvas.width, canvas.height);\n\n // Draw Snowflakes\n for (const flake of snowflakes) {\n drawSnowflake(ctx, flake);\n }\n\n // Spawn new snowflakes\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(canvas.width, physicsConfigRef.current, isBackground));\n }\n\n // Draw Accumulation\n drawAccumulations(ctx, fixedCtx, elementRects);\n drawSideAccumulations(ctx, fixedCtx, elementRects);\n\n animationIdRef.current = requestAnimationFrame(animate);\n };\n\n animationIdRef.current = requestAnimationFrame(animate);\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n };\n\n window.addEventListener('resize', handleResize);\n const checkInterval = setInterval(initAccumulationWrapper, 3000);\n\n return () => {\n cancelAnimationFrame(animationIdRef.current);\n window.removeEventListener('resize', handleResize);\n clearInterval(checkInterval);\n resizeObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n }}\n aria-hidden=\"true\"\n />\n <canvas\n ref={fixedCanvasRef}\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n }}\n aria-hidden=\"true\"\n />\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 1000,\n MELT_SPEED: 0.00001,\n WIND_STRENGTH: 1.5,\n ACCUMULATION: {\n SIDE_RATE: 1,\n TOP_RATE: 5,\n BOTTOM_RATE: 5.0,\n },\n MAX_DEPTH: {\n TOP: 100,\n BOTTOM: 50,\n SIDE: 20,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 1.6,\n }\n};\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children }: { children: ReactNode }) {\n const [isEnabled, setIsEnabled] = useState(true);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\nconst BOTTOM_TAGS = [TAG_HEADER, TAG_FOOTER];\nconst BOTTOM_ROLES = [ROLE_BANNER, ROLE_CONTENTINFO];\n\nconst AUTO_DETECT_TAGS = ['header', 'footer', 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = ['[role=\"banner\"]', '[role=\"contentinfo\"]', '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element): boolean => {\n // Explicit opt-out\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n // Heuristics\n const styles = window.getComputedStyle(el);\n const rect = el.getBoundingClientRect();\n\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = parseFloat(styles.borderWidth) > 0 || styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasBorderRadius = parseFloat(styles.borderRadius) > 0;\n\n return hasBackground || hasBorder || hasBoxShadow || hasBorderRadius;\n};\n\nexport const getAccumulationSurfaces = (): { el: Element; type: SnowfallSurface; isFixed: boolean }[] => {\n const surfaces: { el: Element; type: SnowfallSurface; isFixed: boolean }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n candidates.forEach(el => {\n if (seen.has(el)) return;\n\n const rect = el.getBoundingClientRect();\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) return;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n\n const styles = window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) return;\n\n // Skip really small elements unless manually forced\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) return;\n\n // HEURISTIC: Skip full-page wrappers\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n\n\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole ||\n manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) return;\n\n let isFixed = false;\n let currentEl: Element | null = el;\n while (currentEl && currentEl !== document.body) {\n const style = window.getComputedStyle(currentEl);\n if (style.position === 'fixed' || style.position === 'sticky') {\n isFixed = true;\n break;\n }\n currentEl = currentEl.parentElement;\n }\n\n if (shouldAccumulate(el)) {\n // Determine type: manual override takes precedence\n let type: SnowfallSurface = getElementType(el);\n\n if (manualOverride === VAL_BOTTOM) {\n type = VAL_BOTTOM;\n } else if (manualOverride === VAL_TOP) {\n type = VAL_TOP;\n }\n\n surfaces.push({ el, type, isFixed });\n seen.add(el);\n }\n });\n\n console.log(`[Snowfall] Auto-detection found ${surfaces.length} surfaces`);\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n const rect = el.getBoundingClientRect();\n // Convert viewport coordinates to absolute page coordinates\n const absoluteRect = {\n left: rect.left + window.scrollX,\n right: rect.right + window.scrollX,\n top: rect.top + window.scrollY,\n bottom: rect.bottom + window.scrollY,\n width: rect.width,\n height: rect.height,\n x: rect.x, // Note: these are strictly viewport relative in DOMRect usually, \n // but we just need consistent absolute coords for physics\n y: rect.y,\n toJSON: rect.toJSON\n };\n // We cast because we constructed a compatible object, though strictly DOMRect has readonly properties\n elementRects.push({ el, rect: absoluteRect as unknown as DOMRect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../../SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM } from './constants';\n\nexport const createSnowflake = (\n canvasWidth: number,\n config: PhysicsConfig,\n isBackground: boolean = false\n): Snowflake => {\n if (isBackground) {\n const sizeRatio = Math.random();\n const radius = config.FLAKE_SIZE.MIN * 0.6 + sizeRatio * (config.FLAKE_SIZE.MAX - config.FLAKE_SIZE.MIN) * 0.4;\n return {\n x: Math.random() * canvasWidth,\n y: window.scrollY - 5,\n radius,\n speed: radius * 0.3 + Math.random() * 0.2 + 0.2,\n wind: (Math.random() - 0.5) * (config.WIND_STRENGTH * 0.625),\n opacity: Math.random() * 0.2 + 0.2,\n wobble: Math.random() * Math.PI * 2,\n wobbleSpeed: Math.random() * 0.015 + 0.005,\n sizeRatio,\n isBackground: true\n };\n } else {\n const sizeRatio = Math.random();\n const radius = config.FLAKE_SIZE.MIN + sizeRatio * (config.FLAKE_SIZE.MAX - config.FLAKE_SIZE.MIN);\n return {\n x: Math.random() * canvasWidth,\n y: window.scrollY - 5,\n radius,\n speed: radius * 0.5 + Math.random() * 0.3 + 0.5,\n wind: (Math.random() - 0.5) * config.WIND_STRENGTH,\n opacity: Math.random() * 0.3 + 0.5,\n wobble: Math.random() * Math.PI * 2,\n wobbleSpeed: Math.random() * 0.02 + 0.01,\n sizeRatio,\n isBackground: false\n };\n }\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n const elements = getAccumulationSurfaces();\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type, isFixed }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n existing.isFixed = isFixed;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el); // Potentially slow in loop, strictly necessary?\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n // Smooth maxHeights\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n borderRadius,\n type,\n isFixed,\n });\n });\n};\n\nconst accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig\n) => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n sideArray[y] = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n }\n }\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n canvasWidth: number,\n canvasHeight: number\n) => {\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n flake.wobble += flake.wobbleSpeed * dt;\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n\n let landed = false;\n\n for (const { rect, acc } of elementRects) {\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Side collisions\n if (!landed && acc.maxSideHeight > 0 && !isBottom) {\n const isInVerticalBounds = flake.y >= rect.top && flake.y <= rect.bottom;\n\n if (isInVerticalBounds) {\n const localY = Math.floor(flake.y - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (flake.x >= rect.left - 5 && flake.x < rect.left + 3) {\n if (!isCorner) {\n accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (!landed && flake.x > rect.right - 3 && flake.x <= rect.right + 5) {\n if (!isCorner) {\n accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (landed) break;\n }\n }\n\n // Top accumulation\n if (flake.x >= rect.left && flake.x <= rect.right) {\n const localX = Math.floor(flake.x - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flake.y >= surfaceY && flake.y < surfaceY + 10 && currentHeight < maxHeight) {\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n if (isBottom) {\n landed = true;\n break;\n }\n }\n\n if (!isBottom) {\n landed = true;\n break;\n }\n }\n }\n }\n\n if (landed || flake.y > canvasHeight + 10 || flake.x < -20 || flake.x > canvasWidth + 20) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.rightSide[i] > 0) acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n }\n }\n};\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM } from './constants';\n\nexport const drawSnowflake = (ctx: CanvasRenderingContext2D, flake: Snowflake) => {\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2);\n ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity})`;\n ctx.fill();\n\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius * 1.5, 0, Math.PI * 2);\n ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity * 0.2})`;\n ctx.fill();\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n fixedCtx: CanvasRenderingContext2D | null,\n elementRects: ElementSurface[]\n) => {\n // Setup styles for both contexts\n const setupCtx = (c: CanvasRenderingContext2D) => {\n c.fillStyle = 'rgba(255, 255, 255, 0.95)';\n c.shadowColor = 'rgba(200, 230, 255, 0.6)';\n c.shadowBlur = 4;\n c.shadowOffsetY = -1;\n };\n setupCtx(ctx);\n if (fixedCtx) setupCtx(fixedCtx);\n\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (const { rect, acc } of elementRects) {\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const useFixed = acc.isFixed && fixedCtx;\n const targetCtx = useFixed ? fixedCtx! : ctx;\n\n // If using fixed context, we need to convert absolute coordinates (rect) back to viewport coordinates\n const dx = useFixed ? -scrollX : 0;\n const dy = useFixed ? -scrollY : 0;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n const borderRadius = acc.borderRadius;\n\n const getCurveOffset = (xPos: number) => {\n if (borderRadius <= 0 || isBottom) return 0;\n let offset = 0;\n if (xPos < borderRadius) {\n const dist = borderRadius - xPos;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (xPos > rect.width - borderRadius) {\n const dist = xPos - (rect.width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n return offset;\n };\n\n targetCtx.beginPath();\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = rect.left + x + dx;\n const py = baseY - height + getCurveOffset(x) + dy;\n if (first) {\n targetCtx.moveTo(px, py);\n first = false;\n } else {\n targetCtx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = rect.left + x + dx;\n const py = baseY - height + getCurveOffset(x) + dy;\n targetCtx.lineTo(px, py);\n }\n\n for (let x = len - 1; x >= 0; x -= step) {\n const px = rect.left + x + dx;\n const py = baseY + getCurveOffset(x) + dy;\n targetCtx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = rect.left + startX + dx;\n const startPy = baseY + getCurveOffset(startX) + dy;\n targetCtx.lineTo(startPx, startPy);\n\n targetCtx.closePath();\n targetCtx.fill();\n }\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n if (fixedCtx) {\n fixedCtx.shadowBlur = 0;\n fixedCtx.shadowOffsetY = 0;\n }\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n fixedCtx: CanvasRenderingContext2D | null,\n elementRects: ElementSurface[]\n) => {\n const setupCtx = (c: CanvasRenderingContext2D) => {\n c.fillStyle = 'rgba(255, 255, 255, 0.95)';\n c.shadowColor = 'rgba(200, 230, 255, 0.6)';\n c.shadowBlur = 3;\n };\n setupCtx(ctx);\n if (fixedCtx) setupCtx(fixedCtx);\n\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (const { rect, acc } of elementRects) {\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftSide.some(h => h > 0.3);\n const hasRightSnow = acc.rightSide.some(h => h > 0.3);\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n const useFixed = acc.isFixed && fixedCtx;\n const targetCtx = useFixed ? fixedCtx! : ctx;\n const dx = useFixed ? -scrollX : 0;\n const dy = useFixed ? -scrollY : 0;\n\n const drawSide = (sideArray: number[], isLeft: boolean) => {\n targetCtx.beginPath();\n const baseX = isLeft ? rect.left : rect.right;\n targetCtx.moveTo(baseX + dx, rect.top + dy);\n\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const py = rect.top + y + dy;\n const px = (isLeft ? baseX - width : baseX + width) + dx;\n const ny = rect.top + nextY + dy;\n const nx = (isLeft ? baseX - nextWidth : baseX + nextWidth) + dx;\n\n targetCtx.lineTo(px, py);\n targetCtx.lineTo(nx, ny);\n }\n targetCtx.lineTo(baseX + dx, rect.bottom + dy);\n targetCtx.closePath();\n targetCtx.fill();\n };\n\n if (hasLeftSnow) drawSide(acc.leftSide, true);\n if (hasRightSnow) drawSide(acc.rightSide, false);\n }\n\n ctx.shadowBlur = 0;\n if (fixedCtx) fixedCtx.shadowBlur = 0;\n};\n"],"mappings":"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","names":["import_react","shouldAccumulate","import_jsx_runtime"]}
|
package/dist/index.mjs
CHANGED
|
@@ -7,22 +7,22 @@ import { useEffect, useRef, useState as useState2 } from "react";
|
|
|
7
7
|
import { createContext, useContext, useState } from "react";
|
|
8
8
|
import { jsx } from "react/jsx-runtime";
|
|
9
9
|
var DEFAULT_PHYSICS = {
|
|
10
|
-
MAX_FLAKES:
|
|
11
|
-
MELT_SPEED:
|
|
12
|
-
WIND_STRENGTH:
|
|
10
|
+
MAX_FLAKES: 1e3,
|
|
11
|
+
MELT_SPEED: 1e-5,
|
|
12
|
+
WIND_STRENGTH: 1.5,
|
|
13
13
|
ACCUMULATION: {
|
|
14
|
-
SIDE_RATE: 1
|
|
15
|
-
TOP_RATE:
|
|
16
|
-
BOTTOM_RATE:
|
|
14
|
+
SIDE_RATE: 1,
|
|
15
|
+
TOP_RATE: 5,
|
|
16
|
+
BOTTOM_RATE: 5
|
|
17
17
|
},
|
|
18
18
|
MAX_DEPTH: {
|
|
19
|
-
TOP:
|
|
20
|
-
BOTTOM:
|
|
21
|
-
SIDE:
|
|
19
|
+
TOP: 100,
|
|
20
|
+
BOTTOM: 50,
|
|
21
|
+
SIDE: 20
|
|
22
22
|
},
|
|
23
23
|
FLAKE_SIZE: {
|
|
24
24
|
MIN: 0.5,
|
|
25
|
-
MAX:
|
|
25
|
+
MAX: 1.6
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
28
|
var SnowfallContext = createContext(void 0);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/Snowfall.tsx","../src/SnowfallProvider.tsx","../src/utils/snowfall/constants.ts","../src/utils/snowfall/dom.ts","../src/utils/snowfall/physics.ts","../src/utils/snowfall/draw.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from './utils/snowfall/types';\nimport { getElementRects } from './utils/snowfall/dom';\nimport { createSnowflake, initializeAccumulation, meltAndSmoothAccumulation, updateSnowflakes } from './utils/snowfall/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflake } from './utils/snowfall/draw';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const fixedCanvasRef = useRef<HTMLCanvasElement>(null);\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n const animationIdRef = useRef<number>(0);\n\n useEffect(() => {\n setIsMounted(true);\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n const fixedCanvas = fixedCanvasRef.current;\n if (!canvas || !fixedCanvas) return;\n\n const ctx = canvas.getContext('2d');\n const fixedCtx = fixedCanvas.getContext('2d');\n if (!ctx || !fixedCtx) return;\n\n const resizeCanvas = () => {\n if (canvasRef.current && fixedCanvasRef.current) {\n const newHeight = Math.max(document.documentElement.scrollHeight, window.innerHeight);\n const newWidth = Math.max(document.documentElement.scrollWidth, window.innerWidth);\n\n if (canvasRef.current.height !== newHeight || canvasRef.current.width !== newWidth) {\n canvasRef.current.width = newWidth;\n canvasRef.current.height = newHeight;\n }\n\n // Fixed canvas matches viewport\n if (fixedCanvasRef.current.width !== window.innerWidth || fixedCanvasRef.current.height !== window.innerHeight) {\n fixedCanvasRef.current.width = window.innerWidth;\n fixedCanvasRef.current.height = window.innerHeight;\n }\n }\n };\n resizeCanvas();\n\n const resizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n resizeObserver.observe(document.body);\n\n snowflakesRef.current = [];\n\n const initAccumulationWrapper = () => {\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n };\n initAccumulationWrapper();\n\n setIsVisible(true);\n\n let lastTime = 0;\n\n const animate = (currentTime: number) => {\n if (lastTime === 0) {\n lastTime = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const deltaTime = Math.min(currentTime - lastTime, 50);\n lastTime = currentTime;\n const dt = deltaTime / 16.67;\n\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n fixedCtx.clearRect(0, 0, fixedCanvas.width, fixedCanvas.height);\n\n const snowflakes = snowflakesRef.current;\n const elementRects = getElementRects(accumulationRef.current);\n\n // Physics Update: Melt and Smooth\n meltAndSmoothAccumulation(elementRects, physicsConfigRef.current, dt);\n\n // Physics Update: Snowflakes & Collisions\n updateSnowflakes(snowflakes, elementRects, physicsConfigRef.current, dt, canvas.width, canvas.height);\n\n // Draw Snowflakes\n for (const flake of snowflakes) {\n drawSnowflake(ctx, flake);\n }\n\n // Spawn new snowflakes\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(canvas.width, physicsConfigRef.current, isBackground));\n }\n\n // Draw Accumulation\n drawAccumulations(ctx, fixedCtx, elementRects);\n drawSideAccumulations(ctx, fixedCtx, elementRects);\n\n animationIdRef.current = requestAnimationFrame(animate);\n };\n\n animationIdRef.current = requestAnimationFrame(animate);\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n };\n\n window.addEventListener('resize', handleResize);\n const checkInterval = setInterval(initAccumulationWrapper, 3000);\n\n return () => {\n cancelAnimationFrame(animationIdRef.current);\n window.removeEventListener('resize', handleResize);\n clearInterval(checkInterval);\n resizeObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n }}\n aria-hidden=\"true\"\n />\n <canvas\n ref={fixedCanvasRef}\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n }}\n aria-hidden=\"true\"\n />\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 500,\n MELT_SPEED: 0.00005,\n WIND_STRENGTH: 0.8,\n ACCUMULATION: {\n SIDE_RATE: 1.2,\n TOP_RATE: 1.9,\n BOTTOM_RATE: 1.2,\n },\n MAX_DEPTH: {\n TOP: 50,\n BOTTOM: 25,\n SIDE: 8,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 2.5,\n }\n};\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children }: { children: ReactNode }) {\n const [isEnabled, setIsEnabled] = useState(true);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\nconst BOTTOM_TAGS = [TAG_HEADER, TAG_FOOTER];\nconst BOTTOM_ROLES = [ROLE_BANNER, ROLE_CONTENTINFO];\n\nconst AUTO_DETECT_TAGS = ['header', 'footer', 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = ['[role=\"banner\"]', '[role=\"contentinfo\"]', '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element): boolean => {\n // Explicit opt-out\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n // Heuristics\n const styles = window.getComputedStyle(el);\n const rect = el.getBoundingClientRect();\n\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = parseFloat(styles.borderWidth) > 0 || styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasBorderRadius = parseFloat(styles.borderRadius) > 0;\n\n return hasBackground || hasBorder || hasBoxShadow || hasBorderRadius;\n};\n\nexport const getAccumulationSurfaces = (): { el: Element; type: SnowfallSurface; isFixed: boolean }[] => {\n const surfaces: { el: Element; type: SnowfallSurface; isFixed: boolean }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n candidates.forEach(el => {\n if (seen.has(el)) return;\n\n const rect = el.getBoundingClientRect();\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) return;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n\n const styles = window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) return;\n\n // Skip really small elements unless manually forced\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) return;\n\n // HEURISTIC: Skip full-page wrappers\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n\n\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole ||\n manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) return;\n\n let isFixed = false;\n let currentEl: Element | null = el;\n while (currentEl && currentEl !== document.body) {\n const style = window.getComputedStyle(currentEl);\n if (style.position === 'fixed' || style.position === 'sticky') {\n isFixed = true;\n break;\n }\n currentEl = currentEl.parentElement;\n }\n\n if (shouldAccumulate(el)) {\n // Determine type: manual override takes precedence\n let type: SnowfallSurface = getElementType(el);\n\n if (manualOverride === VAL_BOTTOM) {\n type = VAL_BOTTOM;\n } else if (manualOverride === VAL_TOP) {\n type = VAL_TOP;\n }\n\n surfaces.push({ el, type, isFixed });\n seen.add(el);\n }\n });\n\n console.log(`[Snowfall] Auto-detection found ${surfaces.length} surfaces`);\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n const rect = el.getBoundingClientRect();\n // Convert viewport coordinates to absolute page coordinates\n const absoluteRect = {\n left: rect.left + window.scrollX,\n right: rect.right + window.scrollX,\n top: rect.top + window.scrollY,\n bottom: rect.bottom + window.scrollY,\n width: rect.width,\n height: rect.height,\n x: rect.x, // Note: these are strictly viewport relative in DOMRect usually, \n // but we just need consistent absolute coords for physics\n y: rect.y,\n toJSON: rect.toJSON\n };\n // We cast because we constructed a compatible object, though strictly DOMRect has readonly properties\n elementRects.push({ el, rect: absoluteRect as unknown as DOMRect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../../SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM } from './constants';\n\nexport const createSnowflake = (\n canvasWidth: number,\n config: PhysicsConfig,\n isBackground: boolean = false\n): Snowflake => {\n if (isBackground) {\n const sizeRatio = Math.random();\n const radius = config.FLAKE_SIZE.MIN * 0.6 + sizeRatio * (config.FLAKE_SIZE.MAX - config.FLAKE_SIZE.MIN) * 0.4;\n return {\n x: Math.random() * canvasWidth,\n y: window.scrollY - 5,\n radius,\n speed: radius * 0.3 + Math.random() * 0.2 + 0.2,\n wind: (Math.random() - 0.5) * (config.WIND_STRENGTH * 0.625),\n opacity: Math.random() * 0.2 + 0.2,\n wobble: Math.random() * Math.PI * 2,\n wobbleSpeed: Math.random() * 0.015 + 0.005,\n sizeRatio,\n isBackground: true\n };\n } else {\n const sizeRatio = Math.random();\n const radius = config.FLAKE_SIZE.MIN + sizeRatio * (config.FLAKE_SIZE.MAX - config.FLAKE_SIZE.MIN);\n return {\n x: Math.random() * canvasWidth,\n y: window.scrollY - 5,\n radius,\n speed: radius * 0.5 + Math.random() * 0.3 + 0.5,\n wind: (Math.random() - 0.5) * config.WIND_STRENGTH,\n opacity: Math.random() * 0.3 + 0.5,\n wobble: Math.random() * Math.PI * 2,\n wobbleSpeed: Math.random() * 0.02 + 0.01,\n sizeRatio,\n isBackground: false\n };\n }\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n const elements = getAccumulationSurfaces();\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type, isFixed }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n existing.isFixed = isFixed;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el); // Potentially slow in loop, strictly necessary?\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n // Smooth maxHeights\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n borderRadius,\n type,\n isFixed,\n });\n });\n};\n\nconst accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig\n) => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n sideArray[y] = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n }\n }\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n canvasWidth: number,\n canvasHeight: number\n) => {\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n flake.wobble += flake.wobbleSpeed * dt;\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n\n let landed = false;\n\n for (const { rect, acc } of elementRects) {\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Side collisions\n if (!landed && acc.maxSideHeight > 0 && !isBottom) {\n const isInVerticalBounds = flake.y >= rect.top && flake.y <= rect.bottom;\n\n if (isInVerticalBounds) {\n const localY = Math.floor(flake.y - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (flake.x >= rect.left - 5 && flake.x < rect.left + 3) {\n if (!isCorner) {\n accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (!landed && flake.x > rect.right - 3 && flake.x <= rect.right + 5) {\n if (!isCorner) {\n accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (landed) break;\n }\n }\n\n // Top accumulation\n if (flake.x >= rect.left && flake.x <= rect.right) {\n const localX = Math.floor(flake.x - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flake.y >= surfaceY && flake.y < surfaceY + 10 && currentHeight < maxHeight) {\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n if (isBottom) {\n landed = true;\n break;\n }\n }\n\n if (!isBottom) {\n landed = true;\n break;\n }\n }\n }\n }\n\n if (landed || flake.y > canvasHeight + 10 || flake.x < -20 || flake.x > canvasWidth + 20) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.rightSide[i] > 0) acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n }\n }\n};\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM } from './constants';\n\nexport const drawSnowflake = (ctx: CanvasRenderingContext2D, flake: Snowflake) => {\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2);\n ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity})`;\n ctx.fill();\n\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius * 1.5, 0, Math.PI * 2);\n ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity * 0.2})`;\n ctx.fill();\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n fixedCtx: CanvasRenderingContext2D | null,\n elementRects: ElementSurface[]\n) => {\n // Setup styles for both contexts\n const setupCtx = (c: CanvasRenderingContext2D) => {\n c.fillStyle = 'rgba(255, 255, 255, 0.95)';\n c.shadowColor = 'rgba(200, 230, 255, 0.6)';\n c.shadowBlur = 4;\n c.shadowOffsetY = -1;\n };\n setupCtx(ctx);\n if (fixedCtx) setupCtx(fixedCtx);\n\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (const { rect, acc } of elementRects) {\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const useFixed = acc.isFixed && fixedCtx;\n const targetCtx = useFixed ? fixedCtx! : ctx;\n\n // If using fixed context, we need to convert absolute coordinates (rect) back to viewport coordinates\n const dx = useFixed ? -scrollX : 0;\n const dy = useFixed ? -scrollY : 0;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n const borderRadius = acc.borderRadius;\n\n const getCurveOffset = (xPos: number) => {\n if (borderRadius <= 0 || isBottom) return 0;\n let offset = 0;\n if (xPos < borderRadius) {\n const dist = borderRadius - xPos;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (xPos > rect.width - borderRadius) {\n const dist = xPos - (rect.width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n return offset;\n };\n\n targetCtx.beginPath();\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = rect.left + x + dx;\n const py = baseY - height + getCurveOffset(x) + dy;\n if (first) {\n targetCtx.moveTo(px, py);\n first = false;\n } else {\n targetCtx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = rect.left + x + dx;\n const py = baseY - height + getCurveOffset(x) + dy;\n targetCtx.lineTo(px, py);\n }\n\n for (let x = len - 1; x >= 0; x -= step) {\n const px = rect.left + x + dx;\n const py = baseY + getCurveOffset(x) + dy;\n targetCtx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = rect.left + startX + dx;\n const startPy = baseY + getCurveOffset(startX) + dy;\n targetCtx.lineTo(startPx, startPy);\n\n targetCtx.closePath();\n targetCtx.fill();\n }\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n if (fixedCtx) {\n fixedCtx.shadowBlur = 0;\n fixedCtx.shadowOffsetY = 0;\n }\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n fixedCtx: CanvasRenderingContext2D | null,\n elementRects: ElementSurface[]\n) => {\n const setupCtx = (c: CanvasRenderingContext2D) => {\n c.fillStyle = 'rgba(255, 255, 255, 0.95)';\n c.shadowColor = 'rgba(200, 230, 255, 0.6)';\n c.shadowBlur = 3;\n };\n setupCtx(ctx);\n if (fixedCtx) setupCtx(fixedCtx);\n\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (const { rect, acc } of elementRects) {\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftSide.some(h => h > 0.3);\n const hasRightSnow = acc.rightSide.some(h => h > 0.3);\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n const useFixed = acc.isFixed && fixedCtx;\n const targetCtx = useFixed ? fixedCtx! : ctx;\n const dx = useFixed ? -scrollX : 0;\n const dy = useFixed ? -scrollY : 0;\n\n const drawSide = (sideArray: number[], isLeft: boolean) => {\n targetCtx.beginPath();\n const baseX = isLeft ? rect.left : rect.right;\n targetCtx.moveTo(baseX + dx, rect.top + dy);\n\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const py = rect.top + y + dy;\n const px = (isLeft ? baseX - width : baseX + width) + dx;\n const ny = rect.top + nextY + dy;\n const nx = (isLeft ? baseX - nextWidth : baseX + nextWidth) + dx;\n\n targetCtx.lineTo(px, py);\n targetCtx.lineTo(nx, ny);\n }\n targetCtx.lineTo(baseX + dx, rect.bottom + dy);\n targetCtx.closePath();\n targetCtx.fill();\n };\n\n if (hasLeftSnow) drawSide(acc.leftSide, true);\n if (hasRightSnow) drawSide(acc.rightSide, false);\n }\n\n ctx.shadowBlur = 0;\n if (fixedCtx) fixedCtx.shadowBlur = 0;\n};\n"],"mappings":"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","names":["useState","shouldAccumulate","jsx","useState"]}
|
|
1
|
+
{"version":3,"sources":["../src/Snowfall.tsx","../src/SnowfallProvider.tsx","../src/utils/snowfall/constants.ts","../src/utils/snowfall/dom.ts","../src/utils/snowfall/physics.ts","../src/utils/snowfall/draw.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from './utils/snowfall/types';\nimport { getElementRects } from './utils/snowfall/dom';\nimport { createSnowflake, initializeAccumulation, meltAndSmoothAccumulation, updateSnowflakes } from './utils/snowfall/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflake } from './utils/snowfall/draw';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const fixedCanvasRef = useRef<HTMLCanvasElement>(null);\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n const animationIdRef = useRef<number>(0);\n\n useEffect(() => {\n setIsMounted(true);\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n const fixedCanvas = fixedCanvasRef.current;\n if (!canvas || !fixedCanvas) return;\n\n const ctx = canvas.getContext('2d');\n const fixedCtx = fixedCanvas.getContext('2d');\n if (!ctx || !fixedCtx) return;\n\n const resizeCanvas = () => {\n if (canvasRef.current && fixedCanvasRef.current) {\n const newHeight = Math.max(document.documentElement.scrollHeight, window.innerHeight);\n const newWidth = Math.max(document.documentElement.scrollWidth, window.innerWidth);\n\n if (canvasRef.current.height !== newHeight || canvasRef.current.width !== newWidth) {\n canvasRef.current.width = newWidth;\n canvasRef.current.height = newHeight;\n }\n\n // Fixed canvas matches viewport\n if (fixedCanvasRef.current.width !== window.innerWidth || fixedCanvasRef.current.height !== window.innerHeight) {\n fixedCanvasRef.current.width = window.innerWidth;\n fixedCanvasRef.current.height = window.innerHeight;\n }\n }\n };\n resizeCanvas();\n\n const resizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n resizeObserver.observe(document.body);\n\n snowflakesRef.current = [];\n\n const initAccumulationWrapper = () => {\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n };\n initAccumulationWrapper();\n\n setIsVisible(true);\n\n let lastTime = 0;\n\n const animate = (currentTime: number) => {\n if (lastTime === 0) {\n lastTime = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const deltaTime = Math.min(currentTime - lastTime, 50);\n lastTime = currentTime;\n const dt = deltaTime / 16.67;\n\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n fixedCtx.clearRect(0, 0, fixedCanvas.width, fixedCanvas.height);\n\n const snowflakes = snowflakesRef.current;\n const elementRects = getElementRects(accumulationRef.current);\n\n // Physics Update: Melt and Smooth\n meltAndSmoothAccumulation(elementRects, physicsConfigRef.current, dt);\n\n // Physics Update: Snowflakes & Collisions\n updateSnowflakes(snowflakes, elementRects, physicsConfigRef.current, dt, canvas.width, canvas.height);\n\n // Draw Snowflakes\n for (const flake of snowflakes) {\n drawSnowflake(ctx, flake);\n }\n\n // Spawn new snowflakes\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(canvas.width, physicsConfigRef.current, isBackground));\n }\n\n // Draw Accumulation\n drawAccumulations(ctx, fixedCtx, elementRects);\n drawSideAccumulations(ctx, fixedCtx, elementRects);\n\n animationIdRef.current = requestAnimationFrame(animate);\n };\n\n animationIdRef.current = requestAnimationFrame(animate);\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n };\n\n window.addEventListener('resize', handleResize);\n const checkInterval = setInterval(initAccumulationWrapper, 3000);\n\n return () => {\n cancelAnimationFrame(animationIdRef.current);\n window.removeEventListener('resize', handleResize);\n clearInterval(checkInterval);\n resizeObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'absolute',\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n }}\n aria-hidden=\"true\"\n />\n <canvas\n ref={fixedCanvasRef}\n style={{\n position: 'fixed',\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n }}\n aria-hidden=\"true\"\n />\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 1000,\n MELT_SPEED: 0.00001,\n WIND_STRENGTH: 1.5,\n ACCUMULATION: {\n SIDE_RATE: 1,\n TOP_RATE: 5,\n BOTTOM_RATE: 5.0,\n },\n MAX_DEPTH: {\n TOP: 100,\n BOTTOM: 50,\n SIDE: 20,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 1.6,\n }\n};\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children }: { children: ReactNode }) {\n const [isEnabled, setIsEnabled] = useState(true);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\nconst BOTTOM_TAGS = [TAG_HEADER, TAG_FOOTER];\nconst BOTTOM_ROLES = [ROLE_BANNER, ROLE_CONTENTINFO];\n\nconst AUTO_DETECT_TAGS = ['header', 'footer', 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = ['[role=\"banner\"]', '[role=\"contentinfo\"]', '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element): boolean => {\n // Explicit opt-out\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n // Heuristics\n const styles = window.getComputedStyle(el);\n const rect = el.getBoundingClientRect();\n\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = parseFloat(styles.borderWidth) > 0 || styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasBorderRadius = parseFloat(styles.borderRadius) > 0;\n\n return hasBackground || hasBorder || hasBoxShadow || hasBorderRadius;\n};\n\nexport const getAccumulationSurfaces = (): { el: Element; type: SnowfallSurface; isFixed: boolean }[] => {\n const surfaces: { el: Element; type: SnowfallSurface; isFixed: boolean }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n candidates.forEach(el => {\n if (seen.has(el)) return;\n\n const rect = el.getBoundingClientRect();\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) return;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n\n const styles = window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) return;\n\n // Skip really small elements unless manually forced\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) return;\n\n // HEURISTIC: Skip full-page wrappers\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n\n\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole ||\n manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) return;\n\n let isFixed = false;\n let currentEl: Element | null = el;\n while (currentEl && currentEl !== document.body) {\n const style = window.getComputedStyle(currentEl);\n if (style.position === 'fixed' || style.position === 'sticky') {\n isFixed = true;\n break;\n }\n currentEl = currentEl.parentElement;\n }\n\n if (shouldAccumulate(el)) {\n // Determine type: manual override takes precedence\n let type: SnowfallSurface = getElementType(el);\n\n if (manualOverride === VAL_BOTTOM) {\n type = VAL_BOTTOM;\n } else if (manualOverride === VAL_TOP) {\n type = VAL_TOP;\n }\n\n surfaces.push({ el, type, isFixed });\n seen.add(el);\n }\n });\n\n console.log(`[Snowfall] Auto-detection found ${surfaces.length} surfaces`);\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n const rect = el.getBoundingClientRect();\n // Convert viewport coordinates to absolute page coordinates\n const absoluteRect = {\n left: rect.left + window.scrollX,\n right: rect.right + window.scrollX,\n top: rect.top + window.scrollY,\n bottom: rect.bottom + window.scrollY,\n width: rect.width,\n height: rect.height,\n x: rect.x, // Note: these are strictly viewport relative in DOMRect usually, \n // but we just need consistent absolute coords for physics\n y: rect.y,\n toJSON: rect.toJSON\n };\n // We cast because we constructed a compatible object, though strictly DOMRect has readonly properties\n elementRects.push({ el, rect: absoluteRect as unknown as DOMRect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../../SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM } from './constants';\n\nexport const createSnowflake = (\n canvasWidth: number,\n config: PhysicsConfig,\n isBackground: boolean = false\n): Snowflake => {\n if (isBackground) {\n const sizeRatio = Math.random();\n const radius = config.FLAKE_SIZE.MIN * 0.6 + sizeRatio * (config.FLAKE_SIZE.MAX - config.FLAKE_SIZE.MIN) * 0.4;\n return {\n x: Math.random() * canvasWidth,\n y: window.scrollY - 5,\n radius,\n speed: radius * 0.3 + Math.random() * 0.2 + 0.2,\n wind: (Math.random() - 0.5) * (config.WIND_STRENGTH * 0.625),\n opacity: Math.random() * 0.2 + 0.2,\n wobble: Math.random() * Math.PI * 2,\n wobbleSpeed: Math.random() * 0.015 + 0.005,\n sizeRatio,\n isBackground: true\n };\n } else {\n const sizeRatio = Math.random();\n const radius = config.FLAKE_SIZE.MIN + sizeRatio * (config.FLAKE_SIZE.MAX - config.FLAKE_SIZE.MIN);\n return {\n x: Math.random() * canvasWidth,\n y: window.scrollY - 5,\n radius,\n speed: radius * 0.5 + Math.random() * 0.3 + 0.5,\n wind: (Math.random() - 0.5) * config.WIND_STRENGTH,\n opacity: Math.random() * 0.3 + 0.5,\n wobble: Math.random() * Math.PI * 2,\n wobbleSpeed: Math.random() * 0.02 + 0.01,\n sizeRatio,\n isBackground: false\n };\n }\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n const elements = getAccumulationSurfaces();\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type, isFixed }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n existing.isFixed = isFixed;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el); // Potentially slow in loop, strictly necessary?\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n // Smooth maxHeights\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n borderRadius,\n type,\n isFixed,\n });\n });\n};\n\nconst accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig\n) => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n sideArray[y] = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n }\n }\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n canvasWidth: number,\n canvasHeight: number\n) => {\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n flake.wobble += flake.wobbleSpeed * dt;\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n\n let landed = false;\n\n for (const { rect, acc } of elementRects) {\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Side collisions\n if (!landed && acc.maxSideHeight > 0 && !isBottom) {\n const isInVerticalBounds = flake.y >= rect.top && flake.y <= rect.bottom;\n\n if (isInVerticalBounds) {\n const localY = Math.floor(flake.y - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (flake.x >= rect.left - 5 && flake.x < rect.left + 3) {\n if (!isCorner) {\n accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (!landed && flake.x > rect.right - 3 && flake.x <= rect.right + 5) {\n if (!isCorner) {\n accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config);\n landed = true;\n }\n }\n\n if (landed) break;\n }\n }\n\n // Top accumulation\n if (flake.x >= rect.left && flake.x <= rect.right) {\n const localX = Math.floor(flake.x - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flake.y >= surfaceY && flake.y < surfaceY + 10 && currentHeight < maxHeight) {\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n if (isBottom) {\n landed = true;\n break;\n }\n }\n\n if (!isBottom) {\n landed = true;\n break;\n }\n }\n }\n }\n\n if (landed || flake.y > canvasHeight + 10 || flake.x < -20 || flake.x > canvasWidth + 20) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.rightSide[i] > 0) acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n }\n }\n};\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM } from './constants';\n\nexport const drawSnowflake = (ctx: CanvasRenderingContext2D, flake: Snowflake) => {\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2);\n ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity})`;\n ctx.fill();\n\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius * 1.5, 0, Math.PI * 2);\n ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity * 0.2})`;\n ctx.fill();\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n fixedCtx: CanvasRenderingContext2D | null,\n elementRects: ElementSurface[]\n) => {\n // Setup styles for both contexts\n const setupCtx = (c: CanvasRenderingContext2D) => {\n c.fillStyle = 'rgba(255, 255, 255, 0.95)';\n c.shadowColor = 'rgba(200, 230, 255, 0.6)';\n c.shadowBlur = 4;\n c.shadowOffsetY = -1;\n };\n setupCtx(ctx);\n if (fixedCtx) setupCtx(fixedCtx);\n\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (const { rect, acc } of elementRects) {\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const useFixed = acc.isFixed && fixedCtx;\n const targetCtx = useFixed ? fixedCtx! : ctx;\n\n // If using fixed context, we need to convert absolute coordinates (rect) back to viewport coordinates\n const dx = useFixed ? -scrollX : 0;\n const dy = useFixed ? -scrollY : 0;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n const borderRadius = acc.borderRadius;\n\n const getCurveOffset = (xPos: number) => {\n if (borderRadius <= 0 || isBottom) return 0;\n let offset = 0;\n if (xPos < borderRadius) {\n const dist = borderRadius - xPos;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (xPos > rect.width - borderRadius) {\n const dist = xPos - (rect.width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n return offset;\n };\n\n targetCtx.beginPath();\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = rect.left + x + dx;\n const py = baseY - height + getCurveOffset(x) + dy;\n if (first) {\n targetCtx.moveTo(px, py);\n first = false;\n } else {\n targetCtx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = rect.left + x + dx;\n const py = baseY - height + getCurveOffset(x) + dy;\n targetCtx.lineTo(px, py);\n }\n\n for (let x = len - 1; x >= 0; x -= step) {\n const px = rect.left + x + dx;\n const py = baseY + getCurveOffset(x) + dy;\n targetCtx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = rect.left + startX + dx;\n const startPy = baseY + getCurveOffset(startX) + dy;\n targetCtx.lineTo(startPx, startPy);\n\n targetCtx.closePath();\n targetCtx.fill();\n }\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n if (fixedCtx) {\n fixedCtx.shadowBlur = 0;\n fixedCtx.shadowOffsetY = 0;\n }\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n fixedCtx: CanvasRenderingContext2D | null,\n elementRects: ElementSurface[]\n) => {\n const setupCtx = (c: CanvasRenderingContext2D) => {\n c.fillStyle = 'rgba(255, 255, 255, 0.95)';\n c.shadowColor = 'rgba(200, 230, 255, 0.6)';\n c.shadowBlur = 3;\n };\n setupCtx(ctx);\n if (fixedCtx) setupCtx(fixedCtx);\n\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (const { rect, acc } of elementRects) {\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftSide.some(h => h > 0.3);\n const hasRightSnow = acc.rightSide.some(h => h > 0.3);\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n const useFixed = acc.isFixed && fixedCtx;\n const targetCtx = useFixed ? fixedCtx! : ctx;\n const dx = useFixed ? -scrollX : 0;\n const dy = useFixed ? -scrollY : 0;\n\n const drawSide = (sideArray: number[], isLeft: boolean) => {\n targetCtx.beginPath();\n const baseX = isLeft ? rect.left : rect.right;\n targetCtx.moveTo(baseX + dx, rect.top + dy);\n\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const py = rect.top + y + dy;\n const px = (isLeft ? baseX - width : baseX + width) + dx;\n const ny = rect.top + nextY + dy;\n const nx = (isLeft ? baseX - nextWidth : baseX + nextWidth) + dx;\n\n targetCtx.lineTo(px, py);\n targetCtx.lineTo(nx, ny);\n }\n targetCtx.lineTo(baseX + dx, rect.bottom + dy);\n targetCtx.closePath();\n targetCtx.fill();\n };\n\n if (hasLeftSnow) drawSide(acc.leftSide, true);\n if (hasRightSnow) drawSide(acc.rightSide, false);\n }\n\n ctx.shadowBlur = 0;\n if (fixedCtx) fixedCtx.shadowBlur = 0;\n};\n"],"mappings":"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","names":["useState","shouldAccumulate","jsx","useState"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hdcodedev/snowfall",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Realistic snowfall effect for React with physics-based accumulation on surfaces. Features melting, wind, and smart surface detection.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|