@hdcodedev/snowfall 1.0.0

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.
Files changed (51) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +144 -0
  3. package/dist/Snowfall.d.mts +5 -0
  4. package/dist/Snowfall.d.ts +5 -0
  5. package/dist/Snowfall.js +162 -0
  6. package/dist/Snowfall.js.map +1 -0
  7. package/dist/Snowfall.mjs +142 -0
  8. package/dist/Snowfall.mjs.map +1 -0
  9. package/dist/SnowfallProvider.d.mts +32 -0
  10. package/dist/SnowfallProvider.d.ts +32 -0
  11. package/dist/SnowfallProvider.js +89 -0
  12. package/dist/SnowfallProvider.js.map +1 -0
  13. package/dist/SnowfallProvider.mjs +63 -0
  14. package/dist/SnowfallProvider.mjs.map +1 -0
  15. package/dist/index.d.mts +4 -0
  16. package/dist/index.d.ts +4 -0
  17. package/dist/index.js +47 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/index.mjs +10 -0
  20. package/dist/index.mjs.map +1 -0
  21. package/dist/utils/snowfall/constants.d.mts +10 -0
  22. package/dist/utils/snowfall/constants.d.ts +10 -0
  23. package/dist/utils/snowfall/constants.js +50 -0
  24. package/dist/utils/snowfall/constants.js.map +1 -0
  25. package/dist/utils/snowfall/constants.mjs +19 -0
  26. package/dist/utils/snowfall/constants.mjs.map +1 -0
  27. package/dist/utils/snowfall/dom.d.mts +11 -0
  28. package/dist/utils/snowfall/dom.d.ts +11 -0
  29. package/dist/utils/snowfall/dom.js +130 -0
  30. package/dist/utils/snowfall/dom.js.map +1 -0
  31. package/dist/utils/snowfall/dom.mjs +113 -0
  32. package/dist/utils/snowfall/dom.mjs.map +1 -0
  33. package/dist/utils/snowfall/draw.d.mts +7 -0
  34. package/dist/utils/snowfall/draw.d.ts +7 -0
  35. package/dist/utils/snowfall/draw.js +160 -0
  36. package/dist/utils/snowfall/draw.js.map +1 -0
  37. package/dist/utils/snowfall/draw.mjs +134 -0
  38. package/dist/utils/snowfall/draw.mjs.map +1 -0
  39. package/dist/utils/snowfall/physics.d.mts +11 -0
  40. package/dist/utils/snowfall/physics.d.ts +11 -0
  41. package/dist/utils/snowfall/physics.js +233 -0
  42. package/dist/utils/snowfall/physics.js.map +1 -0
  43. package/dist/utils/snowfall/physics.mjs +206 -0
  44. package/dist/utils/snowfall/physics.mjs.map +1 -0
  45. package/dist/utils/snowfall/types.d.mts +28 -0
  46. package/dist/utils/snowfall/types.d.ts +28 -0
  47. package/dist/utils/snowfall/types.js +17 -0
  48. package/dist/utils/snowfall/types.js.map +1 -0
  49. package/dist/utils/snowfall/types.mjs +1 -0
  50. package/dist/utils/snowfall/types.mjs.map +1 -0
  51. package/package.json +57 -0
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var dom_exports = {};
20
+ __export(dom_exports, {
21
+ getAccumulationSurfaces: () => getAccumulationSurfaces,
22
+ getElementRects: () => getElementRects,
23
+ getElementType: () => getElementType
24
+ });
25
+ module.exports = __toCommonJS(dom_exports);
26
+ var import_constants = require("./constants");
27
+ const BOTTOM_TAGS = [import_constants.TAG_HEADER, import_constants.TAG_FOOTER];
28
+ const BOTTOM_ROLES = [import_constants.ROLE_BANNER, import_constants.ROLE_CONTENTINFO];
29
+ const AUTO_DETECT_TAGS = ["header", "footer", "article", "section", "aside", "nav"];
30
+ const AUTO_DETECT_ROLES = ['[role="banner"]', '[role="contentinfo"]', '[role="main"]'];
31
+ const AUTO_DETECT_CLASSES = [
32
+ ".card",
33
+ '[class*="card"]',
34
+ '[class*="Card"]',
35
+ '[class*="bg-"]',
36
+ '[class*="shadow-"]',
37
+ '[class*="rounded-"]'
38
+ ];
39
+ const getElementType = (el) => {
40
+ const tagName = el.tagName.toLowerCase();
41
+ if (BOTTOM_TAGS.includes(tagName)) return import_constants.VAL_BOTTOM;
42
+ const role = el.getAttribute("role");
43
+ if (role && BOTTOM_ROLES.includes(role)) return import_constants.VAL_BOTTOM;
44
+ return import_constants.VAL_TOP;
45
+ };
46
+ const shouldAccumulate = (el) => {
47
+ if (el.getAttribute(import_constants.ATTR_SNOWFALL) === import_constants.VAL_IGNORE) return false;
48
+ if (el.hasAttribute(import_constants.ATTR_SNOWFALL)) return true;
49
+ const styles = window.getComputedStyle(el);
50
+ const rect = el.getBoundingClientRect();
51
+ const isVisible = styles.display !== "none" && styles.visibility !== "hidden" && parseFloat(styles.opacity) > 0.1;
52
+ if (!isVisible) return false;
53
+ const bgColor = styles.backgroundColor;
54
+ const hasBackground = bgColor !== "rgba(0, 0, 0, 0)" && bgColor !== "transparent";
55
+ const hasBorder = parseFloat(styles.borderWidth) > 0 || styles.borderStyle !== "none";
56
+ const hasBoxShadow = styles.boxShadow !== "none";
57
+ const hasBorderRadius = parseFloat(styles.borderRadius) > 0;
58
+ return hasBackground || hasBorder || hasBoxShadow || hasBorderRadius;
59
+ };
60
+ const getAccumulationSurfaces = () => {
61
+ const surfaces = [];
62
+ const seen = /* @__PURE__ */ new Set();
63
+ const candidates = document.querySelectorAll(
64
+ [
65
+ `[${import_constants.ATTR_SNOWFALL}]`,
66
+ ...AUTO_DETECT_TAGS,
67
+ ...AUTO_DETECT_ROLES,
68
+ ...AUTO_DETECT_CLASSES
69
+ ].join(", ")
70
+ );
71
+ candidates.forEach((el) => {
72
+ if (seen.has(el)) return;
73
+ const rect = el.getBoundingClientRect();
74
+ const manualOverride = el.getAttribute(import_constants.ATTR_SNOWFALL);
75
+ if (manualOverride === import_constants.VAL_IGNORE) return;
76
+ const isManuallyIncluded = manualOverride !== null;
77
+ const styles = window.getComputedStyle(el);
78
+ const isVisible = styles.display !== "none" && styles.visibility !== "hidden" && parseFloat(styles.opacity) > 0.1;
79
+ if (!isVisible && !isManuallyIncluded) return;
80
+ const hasSize = rect.width >= 100 && rect.height >= 50;
81
+ if (!hasSize && !isManuallyIncluded) return;
82
+ const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;
83
+ const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());
84
+ const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute("role") || "");
85
+ const isBottomSurface = isBottomTag || isBottomRole || manualOverride === import_constants.VAL_BOTTOM;
86
+ if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) return;
87
+ const isFixed = styles.position === "fixed" || styles.position === "sticky";
88
+ if (shouldAccumulate(el)) {
89
+ let type = getElementType(el);
90
+ if (manualOverride === import_constants.VAL_BOTTOM) {
91
+ type = import_constants.VAL_BOTTOM;
92
+ } else if (manualOverride === import_constants.VAL_TOP) {
93
+ type = import_constants.VAL_TOP;
94
+ }
95
+ surfaces.push({ el, type, isFixed });
96
+ seen.add(el);
97
+ }
98
+ });
99
+ console.log(`[Snowfall] Auto-detection found ${surfaces.length} surfaces`);
100
+ return surfaces;
101
+ };
102
+ const getElementRects = (accumulationMap) => {
103
+ const elementRects = [];
104
+ for (const [el, acc] of accumulationMap.entries()) {
105
+ if (!el.isConnected) continue;
106
+ const rect = el.getBoundingClientRect();
107
+ const absoluteRect = {
108
+ left: rect.left + window.scrollX,
109
+ right: rect.right + window.scrollX,
110
+ top: rect.top + window.scrollY,
111
+ bottom: rect.bottom + window.scrollY,
112
+ width: rect.width,
113
+ height: rect.height,
114
+ x: rect.x,
115
+ // Note: these are strictly viewport relative in DOMRect usually,
116
+ // but we just need consistent absolute coords for physics
117
+ y: rect.y,
118
+ toJSON: rect.toJSON
119
+ };
120
+ elementRects.push({ el, rect: absoluteRect, acc });
121
+ }
122
+ return elementRects;
123
+ };
124
+ // Annotate the CommonJS export names for ESM import in node:
125
+ 0 && (module.exports = {
126
+ getAccumulationSurfaces,
127
+ getElementRects,
128
+ getElementType
129
+ });
130
+ //# sourceMappingURL=dom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/snowfall/dom.ts"],"sourcesContent":["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 const isFixed = styles.position === 'fixed' || styles.position === 'sticky';\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"],"mappings":"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","names":[]}
@@ -0,0 +1,113 @@
1
+ import {
2
+ ATTR_SNOWFALL,
3
+ VAL_IGNORE,
4
+ VAL_TOP,
5
+ VAL_BOTTOM,
6
+ TAG_HEADER,
7
+ TAG_FOOTER,
8
+ ROLE_BANNER,
9
+ ROLE_CONTENTINFO
10
+ } from "./constants";
11
+ const BOTTOM_TAGS = [TAG_HEADER, TAG_FOOTER];
12
+ const BOTTOM_ROLES = [ROLE_BANNER, ROLE_CONTENTINFO];
13
+ const AUTO_DETECT_TAGS = ["header", "footer", "article", "section", "aside", "nav"];
14
+ const AUTO_DETECT_ROLES = ['[role="banner"]', '[role="contentinfo"]', '[role="main"]'];
15
+ const AUTO_DETECT_CLASSES = [
16
+ ".card",
17
+ '[class*="card"]',
18
+ '[class*="Card"]',
19
+ '[class*="bg-"]',
20
+ '[class*="shadow-"]',
21
+ '[class*="rounded-"]'
22
+ ];
23
+ const getElementType = (el) => {
24
+ const tagName = el.tagName.toLowerCase();
25
+ if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;
26
+ const role = el.getAttribute("role");
27
+ if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;
28
+ return VAL_TOP;
29
+ };
30
+ const shouldAccumulate = (el) => {
31
+ if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;
32
+ if (el.hasAttribute(ATTR_SNOWFALL)) return true;
33
+ const styles = window.getComputedStyle(el);
34
+ const rect = el.getBoundingClientRect();
35
+ const isVisible = styles.display !== "none" && styles.visibility !== "hidden" && parseFloat(styles.opacity) > 0.1;
36
+ if (!isVisible) return false;
37
+ const bgColor = styles.backgroundColor;
38
+ const hasBackground = bgColor !== "rgba(0, 0, 0, 0)" && bgColor !== "transparent";
39
+ const hasBorder = parseFloat(styles.borderWidth) > 0 || styles.borderStyle !== "none";
40
+ const hasBoxShadow = styles.boxShadow !== "none";
41
+ const hasBorderRadius = parseFloat(styles.borderRadius) > 0;
42
+ return hasBackground || hasBorder || hasBoxShadow || hasBorderRadius;
43
+ };
44
+ const getAccumulationSurfaces = () => {
45
+ const surfaces = [];
46
+ const seen = /* @__PURE__ */ new Set();
47
+ const candidates = document.querySelectorAll(
48
+ [
49
+ `[${ATTR_SNOWFALL}]`,
50
+ ...AUTO_DETECT_TAGS,
51
+ ...AUTO_DETECT_ROLES,
52
+ ...AUTO_DETECT_CLASSES
53
+ ].join(", ")
54
+ );
55
+ candidates.forEach((el) => {
56
+ if (seen.has(el)) return;
57
+ const rect = el.getBoundingClientRect();
58
+ const manualOverride = el.getAttribute(ATTR_SNOWFALL);
59
+ if (manualOverride === VAL_IGNORE) return;
60
+ const isManuallyIncluded = manualOverride !== null;
61
+ const styles = window.getComputedStyle(el);
62
+ const isVisible = styles.display !== "none" && styles.visibility !== "hidden" && parseFloat(styles.opacity) > 0.1;
63
+ if (!isVisible && !isManuallyIncluded) return;
64
+ const hasSize = rect.width >= 100 && rect.height >= 50;
65
+ if (!hasSize && !isManuallyIncluded) return;
66
+ const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;
67
+ const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());
68
+ const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute("role") || "");
69
+ const isBottomSurface = isBottomTag || isBottomRole || manualOverride === VAL_BOTTOM;
70
+ if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) return;
71
+ const isFixed = styles.position === "fixed" || styles.position === "sticky";
72
+ if (shouldAccumulate(el)) {
73
+ let type = getElementType(el);
74
+ if (manualOverride === VAL_BOTTOM) {
75
+ type = VAL_BOTTOM;
76
+ } else if (manualOverride === VAL_TOP) {
77
+ type = VAL_TOP;
78
+ }
79
+ surfaces.push({ el, type, isFixed });
80
+ seen.add(el);
81
+ }
82
+ });
83
+ console.log(`[Snowfall] Auto-detection found ${surfaces.length} surfaces`);
84
+ return surfaces;
85
+ };
86
+ const getElementRects = (accumulationMap) => {
87
+ const elementRects = [];
88
+ for (const [el, acc] of accumulationMap.entries()) {
89
+ if (!el.isConnected) continue;
90
+ const rect = el.getBoundingClientRect();
91
+ const absoluteRect = {
92
+ left: rect.left + window.scrollX,
93
+ right: rect.right + window.scrollX,
94
+ top: rect.top + window.scrollY,
95
+ bottom: rect.bottom + window.scrollY,
96
+ width: rect.width,
97
+ height: rect.height,
98
+ x: rect.x,
99
+ // Note: these are strictly viewport relative in DOMRect usually,
100
+ // but we just need consistent absolute coords for physics
101
+ y: rect.y,
102
+ toJSON: rect.toJSON
103
+ };
104
+ elementRects.push({ el, rect: absoluteRect, acc });
105
+ }
106
+ return elementRects;
107
+ };
108
+ export {
109
+ getAccumulationSurfaces,
110
+ getElementRects,
111
+ getElementType
112
+ };
113
+ //# sourceMappingURL=dom.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/snowfall/dom.ts"],"sourcesContent":["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 const isFixed = styles.position === 'fixed' || styles.position === 'sticky';\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"],"mappings":"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","names":[]}
@@ -0,0 +1,7 @@
1
+ import { Snowflake, ElementSurface } from './types.mjs';
2
+
3
+ declare const drawSnowflake: (ctx: CanvasRenderingContext2D, flake: Snowflake) => void;
4
+ declare const drawAccumulations: (ctx: CanvasRenderingContext2D, fixedCtx: CanvasRenderingContext2D | null, elementRects: ElementSurface[]) => void;
5
+ declare const drawSideAccumulations: (ctx: CanvasRenderingContext2D, fixedCtx: CanvasRenderingContext2D | null, elementRects: ElementSurface[]) => void;
6
+
7
+ export { drawAccumulations, drawSideAccumulations, drawSnowflake };
@@ -0,0 +1,7 @@
1
+ import { Snowflake, ElementSurface } from './types.js';
2
+
3
+ declare const drawSnowflake: (ctx: CanvasRenderingContext2D, flake: Snowflake) => void;
4
+ declare const drawAccumulations: (ctx: CanvasRenderingContext2D, fixedCtx: CanvasRenderingContext2D | null, elementRects: ElementSurface[]) => void;
5
+ declare const drawSideAccumulations: (ctx: CanvasRenderingContext2D, fixedCtx: CanvasRenderingContext2D | null, elementRects: ElementSurface[]) => void;
6
+
7
+ export { drawAccumulations, drawSideAccumulations, drawSnowflake };
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var draw_exports = {};
20
+ __export(draw_exports, {
21
+ drawAccumulations: () => drawAccumulations,
22
+ drawSideAccumulations: () => drawSideAccumulations,
23
+ drawSnowflake: () => drawSnowflake
24
+ });
25
+ module.exports = __toCommonJS(draw_exports);
26
+ var import_constants = require("./constants");
27
+ const drawSnowflake = (ctx, flake) => {
28
+ ctx.beginPath();
29
+ ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2);
30
+ ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity})`;
31
+ ctx.fill();
32
+ ctx.beginPath();
33
+ ctx.arc(flake.x, flake.y, flake.radius * 1.5, 0, Math.PI * 2);
34
+ ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity * 0.2})`;
35
+ ctx.fill();
36
+ };
37
+ const drawAccumulations = (ctx, fixedCtx, elementRects) => {
38
+ const setupCtx = (c) => {
39
+ c.fillStyle = "rgba(255, 255, 255, 0.95)";
40
+ c.shadowColor = "rgba(200, 230, 255, 0.6)";
41
+ c.shadowBlur = 4;
42
+ c.shadowOffsetY = -1;
43
+ };
44
+ setupCtx(ctx);
45
+ if (fixedCtx) setupCtx(fixedCtx);
46
+ const scrollX = window.scrollX;
47
+ const scrollY = window.scrollY;
48
+ for (const { rect, acc } of elementRects) {
49
+ if (!acc.heights.some((h) => h > 0.1)) continue;
50
+ const useFixed = acc.isFixed && fixedCtx;
51
+ const targetCtx = useFixed ? fixedCtx : ctx;
52
+ const dx = useFixed ? -scrollX : 0;
53
+ const dy = useFixed ? -scrollY : 0;
54
+ const isBottom = acc.type === import_constants.VAL_BOTTOM;
55
+ const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;
56
+ const borderRadius = acc.borderRadius;
57
+ const getCurveOffset = (xPos) => {
58
+ if (borderRadius <= 0 || isBottom) return 0;
59
+ let offset = 0;
60
+ if (xPos < borderRadius) {
61
+ const dist = borderRadius - xPos;
62
+ offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));
63
+ } else if (xPos > rect.width - borderRadius) {
64
+ const dist = xPos - (rect.width - borderRadius);
65
+ offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));
66
+ }
67
+ return offset;
68
+ };
69
+ targetCtx.beginPath();
70
+ let first = true;
71
+ const step = 2;
72
+ const len = acc.heights.length;
73
+ for (let x = 0; x < len; x += step) {
74
+ const height = acc.heights[x] || 0;
75
+ const px = rect.left + x + dx;
76
+ const py = baseY - height + getCurveOffset(x) + dy;
77
+ if (first) {
78
+ targetCtx.moveTo(px, py);
79
+ first = false;
80
+ } else {
81
+ targetCtx.lineTo(px, py);
82
+ }
83
+ }
84
+ if ((len - 1) % step !== 0) {
85
+ const x = len - 1;
86
+ const height = acc.heights[x] || 0;
87
+ const px = rect.left + x + dx;
88
+ const py = baseY - height + getCurveOffset(x) + dy;
89
+ targetCtx.lineTo(px, py);
90
+ }
91
+ for (let x = len - 1; x >= 0; x -= step) {
92
+ const px = rect.left + x + dx;
93
+ const py = baseY + getCurveOffset(x) + dy;
94
+ targetCtx.lineTo(px, py);
95
+ }
96
+ const startX = 0;
97
+ const startPx = rect.left + startX + dx;
98
+ const startPy = baseY + getCurveOffset(startX) + dy;
99
+ targetCtx.lineTo(startPx, startPy);
100
+ targetCtx.closePath();
101
+ targetCtx.fill();
102
+ }
103
+ ctx.shadowBlur = 0;
104
+ ctx.shadowOffsetY = 0;
105
+ if (fixedCtx) {
106
+ fixedCtx.shadowBlur = 0;
107
+ fixedCtx.shadowOffsetY = 0;
108
+ }
109
+ };
110
+ const drawSideAccumulations = (ctx, fixedCtx, elementRects) => {
111
+ const setupCtx = (c) => {
112
+ c.fillStyle = "rgba(255, 255, 255, 0.95)";
113
+ c.shadowColor = "rgba(200, 230, 255, 0.6)";
114
+ c.shadowBlur = 3;
115
+ };
116
+ setupCtx(ctx);
117
+ if (fixedCtx) setupCtx(fixedCtx);
118
+ const scrollX = window.scrollX;
119
+ const scrollY = window.scrollY;
120
+ for (const { rect, acc } of elementRects) {
121
+ if (acc.maxSideHeight === 0) continue;
122
+ const hasLeftSnow = acc.leftSide.some((h) => h > 0.3);
123
+ const hasRightSnow = acc.rightSide.some((h) => h > 0.3);
124
+ if (!hasLeftSnow && !hasRightSnow) continue;
125
+ const useFixed = acc.isFixed && fixedCtx;
126
+ const targetCtx = useFixed ? fixedCtx : ctx;
127
+ const dx = useFixed ? -scrollX : 0;
128
+ const dy = useFixed ? -scrollY : 0;
129
+ const drawSide = (sideArray, isLeft) => {
130
+ targetCtx.beginPath();
131
+ const baseX = isLeft ? rect.left : rect.right;
132
+ targetCtx.moveTo(baseX + dx, rect.top + dy);
133
+ for (let y = 0; y < sideArray.length; y += 2) {
134
+ const width = sideArray[y] || 0;
135
+ const nextY = Math.min(y + 2, sideArray.length - 1);
136
+ const nextWidth = sideArray[nextY] || 0;
137
+ const py = rect.top + y + dy;
138
+ const px = (isLeft ? baseX - width : baseX + width) + dx;
139
+ const ny = rect.top + nextY + dy;
140
+ const nx = (isLeft ? baseX - nextWidth : baseX + nextWidth) + dx;
141
+ targetCtx.lineTo(px, py);
142
+ targetCtx.lineTo(nx, ny);
143
+ }
144
+ targetCtx.lineTo(baseX + dx, rect.bottom + dy);
145
+ targetCtx.closePath();
146
+ targetCtx.fill();
147
+ };
148
+ if (hasLeftSnow) drawSide(acc.leftSide, true);
149
+ if (hasRightSnow) drawSide(acc.rightSide, false);
150
+ }
151
+ ctx.shadowBlur = 0;
152
+ if (fixedCtx) fixedCtx.shadowBlur = 0;
153
+ };
154
+ // Annotate the CommonJS export names for ESM import in node:
155
+ 0 && (module.exports = {
156
+ drawAccumulations,
157
+ drawSideAccumulations,
158
+ drawSnowflake
159
+ });
160
+ //# sourceMappingURL=draw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/snowfall/draw.ts"],"sourcesContent":["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":[]}
@@ -0,0 +1,134 @@
1
+ import { VAL_BOTTOM } from "./constants";
2
+ const drawSnowflake = (ctx, flake) => {
3
+ ctx.beginPath();
4
+ ctx.arc(flake.x, flake.y, flake.radius, 0, Math.PI * 2);
5
+ ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity})`;
6
+ ctx.fill();
7
+ ctx.beginPath();
8
+ ctx.arc(flake.x, flake.y, flake.radius * 1.5, 0, Math.PI * 2);
9
+ ctx.fillStyle = `rgba(255, 255, 255, ${flake.opacity * 0.2})`;
10
+ ctx.fill();
11
+ };
12
+ const drawAccumulations = (ctx, fixedCtx, elementRects) => {
13
+ const setupCtx = (c) => {
14
+ c.fillStyle = "rgba(255, 255, 255, 0.95)";
15
+ c.shadowColor = "rgba(200, 230, 255, 0.6)";
16
+ c.shadowBlur = 4;
17
+ c.shadowOffsetY = -1;
18
+ };
19
+ setupCtx(ctx);
20
+ if (fixedCtx) setupCtx(fixedCtx);
21
+ const scrollX = window.scrollX;
22
+ const scrollY = window.scrollY;
23
+ for (const { rect, acc } of elementRects) {
24
+ if (!acc.heights.some((h) => h > 0.1)) continue;
25
+ const useFixed = acc.isFixed && fixedCtx;
26
+ const targetCtx = useFixed ? fixedCtx : ctx;
27
+ const dx = useFixed ? -scrollX : 0;
28
+ const dy = useFixed ? -scrollY : 0;
29
+ const isBottom = acc.type === VAL_BOTTOM;
30
+ const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;
31
+ const borderRadius = acc.borderRadius;
32
+ const getCurveOffset = (xPos) => {
33
+ if (borderRadius <= 0 || isBottom) return 0;
34
+ let offset = 0;
35
+ if (xPos < borderRadius) {
36
+ const dist = borderRadius - xPos;
37
+ offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));
38
+ } else if (xPos > rect.width - borderRadius) {
39
+ const dist = xPos - (rect.width - borderRadius);
40
+ offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));
41
+ }
42
+ return offset;
43
+ };
44
+ targetCtx.beginPath();
45
+ let first = true;
46
+ const step = 2;
47
+ const len = acc.heights.length;
48
+ for (let x = 0; x < len; x += step) {
49
+ const height = acc.heights[x] || 0;
50
+ const px = rect.left + x + dx;
51
+ const py = baseY - height + getCurveOffset(x) + dy;
52
+ if (first) {
53
+ targetCtx.moveTo(px, py);
54
+ first = false;
55
+ } else {
56
+ targetCtx.lineTo(px, py);
57
+ }
58
+ }
59
+ if ((len - 1) % step !== 0) {
60
+ const x = len - 1;
61
+ const height = acc.heights[x] || 0;
62
+ const px = rect.left + x + dx;
63
+ const py = baseY - height + getCurveOffset(x) + dy;
64
+ targetCtx.lineTo(px, py);
65
+ }
66
+ for (let x = len - 1; x >= 0; x -= step) {
67
+ const px = rect.left + x + dx;
68
+ const py = baseY + getCurveOffset(x) + dy;
69
+ targetCtx.lineTo(px, py);
70
+ }
71
+ const startX = 0;
72
+ const startPx = rect.left + startX + dx;
73
+ const startPy = baseY + getCurveOffset(startX) + dy;
74
+ targetCtx.lineTo(startPx, startPy);
75
+ targetCtx.closePath();
76
+ targetCtx.fill();
77
+ }
78
+ ctx.shadowBlur = 0;
79
+ ctx.shadowOffsetY = 0;
80
+ if (fixedCtx) {
81
+ fixedCtx.shadowBlur = 0;
82
+ fixedCtx.shadowOffsetY = 0;
83
+ }
84
+ };
85
+ const drawSideAccumulations = (ctx, fixedCtx, elementRects) => {
86
+ const setupCtx = (c) => {
87
+ c.fillStyle = "rgba(255, 255, 255, 0.95)";
88
+ c.shadowColor = "rgba(200, 230, 255, 0.6)";
89
+ c.shadowBlur = 3;
90
+ };
91
+ setupCtx(ctx);
92
+ if (fixedCtx) setupCtx(fixedCtx);
93
+ const scrollX = window.scrollX;
94
+ const scrollY = window.scrollY;
95
+ for (const { rect, acc } of elementRects) {
96
+ if (acc.maxSideHeight === 0) continue;
97
+ const hasLeftSnow = acc.leftSide.some((h) => h > 0.3);
98
+ const hasRightSnow = acc.rightSide.some((h) => h > 0.3);
99
+ if (!hasLeftSnow && !hasRightSnow) continue;
100
+ const useFixed = acc.isFixed && fixedCtx;
101
+ const targetCtx = useFixed ? fixedCtx : ctx;
102
+ const dx = useFixed ? -scrollX : 0;
103
+ const dy = useFixed ? -scrollY : 0;
104
+ const drawSide = (sideArray, isLeft) => {
105
+ targetCtx.beginPath();
106
+ const baseX = isLeft ? rect.left : rect.right;
107
+ targetCtx.moveTo(baseX + dx, rect.top + dy);
108
+ for (let y = 0; y < sideArray.length; y += 2) {
109
+ const width = sideArray[y] || 0;
110
+ const nextY = Math.min(y + 2, sideArray.length - 1);
111
+ const nextWidth = sideArray[nextY] || 0;
112
+ const py = rect.top + y + dy;
113
+ const px = (isLeft ? baseX - width : baseX + width) + dx;
114
+ const ny = rect.top + nextY + dy;
115
+ const nx = (isLeft ? baseX - nextWidth : baseX + nextWidth) + dx;
116
+ targetCtx.lineTo(px, py);
117
+ targetCtx.lineTo(nx, ny);
118
+ }
119
+ targetCtx.lineTo(baseX + dx, rect.bottom + dy);
120
+ targetCtx.closePath();
121
+ targetCtx.fill();
122
+ };
123
+ if (hasLeftSnow) drawSide(acc.leftSide, true);
124
+ if (hasRightSnow) drawSide(acc.rightSide, false);
125
+ }
126
+ ctx.shadowBlur = 0;
127
+ if (fixedCtx) fixedCtx.shadowBlur = 0;
128
+ };
129
+ export {
130
+ drawAccumulations,
131
+ drawSideAccumulations,
132
+ drawSnowflake
133
+ };
134
+ //# sourceMappingURL=draw.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/utils/snowfall/draw.ts"],"sourcesContent":["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":[]}
@@ -0,0 +1,11 @@
1
+ import { PhysicsConfig } from '../../SnowfallProvider.mjs';
2
+ import { Snowflake, SnowAccumulation, ElementSurface } from './types.mjs';
3
+ import 'react/jsx-runtime';
4
+ import 'react';
5
+
6
+ declare const createSnowflake: (canvasWidth: number, config: PhysicsConfig, isBackground?: boolean) => Snowflake;
7
+ declare const initializeAccumulation: (accumulationMap: Map<Element, SnowAccumulation>, config: PhysicsConfig) => void;
8
+ declare const updateSnowflakes: (snowflakes: Snowflake[], elementRects: ElementSurface[], config: PhysicsConfig, dt: number, canvasWidth: number, canvasHeight: number) => void;
9
+ declare const meltAndSmoothAccumulation: (elementRects: ElementSurface[], config: PhysicsConfig, dt: number) => void;
10
+
11
+ export { createSnowflake, initializeAccumulation, meltAndSmoothAccumulation, updateSnowflakes };