@designbasekorea/ui-wc 0.5.6 → 0.6.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 (202) hide show
  1. package/dist/chunks/lottie-0e23d630.js +1 -0
  2. package/dist/esm/components/animation-background/canvas-controller.js +1 -121
  3. package/dist/esm/components/animation-background/controls.js +1 -180
  4. package/dist/esm/components/animation-background/graphite-controller.js +1 -196
  5. package/dist/esm/components/animation-background/graphite-presets.js +1 -42
  6. package/dist/esm/components/animation-background/graphite-shader.js +1 -111
  7. package/dist/esm/components/animation-background/layers.js +1 -369
  8. package/dist/esm/components/animation-background/lumina-presets.js +1 -53
  9. package/dist/esm/components/animation-background/lumina-shaders.js +1 -181
  10. package/dist/esm/components/animation-background/lumina-utils.js +1 -60
  11. package/dist/esm/components/animation-background/particle-defaults.js +1 -36
  12. package/dist/esm/components/animation-background/renderers.js +1 -167
  13. package/dist/esm/components/animation-background/shader-controller.js +1 -154
  14. package/dist/esm/components/color-picker-utils.js +1 -134
  15. package/dist/esm/components/date-picker-utils.js +1 -140
  16. package/dist/esm/components/db-accordion.js +1 -304
  17. package/dist/esm/components/db-ad-banner.js +1 -455
  18. package/dist/esm/components/db-ai-chat-list.d.ts +43 -0
  19. package/dist/esm/components/db-ai-chat-list.js +1 -0
  20. package/dist/esm/components/db-ai-chat.d.ts +159 -0
  21. package/dist/esm/components/db-ai-chat.js +1 -0
  22. package/dist/esm/components/db-alert.js +1 -149
  23. package/dist/esm/components/db-animation-background.js +1 -486
  24. package/dist/esm/components/db-animation-text.js +1 -601
  25. package/dist/esm/components/db-app-badge.js +1 -88
  26. package/dist/esm/components/db-audio-player.js +1 -752
  27. package/dist/esm/components/db-avatar.js +1 -188
  28. package/dist/esm/components/db-backdrop.js +1 -139
  29. package/dist/esm/components/db-badge.js +1 -91
  30. package/dist/esm/components/db-banner.js +1 -188
  31. package/dist/esm/components/db-bottom-navigation.js +1 -146
  32. package/dist/esm/components/db-bottom-sheet.js +1 -481
  33. package/dist/esm/components/db-breadcrumbs.js +1 -329
  34. package/dist/esm/components/db-button.js +1 -316
  35. package/dist/esm/components/db-calendar.js +1 -675
  36. package/dist/esm/components/db-card.js +1 -265
  37. package/dist/esm/components/db-carousel.js +1 -264
  38. package/dist/esm/components/db-chat-composer.d.ts +134 -0
  39. package/dist/esm/components/db-chat-composer.js +1 -0
  40. package/dist/esm/components/db-chat-list.d.ts +47 -0
  41. package/dist/esm/components/db-chat-list.js +1 -0
  42. package/dist/esm/components/db-chat-message.d.ts +39 -0
  43. package/dist/esm/components/db-chat-message.js +1 -0
  44. package/dist/esm/components/db-chat-orb.d.ts +99 -0
  45. package/dist/esm/components/db-chat-orb.js +1 -0
  46. package/dist/esm/components/db-chat-room.d.ts +71 -0
  47. package/dist/esm/components/db-chat-room.js +1 -0
  48. package/dist/esm/components/db-chat-suggestions.d.ts +44 -0
  49. package/dist/esm/components/db-chat-suggestions.js +1 -0
  50. package/dist/esm/components/db-chat-typing.d.ts +22 -0
  51. package/dist/esm/components/db-chat-typing.js +1 -0
  52. package/dist/esm/components/db-chat-welcome.d.ts +32 -0
  53. package/dist/esm/components/db-chat-welcome.js +1 -0
  54. package/dist/esm/components/db-chat-widget.d.ts +146 -0
  55. package/dist/esm/components/db-chat-widget.js +1 -0
  56. package/dist/esm/components/db-checkbox.js +1 -202
  57. package/dist/esm/components/db-chip.js +1 -104
  58. package/dist/esm/components/db-code-block.js +1 -304
  59. package/dist/esm/components/db-color-picker.js +1 -746
  60. package/dist/esm/components/db-confirm.js +1 -191
  61. package/dist/esm/components/db-container.js +1 -39
  62. package/dist/esm/components/db-context-menu.js +1 -265
  63. package/dist/esm/components/db-countdown.js +1 -146
  64. package/dist/esm/components/db-cursor-follower.js +1 -173
  65. package/dist/esm/components/db-date-picker.js +1 -837
  66. package/dist/esm/components/db-divider.js +1 -66
  67. package/dist/esm/components/db-download-progress.d.ts +89 -0
  68. package/dist/esm/components/db-download-progress.js +1 -0
  69. package/dist/esm/components/db-drawer.js +1 -245
  70. package/dist/esm/components/db-dropdown.js +1 -493
  71. package/dist/esm/components/db-dropzone.js +1 -266
  72. package/dist/esm/components/db-email-verify.d.ts +62 -0
  73. package/dist/esm/components/db-email-verify.js +1 -0
  74. package/dist/esm/components/db-empty-state.js +1 -97
  75. package/dist/esm/components/db-file-uploader.js +1 -256
  76. package/dist/esm/components/db-floating-action-button.js +1 -139
  77. package/dist/esm/components/db-footer.js +1 -385
  78. package/dist/esm/components/db-form.js +1 -566
  79. package/dist/esm/components/db-gantt.js +1 -882
  80. package/dist/esm/components/db-gradient.js +1 -133
  81. package/dist/esm/components/db-grid-background.js +1 -909
  82. package/dist/esm/components/db-grid.js +1 -73
  83. package/dist/esm/components/db-icons.d.ts +3 -0
  84. package/dist/esm/components/db-icons.js +1 -82
  85. package/dist/esm/components/db-image-list.js +1 -214
  86. package/dist/esm/components/db-image-placeholder.js +1 -96
  87. package/dist/esm/components/db-image.js +1 -289
  88. package/dist/esm/components/db-indicator.js +1 -517
  89. package/dist/esm/components/db-input.js +1 -420
  90. package/dist/esm/components/db-kanban.js +1 -538
  91. package/dist/esm/components/db-label.js +1 -66
  92. package/dist/esm/components/db-lightbox.js +1 -372
  93. package/dist/esm/components/db-list.js +1 -340
  94. package/dist/esm/components/db-logo-svg.js +1 -72
  95. package/dist/esm/components/db-logo.js +1 -106
  96. package/dist/esm/components/db-lottie.js +1 -109
  97. package/dist/esm/components/db-markdown-editor.js +1 -458
  98. package/dist/esm/components/db-marquee.js +1 -402
  99. package/dist/esm/components/db-masonry.js +1 -94
  100. package/dist/esm/components/db-menu-item.js +1 -211
  101. package/dist/esm/components/db-modal.js +1 -522
  102. package/dist/esm/components/db-navbar.js +1 -680
  103. package/dist/esm/components/db-onboarding-modal.js +1 -322
  104. package/dist/esm/components/db-otp-input.d.ts +53 -0
  105. package/dist/esm/components/db-otp-input.js +1 -0
  106. package/dist/esm/components/db-page-header.js +1 -312
  107. package/dist/esm/components/db-pagination.js +1 -271
  108. package/dist/esm/components/db-phone-verify.d.ts +61 -0
  109. package/dist/esm/components/db-phone-verify.js +1 -0
  110. package/dist/esm/components/db-popover.js +1 -580
  111. package/dist/esm/components/db-progress-step.js +1 -166
  112. package/dist/esm/components/db-progress.js +1 -171
  113. package/dist/esm/components/db-progressbar.js +1 -72
  114. package/dist/esm/components/db-radio.js +1 -172
  115. package/dist/esm/components/db-random-gradient.js +1 -121
  116. package/dist/esm/components/db-range-slider.js +1 -811
  117. package/dist/esm/components/db-rating.js +1 -325
  118. package/dist/esm/components/db-reorder.js +1 -286
  119. package/dist/esm/components/db-resizable-panels.js +1 -282
  120. package/dist/esm/components/db-scroll-area.js +1 -126
  121. package/dist/esm/components/db-search-bar.js +1 -668
  122. package/dist/esm/components/db-section-app-download.js +1 -101
  123. package/dist/esm/components/db-section-contact.js +1 -102
  124. package/dist/esm/components/db-section-cta.js +1 -150
  125. package/dist/esm/components/db-section-faq.js +1 -120
  126. package/dist/esm/components/db-section-feature-grid.js +1 -137
  127. package/dist/esm/components/db-section-feature.js +1 -179
  128. package/dist/esm/components/db-section-hero.js +1 -243
  129. package/dist/esm/components/db-section-logo-cloud.js +1 -138
  130. package/dist/esm/components/db-section-pricing.js +1 -166
  131. package/dist/esm/components/db-section-stats.js +1 -129
  132. package/dist/esm/components/db-section-testimonials.js +1 -252
  133. package/dist/esm/components/db-section.js +1 -385
  134. package/dist/esm/components/db-segment-control.js +1 -231
  135. package/dist/esm/components/db-select.js +1 -571
  136. package/dist/esm/components/db-share-icons.js +1 -40
  137. package/dist/esm/components/db-share.js +1 -489
  138. package/dist/esm/components/db-sidebar.js +1 -1068
  139. package/dist/esm/components/db-skeleton.js +1 -94
  140. package/dist/esm/components/db-spinner.js +1 -102
  141. package/dist/esm/components/db-split-view.js +1 -297
  142. package/dist/esm/components/db-stack.js +1 -46
  143. package/dist/esm/components/db-stat.js +1 -218
  144. package/dist/esm/components/db-stepper.js +1 -398
  145. package/dist/esm/components/db-table.js +1 -412
  146. package/dist/esm/components/db-tabs.js +1 -335
  147. package/dist/esm/components/db-testimonial.d.ts +2 -0
  148. package/dist/esm/components/db-testimonial.js +1 -162
  149. package/dist/esm/components/db-textarea.js +1 -233
  150. package/dist/esm/components/db-time-picker.js +1 -523
  151. package/dist/esm/components/db-timeline.js +1 -174
  152. package/dist/esm/components/db-toast.js +1 -158
  153. package/dist/esm/components/db-toggle.js +1 -162
  154. package/dist/esm/components/db-toolbar.js +1 -94
  155. package/dist/esm/components/db-tooltip.js +1 -440
  156. package/dist/esm/components/db-top-banner.js +1 -121
  157. package/dist/esm/components/db-tutorial.js +1 -324
  158. package/dist/esm/components/db-two-factor.d.ts +60 -0
  159. package/dist/esm/components/db-two-factor.js +1 -0
  160. package/dist/esm/components/db-video-player.js +1 -503
  161. package/dist/esm/components/db-youtube-player.js +1 -194
  162. package/dist/esm/components/highlight-code.js +1 -65
  163. package/dist/esm/components/input-validation.js +1 -50
  164. package/dist/esm/components/marketing-block-host-utils.js +1 -9
  165. package/dist/esm/components/number-scrub.js +1 -117
  166. package/dist/esm/components/random-gradient-palettes.js +1 -67
  167. package/dist/esm/components/section-block-header-utils.js +1 -48
  168. package/dist/esm/helpers/confirm.js +1 -52
  169. package/dist/esm/helpers/toast.js +1 -73
  170. package/dist/esm/index.d.ts +16 -0
  171. package/dist/esm/index.js +1 -111
  172. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-bash.js +1 -0
  173. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-clike.js +1 -0
  174. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-css.js +1 -0
  175. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-javascript.js +1 -0
  176. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-json.js +1 -0
  177. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-jsx.js +1 -0
  178. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-markup.js +1 -0
  179. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-scss.js +1 -0
  180. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-tsx.js +1 -0
  181. package/dist/esm/node_modules/.pnpm/prismjs@1.30.0/node_modules/prismjs/components/prism-typescript.js +1 -0
  182. package/dist/index.cjs +1 -55414
  183. package/dist/index.css +1 -1
  184. package/dist/index.d.ts +1091 -2
  185. package/dist/index.esm.js +1 -38284
  186. package/dist/react/chunks/lottie-14e7addb.js +1 -0
  187. package/dist/react/index.cjs +1 -56503
  188. package/dist/react/index.d.ts +1620 -54
  189. package/dist/react/index.esm.js +1 -39373
  190. package/package.json +1 -1
  191. package/dist/chunks/lottie-e273b936.js +0 -17032
  192. package/dist/esm/node_modules/prismjs/components/prism-bash.js +0 -235
  193. package/dist/esm/node_modules/prismjs/components/prism-clike.js +0 -31
  194. package/dist/esm/node_modules/prismjs/components/prism-css.js +0 -64
  195. package/dist/esm/node_modules/prismjs/components/prism-javascript.js +0 -172
  196. package/dist/esm/node_modules/prismjs/components/prism-json.js +0 -27
  197. package/dist/esm/node_modules/prismjs/components/prism-jsx.js +0 -143
  198. package/dist/esm/node_modules/prismjs/components/prism-markup.js +0 -186
  199. package/dist/esm/node_modules/prismjs/components/prism-scss.js +0 -81
  200. package/dist/esm/node_modules/prismjs/components/prism-tsx.js +0 -15
  201. package/dist/esm/node_modules/prismjs/components/prism-typescript.js +0 -60
  202. package/dist/react/chunks/lottie-72a4c6d4.js +0 -17032
@@ -1,909 +1 @@
1
- const CLASS = 'designbase-wc-grid-background';
2
- const MAX_DPR = 2;
3
- /** trace 이동 기준 속도(px/s). animation-speed로 배율 조정 */
4
- const TRACE_BASE_SPEED = 88;
5
- /** trace-length 미지정 시 기본 세그먼트 길이(px) */
6
- const TRACE_DEFAULT_LENGTH = 28;
7
- /**
8
- * db-grid-background — 캔버스 기반 그리드 배경.
9
- *
10
- * 패턴은 항상 중앙을 기준으로 대칭 배치되며(가장자리 라인 없음),
11
- * 단독 배경으로도, 다른 배경 위 데코 레이어(overlay)로도 쓸 수 있다.
12
- * interactive/animate 로 마우스 인터랙션과 앰비언트 애니메이션을 켤 수 있다.
13
- *
14
- * @example
15
- * <db-grid-background type="lines" size="32" fade="all"
16
- * interactive animate height="240"></db-grid-background>
17
- */
18
- class DbGridBackground extends HTMLElement {
19
- constructor() {
20
- super(...arguments);
21
- this.lastTime = 0;
22
- this.lastPointerMove = 0;
23
- this.bandAngle = Math.random() * Math.PI * 2;
24
- this.pointer = { x: 0, y: 0, strength: 0, inside: false };
25
- this.cssW = 0;
26
- this.cssH = 0;
27
- this.tracePulses = [];
28
- this.traceSpawnAccumulator = 0;
29
- this.onThemeMediaChange = () => this.scheduleDraw();
30
- this.onPointerMove = (e) => this.handlePointerMove(e);
31
- }
32
- static get observedAttributes() {
33
- return [
34
- 'type',
35
- 'size',
36
- 'thickness',
37
- 'color',
38
- 'theme',
39
- 'opacity',
40
- 'fade',
41
- 'fade-size',
42
- 'background',
43
- 'overlay',
44
- 'interactive',
45
- 'interaction',
46
- 'interaction-color',
47
- 'interaction-radius',
48
- 'cross-size',
49
- 'cross-style',
50
- 'lines-layout',
51
- 'guide-inset',
52
- 'guide-edges',
53
- 'guide-x',
54
- 'guide-y',
55
- 'guide-span-x',
56
- 'guide-span-y',
57
- 'animated',
58
- 'animation-style',
59
- 'animation-speed',
60
- 'trace-length',
61
- 'trace-direction',
62
- 'width',
63
- 'height',
64
- 'border-radius',
65
- ];
66
- }
67
- connectedCallback() {
68
- this.ensureCanvas();
69
- this.applyHostStyles();
70
- this.startObserving();
71
- this.observeDocumentTheme();
72
- this.bindPointer();
73
- this.measure();
74
- this.scheduleDraw();
75
- }
76
- disconnectedCallback() {
77
- this.ro?.disconnect();
78
- this.ro = undefined;
79
- this.themeObserver?.disconnect();
80
- this.themeObserver = undefined;
81
- window.removeEventListener('pointermove', this.onPointerMove);
82
- this.themeMedia?.removeEventListener('change', this.onThemeMediaChange);
83
- this.themeMedia = undefined;
84
- if (this.rafId != null)
85
- cancelAnimationFrame(this.rafId);
86
- this.rafId = undefined;
87
- }
88
- attributeChangedCallback() {
89
- if (!this.isConnected)
90
- return;
91
- this.applyHostStyles();
92
- this.bindPointer();
93
- this.measure();
94
- this.scheduleDraw();
95
- }
96
- /* --------------------------------------------------------------------- */
97
- /* Attr helpers */
98
- /* --------------------------------------------------------------------- */
99
- getBool(name) {
100
- return this.hasAttribute(name) && this.getAttribute(name) !== 'false';
101
- }
102
- getNumber(name, fallback) {
103
- const raw = this.getAttribute(name);
104
- if (raw === null || raw === '')
105
- return fallback;
106
- const n = Number(raw);
107
- return Number.isFinite(n) ? n : fallback;
108
- }
109
- getSizeStyle(value) {
110
- if (!value)
111
- return undefined;
112
- const num = Number(value);
113
- return Number.isFinite(num) ? `${num}px` : value;
114
- }
115
- getColorProbe() {
116
- if (!this.colorProbe) {
117
- const probe = document.createElement('span');
118
- probe.setAttribute('aria-hidden', 'true');
119
- Object.assign(probe.style, {
120
- position: 'absolute',
121
- width: '0',
122
- height: '0',
123
- overflow: 'hidden',
124
- visibility: 'hidden',
125
- pointerEvents: 'none',
126
- });
127
- this.appendChild(probe);
128
- this.colorProbe = probe;
129
- }
130
- return this.colorProbe;
131
- }
132
- resolveCssColor(input) {
133
- if (!input.includes('var(') && !input.startsWith('color-mix'))
134
- return input;
135
- const probe = this.getColorProbe();
136
- probe.style.color = input;
137
- const resolved = getComputedStyle(probe).color;
138
- return resolved && resolved !== 'rgba(0, 0, 0, 0)' ? resolved : input;
139
- }
140
- isDarkContext() {
141
- const theme = this.getAttribute('theme');
142
- if (theme === 'dark')
143
- return true;
144
- if (theme === 'light')
145
- return false;
146
- const root = document.documentElement;
147
- const colorMode = root.getAttribute('data-color-mode');
148
- if (colorMode === 'dark')
149
- return true;
150
- if (colorMode === 'light')
151
- return false;
152
- const rootTheme = root.getAttribute('data-theme');
153
- if (rootTheme === 'dark')
154
- return true;
155
- if (rootTheme === 'light')
156
- return false;
157
- return window.matchMedia('(prefers-color-scheme: dark)').matches;
158
- }
159
- resolveThemeColor(explicit, cssVarName, fallbackLight, fallbackDark) {
160
- if (explicit)
161
- return this.resolveCssColor(explicit);
162
- const probe = this.getColorProbe();
163
- const dark = this.isDarkContext();
164
- probe.style.color = `var(${cssVarName}, ${dark ? fallbackDark : fallbackLight})`;
165
- const resolved = getComputedStyle(probe).color;
166
- if (resolved && resolved !== 'rgba(0, 0, 0, 0)')
167
- return resolved;
168
- return dark ? fallbackDark : fallbackLight;
169
- }
170
- effectiveOpacity() {
171
- const raw = this.getAttribute('opacity');
172
- if (raw !== null && raw !== '') {
173
- return this.getNumber('opacity', 0.05);
174
- }
175
- return this.isDarkContext() ? 0.1 : 0.05;
176
- }
177
- effectiveColor() {
178
- const explicit = this.getAttribute('color');
179
- if (explicit)
180
- return this.resolveCssColor(explicit);
181
- return this.isDarkContext() ? '#ffffff' : '#000000';
182
- }
183
- effectiveInterColor() {
184
- const explicit = this.getAttribute('interaction-color');
185
- if (explicit)
186
- return this.resolveCssColor(explicit);
187
- return this.effectiveColor();
188
- }
189
- traceDrawColor(cfg) {
190
- return this.hasAttribute('interaction-color') ? cfg.interColor : cfg.color;
191
- }
192
- traceDrawOpacity(cfg) {
193
- const base = cfg.opacity;
194
- if (this.hasAttribute('interaction-color')) {
195
- return Math.min(1, base + 0.2);
196
- }
197
- return Math.min(1, base * 3);
198
- }
199
- parseColorRgb(color) {
200
- const resolved = this.resolveCssColor(color);
201
- const rgb = resolved.match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/);
202
- if (rgb)
203
- return [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])];
204
- return this.isDarkContext() ? [255, 255, 255] : [0, 0, 0];
205
- }
206
- crossArm(size, thickness, crossSize) {
207
- if (crossSize > 0) {
208
- if (crossSize <= 1)
209
- return size * crossSize;
210
- return crossSize;
211
- }
212
- return Math.min(size * 0.32, 8 + thickness * 1.5);
213
- }
214
- interactionMode() {
215
- const explicit = this.getAttribute('interaction');
216
- if (explicit === 'glow' || explicit === 'magnet' || explicit === 'rotate' || explicit === 'none') {
217
- return explicit;
218
- }
219
- if (!this.getBool('interactive'))
220
- return 'none';
221
- const type = this.getAttribute('type') || 'lines';
222
- if (type === 'dots')
223
- return 'magnet';
224
- if (type === 'cross')
225
- return 'rotate';
226
- return 'glow';
227
- }
228
- animationStyle() {
229
- const raw = this.getAttribute('animation-style') || 'band';
230
- if (raw === 'trace' || raw === 'both')
231
- return raw;
232
- return 'band';
233
- }
234
- traceDirection() {
235
- const raw = this.getAttribute('trace-direction') || 'both';
236
- if (raw === 'vertical' || raw === 'horizontal')
237
- return raw;
238
- return 'both';
239
- }
240
- linesLayout() {
241
- const raw = this.getAttribute('lines-layout') || 'grid';
242
- if (raw === 'frame' || raw === 'center')
243
- return raw;
244
- return 'grid';
245
- }
246
- crossStyle() {
247
- const raw = this.getAttribute('cross-style') || 'plus';
248
- return raw === 'line' ? 'line' : 'plus';
249
- }
250
- parseGuideEdges(raw) {
251
- const parts = (raw || 'top,right,bottom,left')
252
- .split(',')
253
- .map((part) => part.trim().toLowerCase())
254
- .filter(Boolean);
255
- return new Set(parts);
256
- }
257
- guideFraction(name, fallback) {
258
- const raw = this.getAttribute(name);
259
- if (raw === null || raw === '')
260
- return fallback;
261
- const n = Number(raw);
262
- if (!Number.isFinite(n))
263
- return fallback;
264
- return Math.min(1, Math.max(0, n));
265
- }
266
- isGridLines(cfg) {
267
- return cfg.type === 'lines' && cfg.linesLayout === 'grid';
268
- }
269
- isTraceableLines(cfg) {
270
- return cfg.type === 'lines' && (cfg.linesLayout === 'grid' || cfg.linesLayout === 'frame');
271
- }
272
- get cfg() {
273
- return {
274
- type: (this.getAttribute('type') || 'lines'),
275
- linesLayout: this.linesLayout(),
276
- crossStyle: this.crossStyle(),
277
- size: Math.max(4, this.getNumber('size', 32)),
278
- thickness: Math.max(0.5, this.getNumber('thickness', 1)),
279
- color: this.effectiveColor(),
280
- opacity: this.effectiveOpacity(),
281
- fade: (this.getAttribute('fade') || 'none'),
282
- fadeSize: this.getNumber('fade-size', 0.22),
283
- interaction: this.interactionMode(),
284
- interColor: this.effectiveInterColor(),
285
- interRadius: Math.max(20, this.getNumber('interaction-radius', 130)),
286
- crossSize: this.getNumber('cross-size', 0),
287
- guideInset: Math.max(0, this.getNumber('guide-inset', 16)),
288
- guideEdges: this.parseGuideEdges(this.getAttribute('guide-edges')),
289
- guideX: this.guideFraction('guide-x', 0.5),
290
- guideY: this.guideFraction('guide-y', 0.5),
291
- guideSpanX: Math.max(0, this.getNumber('guide-span-x', 0)),
292
- guideSpanY: Math.max(0, this.getNumber('guide-span-y', 0)),
293
- animate: this.getBool('animated'),
294
- animSpeed: this.getNumber('animation-speed', 1),
295
- animationStyle: this.animationStyle(),
296
- traceLength: this.getNumber('trace-length', 0),
297
- traceDirection: this.traceDirection(),
298
- };
299
- }
300
- /* --------------------------------------------------------------------- */
301
- /* Setup */
302
- /* --------------------------------------------------------------------- */
303
- ensureCanvas() {
304
- if (this.canvas)
305
- return;
306
- const canvas = document.createElement('canvas');
307
- Object.assign(canvas.style, {
308
- display: 'block',
309
- width: '100%',
310
- height: '100%',
311
- pointerEvents: 'none',
312
- });
313
- this.appendChild(canvas);
314
- this.canvas = canvas;
315
- this.ctx = canvas.getContext('2d') ?? undefined;
316
- this.hlCanvas = document.createElement('canvas');
317
- this.maskCanvas = document.createElement('canvas');
318
- }
319
- applyHostStyles() {
320
- const overlay = this.getBool('overlay');
321
- this.className = [CLASS, `${CLASS}--${this.getAttribute('type') || 'lines'}`, overlay && `${CLASS}--overlay`]
322
- .filter(Boolean)
323
- .join(' ');
324
- this.style.display = 'block';
325
- this.style.boxSizing = 'border-box';
326
- // interactive 여도 canvas 는 pointer-events:none, 좌표는 window 에서 계산 → 하위 상호작용 방해 없음
327
- this.style.pointerEvents = 'none';
328
- const bg = this.getAttribute('background');
329
- this.style.backgroundColor = bg ? this.resolveCssColor(bg) : 'transparent';
330
- if (overlay) {
331
- this.style.position = 'absolute';
332
- this.style.inset = '0';
333
- this.style.width = '100%';
334
- this.style.height = '100%';
335
- if (!this.style.zIndex)
336
- this.style.zIndex = '1';
337
- }
338
- else {
339
- if (this.style.position === 'absolute' || !this.style.position)
340
- this.style.position = 'relative';
341
- const width = this.getSizeStyle(this.getAttribute('width'));
342
- const height = this.getSizeStyle(this.getAttribute('height'));
343
- if (width)
344
- this.style.width = width;
345
- if (height)
346
- this.style.height = height;
347
- }
348
- const borderRadius = this.getSizeStyle(this.getAttribute('border-radius'));
349
- this.style.borderRadius = borderRadius || '';
350
- if (this.canvas)
351
- this.canvas.style.borderRadius = borderRadius || '';
352
- }
353
- startObserving() {
354
- if (this.ro)
355
- return;
356
- this.ro = new ResizeObserver(() => {
357
- this.measure();
358
- this.scheduleDraw();
359
- });
360
- this.ro.observe(this);
361
- }
362
- observeDocumentTheme() {
363
- if (this.themeObserver)
364
- return;
365
- if (typeof MutationObserver === 'undefined')
366
- return;
367
- this.themeObserver = new MutationObserver(() => this.scheduleDraw());
368
- this.themeObserver.observe(document.documentElement, {
369
- attributes: true,
370
- attributeFilter: ['data-theme', 'data-color-mode', 'class'],
371
- });
372
- }
373
- bindPointer() {
374
- window.removeEventListener('pointermove', this.onPointerMove);
375
- this.themeMedia?.removeEventListener('change', this.onThemeMediaChange);
376
- if (!this.themeMedia) {
377
- this.themeMedia = window.matchMedia('(prefers-color-scheme: dark)');
378
- }
379
- this.themeMedia.addEventListener('change', this.onThemeMediaChange);
380
- if (this.interactionMode() !== 'none') {
381
- window.addEventListener('pointermove', this.onPointerMove, { passive: true });
382
- }
383
- else {
384
- this.pointer.strength = 0;
385
- this.pointer.inside = false;
386
- }
387
- }
388
- handlePointerMove(e) {
389
- if (!this.canvas)
390
- return;
391
- const rect = this.canvas.getBoundingClientRect();
392
- const x = e.clientX - rect.left;
393
- const y = e.clientY - rect.top;
394
- const pad = this.cfg.interRadius;
395
- const inside = x >= -pad && x <= rect.width + pad && y >= -pad && y <= rect.height + pad;
396
- this.pointer.x = x;
397
- this.pointer.y = y;
398
- this.pointer.inside = inside;
399
- if (inside) {
400
- this.pointer.strength = 1;
401
- this.lastPointerMove = performance.now();
402
- this.ensureLoop();
403
- }
404
- }
405
- measure() {
406
- if (!this.canvas || !this.ctx)
407
- return;
408
- const rect = this.getBoundingClientRect();
409
- const w = Math.max(1, Math.round(rect.width));
410
- const h = Math.max(1, Math.round(rect.height));
411
- this.cssW = w;
412
- this.cssH = h;
413
- this.tracePulses = [];
414
- this.traceSpawnAccumulator = 0;
415
- const dpr = Math.min(MAX_DPR, window.devicePixelRatio || 1);
416
- [this.canvas, this.hlCanvas, this.maskCanvas].forEach((c) => {
417
- if (!c)
418
- return;
419
- c.width = Math.round(w * dpr);
420
- c.height = Math.round(h * dpr);
421
- });
422
- }
423
- /* --------------------------------------------------------------------- */
424
- /* Geometry */
425
- /* --------------------------------------------------------------------- */
426
- /** 중앙을 기준으로 대칭 배치되며 (0, extent) 내부 좌표만 반환 → 가장자리 라인 없음 */
427
- axis(extent, step) {
428
- const center = extent / 2;
429
- const positions = [];
430
- const kMin = Math.ceil((0 - center) / step);
431
- const kMax = Math.floor((extent - center) / step);
432
- for (let k = kMin; k <= kMax; k++) {
433
- const p = center + k * step;
434
- if (p > 0.75 && p < extent - 0.75)
435
- positions.push(p);
436
- }
437
- return positions;
438
- }
439
- /* --------------------------------------------------------------------- */
440
- /* Draw */
441
- /* --------------------------------------------------------------------- */
442
- scheduleDraw() {
443
- const cfg = this.cfg;
444
- // 즉시 1회 동기 렌더 → rAF 지연(백그라운드 탭 등)에도 첫 프레임 보장
445
- this.drawFrame(performance.now(), 0);
446
- if (cfg.animate || this.pointer.strength > 0.001) {
447
- this.ensureLoop();
448
- }
449
- else if (this.rafId != null) {
450
- cancelAnimationFrame(this.rafId);
451
- this.rafId = undefined;
452
- }
453
- }
454
- ensureLoop() {
455
- if (this.rafId != null)
456
- return;
457
- this.lastTime = performance.now();
458
- const tick = (now) => {
459
- const dt = Math.min(0.05, (now - this.lastTime) / 1000);
460
- this.lastTime = now;
461
- // 포인터 비활성 시 강도 감쇠
462
- if (this.interactionMode() !== 'none') {
463
- if (now - this.lastPointerMove > 90) {
464
- this.pointer.strength = Math.max(0, this.pointer.strength - dt * 2.2);
465
- }
466
- }
467
- else {
468
- this.pointer.strength = 0;
469
- }
470
- this.drawFrame(now, dt);
471
- const cfg = this.cfg;
472
- if (cfg.animate || this.pointer.strength > 0.001) {
473
- this.rafId = requestAnimationFrame(tick);
474
- }
475
- else {
476
- this.rafId = undefined;
477
- this.drawFrame(now, dt); // 최종 정적 프레임
478
- }
479
- };
480
- this.rafId = requestAnimationFrame(tick);
481
- }
482
- drawFrame(now, dt) {
483
- const ctx = this.ctx;
484
- if (!ctx || !this.canvas)
485
- return;
486
- const { cssW: W, cssH: H } = this;
487
- if (W <= 0 || H <= 0)
488
- return;
489
- const cfg = this.cfg;
490
- const dpr = Math.min(MAX_DPR, window.devicePixelRatio || 1);
491
- const time = now / 1000;
492
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
493
- ctx.clearRect(0, 0, W, H);
494
- // 1) 베이스 그리드
495
- this.drawPattern(ctx, W, H, cfg, cfg.color, cfg.opacity, time);
496
- // 2) trace 애니메이션 — 그리드/프레임 선을 따라 짧은 세그먼트 이동
497
- if (cfg.animate && this.isTraceableLines(cfg) && (cfg.animationStyle === 'trace' || cfg.animationStyle === 'both')) {
498
- this.drawTraceHighlight(ctx, W, H, cfg, dt);
499
- }
500
- // 3) 하이라이트(글로우/밴드) 레이어
501
- const wantBandHighlight = (cfg.interaction === 'glow' && this.pointer.strength > 0.001) ||
502
- (cfg.animate &&
503
- cfg.animationStyle !== 'trace' &&
504
- (this.isGridLines(cfg) || cfg.type === 'diagonal'));
505
- if (wantBandHighlight && this.hlCanvas && this.maskCanvas) {
506
- const hl = this.hlCanvas.getContext('2d');
507
- const mask = this.maskCanvas.getContext('2d');
508
- if (hl && mask) {
509
- // 2a) 하이라이트 색으로 동일 패턴
510
- hl.setTransform(dpr, 0, 0, dpr, 0, 0);
511
- hl.clearRect(0, 0, W, H);
512
- this.drawPattern(hl, W, H, cfg, cfg.interColor, Math.min(1, cfg.opacity + 0.4), time);
513
- // 2b) 에너지 필드 마스크
514
- this.buildEnergyMask(mask, W, H, dpr, cfg, time);
515
- // 2c) 하이라이트 ∩ 에너지
516
- hl.setTransform(1, 0, 0, 1, 0, 0);
517
- hl.globalCompositeOperation = 'destination-in';
518
- hl.drawImage(this.maskCanvas, 0, 0);
519
- hl.globalCompositeOperation = 'source-over';
520
- // 2d) 가산 합성
521
- ctx.setTransform(1, 0, 0, 1, 0, 0);
522
- ctx.globalCompositeOperation = 'lighter';
523
- ctx.drawImage(this.hlCanvas, 0, 0);
524
- ctx.globalCompositeOperation = 'source-over';
525
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
526
- }
527
- }
528
- // 4) 페이드 마스크
529
- this.applyFade(ctx, W, H, cfg);
530
- }
531
- easeInOutCubic(t) {
532
- const x = Math.min(1, Math.max(0, t));
533
- return x < 0.5 ? 4 * x * x * x : 1 - (-2 * x + 2) ** 3 / 2;
534
- }
535
- traceSegmentLength(cfg) {
536
- if (cfg.traceLength > 0)
537
- return cfg.traceLength;
538
- return TRACE_DEFAULT_LENGTH;
539
- }
540
- pulseHead(pulse, travel) {
541
- const eased = this.easeInOutCubic(pulse.progress);
542
- return -pulse.length + eased * (travel + pulse.length * 2);
543
- }
544
- updateTracePulses(W, H, cfg, dt) {
545
- const segLen = this.traceSegmentLength(cfg);
546
- const maxActive = Math.min(6, Math.max(2, Math.floor((W * H) / (cfg.size * cfg.size * 180))));
547
- const spawnGap = (0.38 + Math.random() * 0.52) / Math.max(0.25, cfg.animSpeed);
548
- this.traceSpawnAccumulator += dt;
549
- while (this.traceSpawnAccumulator >= spawnGap && this.tracePulses.length < maxActive) {
550
- this.traceSpawnAccumulator -= spawnGap;
551
- this.spawnTracePulse(W, H, cfg, segLen);
552
- if (Math.random() > 0.45)
553
- break;
554
- }
555
- this.tracePulses = this.tracePulses.filter((pulse) => {
556
- pulse.progress += dt / pulse.duration;
557
- if (pulse.progress < 0.1)
558
- pulse.alpha = pulse.progress / 0.1;
559
- else if (pulse.progress > 0.9)
560
- pulse.alpha = (1 - pulse.progress) / 0.1;
561
- else
562
- pulse.alpha = 1;
563
- return pulse.progress < 1 && pulse.alpha > 0.02;
564
- });
565
- }
566
- traceLinePositions(W, H, cfg) {
567
- if (cfg.linesLayout === 'frame') {
568
- const inset = Math.min(cfg.guideInset, W / 2 - 1, H / 2 - 1);
569
- const edges = cfg.guideEdges;
570
- const xs = [];
571
- const ys = [];
572
- if (edges.has('left'))
573
- xs.push(inset);
574
- if (edges.has('right'))
575
- xs.push(W - inset);
576
- if (edges.has('top'))
577
- ys.push(inset);
578
- if (edges.has('bottom'))
579
- ys.push(H - inset);
580
- return { xs, ys };
581
- }
582
- return { xs: this.axis(W, cfg.size), ys: this.axis(H, cfg.size) };
583
- }
584
- spawnTracePulse(W, H, cfg, segLen) {
585
- const { xs, ys } = this.traceLinePositions(W, H, cfg);
586
- const allowV = cfg.traceDirection !== 'horizontal' && xs.length > 0;
587
- const allowH = cfg.traceDirection !== 'vertical' && ys.length > 0;
588
- if (!allowV && !allowH)
589
- return;
590
- let axis;
591
- if (allowV && allowH)
592
- axis = Math.random() < 0.5 ? 'v' : 'h';
593
- else
594
- axis = allowV ? 'v' : 'h';
595
- const travel = axis === 'v' ? H : W;
596
- const length = segLen;
597
- const distance = travel + length * 2;
598
- const duration = distance / (TRACE_BASE_SPEED * Math.max(0.25, cfg.animSpeed));
599
- const pulse = {
600
- axis,
601
- linePos: axis === 'v' ? xs[Math.floor(Math.random() * xs.length)] : ys[Math.floor(Math.random() * ys.length)],
602
- progress: 0,
603
- length,
604
- duration,
605
- alpha: 0,
606
- };
607
- this.tracePulses.push(pulse);
608
- }
609
- drawTracePulseSegment(ctx, pulse, W, H, cfg) {
610
- const [r, g, b] = this.parseColorRgb(this.traceDrawColor(cfg));
611
- const coreAlpha = Math.min(1, this.traceDrawOpacity(cfg) * pulse.alpha);
612
- const travel = pulse.axis === 'v' ? H : W;
613
- const head = this.pulseHead(pulse, travel);
614
- let x0;
615
- let y0;
616
- let x1;
617
- let y1;
618
- if (pulse.axis === 'v') {
619
- x0 = x1 = pulse.linePos;
620
- y1 = Math.min(H, head);
621
- y0 = Math.max(0, head - pulse.length);
622
- }
623
- else {
624
- y0 = y1 = pulse.linePos;
625
- x1 = Math.min(W, head);
626
- x0 = Math.max(0, head - pulse.length);
627
- }
628
- if (Math.abs(x1 - x0) < 0.5 && Math.abs(y1 - y0) < 0.5)
629
- return;
630
- const grad = ctx.createLinearGradient(x0, y0, x1, y1);
631
- grad.addColorStop(0, `rgba(${r},${g},${b},0)`);
632
- grad.addColorStop(0.18, `rgba(${r},${g},${b},${coreAlpha * 0.2})`);
633
- grad.addColorStop(0.5, `rgba(${r},${g},${b},${coreAlpha})`);
634
- grad.addColorStop(0.82, `rgba(${r},${g},${b},${coreAlpha * 0.2})`);
635
- grad.addColorStop(1, `rgba(${r},${g},${b},0)`);
636
- ctx.save();
637
- ctx.strokeStyle = grad;
638
- ctx.lineCap = 'round';
639
- ctx.globalAlpha = pulse.alpha;
640
- ctx.shadowColor = `rgba(${r},${g},${b},${coreAlpha * 0.9})`;
641
- ctx.shadowBlur = 12 + cfg.thickness * 5;
642
- ctx.lineWidth = Math.max(1.1, cfg.thickness + 0.6);
643
- ctx.beginPath();
644
- ctx.moveTo(x0, y0);
645
- ctx.lineTo(x1, y1);
646
- ctx.stroke();
647
- ctx.shadowBlur = 0;
648
- ctx.lineWidth = Math.max(0.6, cfg.thickness * 0.35);
649
- ctx.globalAlpha = Math.min(1, pulse.alpha * 1.1);
650
- ctx.stroke();
651
- ctx.restore();
652
- }
653
- drawTraceHighlight(ctx, W, H, cfg, dt) {
654
- this.updateTracePulses(W, H, cfg, dt);
655
- ctx.save();
656
- ctx.globalCompositeOperation = 'lighter';
657
- this.tracePulses.forEach((pulse) => this.drawTracePulseSegment(ctx, pulse, W, H, cfg));
658
- ctx.restore();
659
- }
660
- drawPattern(ctx, W, H, cfg, color, alpha, time) {
661
- const { type, size, thickness } = cfg;
662
- ctx.save();
663
- ctx.globalAlpha = alpha;
664
- ctx.strokeStyle = color;
665
- ctx.fillStyle = color;
666
- ctx.lineWidth = thickness;
667
- ctx.lineCap = 'round';
668
- if (type === 'lines') {
669
- if (cfg.linesLayout === 'frame') {
670
- this.drawFrameGuides(ctx, W, H, cfg);
671
- }
672
- else if (cfg.linesLayout === 'center') {
673
- this.drawCenterGuides(ctx, W, H, cfg);
674
- }
675
- else {
676
- this.drawGridLines(ctx, W, H, cfg.size);
677
- }
678
- }
679
- else if (type === 'dots') {
680
- const xs = this.axis(W, size);
681
- const ys = this.axis(H, size);
682
- const baseR = Math.max(0.75, thickness);
683
- const magnet = cfg.interaction === 'magnet' && this.pointer.strength > 0.001;
684
- const pr = this.pointer;
685
- const rInf = cfg.interRadius;
686
- xs.forEach((x, ix) => {
687
- ys.forEach((y, iy) => {
688
- let cx = x;
689
- let cy = y;
690
- let r = baseR;
691
- if (magnet && pr.inside) {
692
- const dx = pr.x - x;
693
- const dy = pr.y - y;
694
- const d = Math.hypot(dx, dy);
695
- if (d < rInf) {
696
- const t = (1 - d / rInf) * pr.strength;
697
- r = baseR * (1 + t * 1.6);
698
- cx += (dx / (d || 1)) * t * size * 0.28;
699
- cy += (dy / (d || 1)) * t * size * 0.28;
700
- }
701
- }
702
- if (cfg.animate) {
703
- const tw = 0.75 + 0.25 * Math.sin(time * 2 * cfg.animSpeed + (ix + iy) * 0.9);
704
- r *= tw;
705
- }
706
- ctx.beginPath();
707
- ctx.arc(cx, cy, r, 0, Math.PI * 2);
708
- ctx.fill();
709
- });
710
- });
711
- }
712
- else if (type === 'cross') {
713
- const xs = this.axis(W, size);
714
- const ys = this.axis(H, size);
715
- const arm = this.crossArm(size, thickness, cfg.crossSize);
716
- const rotate = cfg.interaction === 'rotate' && this.pointer.strength > 0.001;
717
- const pr = this.pointer;
718
- xs.forEach((x) => {
719
- ys.forEach((y) => {
720
- ctx.save();
721
- ctx.translate(x, y);
722
- if (rotate && pr.inside && pr.strength > 0.01) {
723
- const dx = pr.x - x;
724
- const dy = pr.y - y;
725
- ctx.rotate(Math.atan2(dy, dx));
726
- }
727
- ctx.beginPath();
728
- if (cfg.crossStyle === 'line') {
729
- ctx.moveTo(-arm, 0);
730
- ctx.lineTo(arm, 0);
731
- }
732
- else {
733
- ctx.moveTo(-arm, 0);
734
- ctx.lineTo(arm, 0);
735
- ctx.moveTo(0, -arm);
736
- ctx.lineTo(0, arm);
737
- }
738
- ctx.stroke();
739
- ctx.restore();
740
- });
741
- });
742
- }
743
- else {
744
- // diagonal — 중앙 기준 45° 크로스해치
745
- ctx.save();
746
- ctx.beginPath();
747
- ctx.rect(0, 0, W, H);
748
- ctx.clip();
749
- ctx.translate(W / 2, H / 2);
750
- ctx.rotate(Math.PI / 4);
751
- const R = Math.hypot(W, H);
752
- ctx.beginPath();
753
- for (let p = -R; p <= R; p += size) {
754
- ctx.moveTo(p, -R);
755
- ctx.lineTo(p, R);
756
- ctx.moveTo(-R, p);
757
- ctx.lineTo(R, p);
758
- }
759
- ctx.stroke();
760
- ctx.restore();
761
- }
762
- ctx.restore();
763
- }
764
- drawGridLines(ctx, W, H, size) {
765
- const xs = this.axis(W, size);
766
- const ys = this.axis(H, size);
767
- ctx.beginPath();
768
- xs.forEach((x) => {
769
- ctx.moveTo(x, 0);
770
- ctx.lineTo(x, H);
771
- });
772
- ys.forEach((y) => {
773
- ctx.moveTo(0, y);
774
- ctx.lineTo(W, y);
775
- });
776
- ctx.stroke();
777
- }
778
- drawFrameGuides(ctx, W, H, cfg) {
779
- const inset = Math.min(cfg.guideInset, W / 2 - 1, H / 2 - 1);
780
- const edges = cfg.guideEdges;
781
- ctx.beginPath();
782
- if (edges.has('top')) {
783
- ctx.moveTo(0, inset);
784
- ctx.lineTo(W, inset);
785
- }
786
- if (edges.has('bottom')) {
787
- ctx.moveTo(0, H - inset);
788
- ctx.lineTo(W, H - inset);
789
- }
790
- if (edges.has('left')) {
791
- ctx.moveTo(inset, 0);
792
- ctx.lineTo(inset, H);
793
- }
794
- if (edges.has('right')) {
795
- ctx.moveTo(W - inset, 0);
796
- ctx.lineTo(W - inset, H);
797
- }
798
- ctx.stroke();
799
- }
800
- drawCenterGuides(ctx, W, H, cfg) {
801
- const cx = W * cfg.guideX;
802
- const cy = H * cfg.guideY;
803
- const spanX = cfg.guideSpanX > 0 ? cfg.guideSpanX : W;
804
- const spanY = cfg.guideSpanY > 0 ? cfg.guideSpanY : H;
805
- ctx.beginPath();
806
- ctx.moveTo(cx - spanX / 2, cy);
807
- ctx.lineTo(cx + spanX / 2, cy);
808
- ctx.moveTo(cx, cy - spanY / 2);
809
- ctx.lineTo(cx, cy + spanY / 2);
810
- ctx.stroke();
811
- }
812
- buildEnergyMask(mask, W, H, dpr, cfg, time) {
813
- mask.setTransform(dpr, 0, 0, dpr, 0, 0);
814
- mask.clearRect(0, 0, W, H);
815
- mask.globalCompositeOperation = 'lighter';
816
- // 포인터 방사형
817
- if (cfg.interaction === 'glow' && this.pointer.strength > 0.001 && this.pointer.inside) {
818
- const r = cfg.interRadius;
819
- const g = mask.createRadialGradient(this.pointer.x, this.pointer.y, 0, this.pointer.x, this.pointer.y, r);
820
- g.addColorStop(0, `rgba(255,255,255,${0.9 * this.pointer.strength})`);
821
- g.addColorStop(1, 'rgba(255,255,255,0)');
822
- mask.fillStyle = g;
823
- mask.fillRect(0, 0, W, H);
824
- }
825
- // 이동하는 밝은 밴드
826
- if (cfg.animate && cfg.animationStyle !== 'trace') {
827
- mask.save();
828
- mask.translate(W / 2, H / 2);
829
- mask.rotate(this.bandAngle);
830
- const span = Math.hypot(W, H);
831
- const bandW = Math.max(60, span * 0.18);
832
- const travel = span + bandW;
833
- const pos = ((time * 90 * cfg.animSpeed) % (travel * 2)) - travel;
834
- const grad = mask.createLinearGradient(pos - bandW, 0, pos + bandW, 0);
835
- grad.addColorStop(0, 'rgba(255,255,255,0)');
836
- grad.addColorStop(0.5, 'rgba(255,255,255,0.55)');
837
- grad.addColorStop(1, 'rgba(255,255,255,0)');
838
- mask.fillStyle = grad;
839
- mask.fillRect(-span, -span, span * 2, span * 2);
840
- mask.restore();
841
- }
842
- mask.globalCompositeOperation = 'source-over';
843
- }
844
- applyFade(ctx, W, H, cfg) {
845
- const fade = cfg.fade;
846
- if (fade === 'none')
847
- return;
848
- const frac = cfg.fadeSize;
849
- const pxX = frac > 1 ? frac : Math.max(8, W * frac);
850
- const pxY = frac > 1 ? frac : Math.max(8, H * frac);
851
- ctx.save();
852
- ctx.globalCompositeOperation = 'destination-in';
853
- const edge = (dir) => {
854
- let g;
855
- if (dir === 'top') {
856
- g = ctx.createLinearGradient(0, 0, 0, pxY);
857
- g.addColorStop(0, 'rgba(0,0,0,0)');
858
- g.addColorStop(1, 'rgba(0,0,0,1)');
859
- }
860
- else if (dir === 'bottom') {
861
- g = ctx.createLinearGradient(0, H - pxY, 0, H);
862
- g.addColorStop(0, 'rgba(0,0,0,1)');
863
- g.addColorStop(1, 'rgba(0,0,0,0)');
864
- }
865
- else if (dir === 'left') {
866
- g = ctx.createLinearGradient(0, 0, pxX, 0);
867
- g.addColorStop(0, 'rgba(0,0,0,0)');
868
- g.addColorStop(1, 'rgba(0,0,0,1)');
869
- }
870
- else {
871
- g = ctx.createLinearGradient(W - pxX, 0, W, 0);
872
- g.addColorStop(0, 'rgba(0,0,0,1)');
873
- g.addColorStop(1, 'rgba(0,0,0,0)');
874
- }
875
- // 여러 방향은 교집합(destination-in 연쇄) → 각 변이 개별적으로 페이드
876
- ctx.fillStyle = g;
877
- ctx.fillRect(0, 0, W, H);
878
- };
879
- if (fade === 'vignette') {
880
- const g = ctx.createRadialGradient(W / 2, H / 2, Math.min(W, H) * 0.2, W / 2, H / 2, Math.hypot(W, H) / 2);
881
- g.addColorStop(0, 'rgba(0,0,0,1)');
882
- g.addColorStop(1, 'rgba(0,0,0,0)');
883
- ctx.fillStyle = g;
884
- ctx.fillRect(0, 0, W, H);
885
- }
886
- else {
887
- const edges = fade === 'top'
888
- ? ['top']
889
- : fade === 'bottom'
890
- ? ['bottom']
891
- : fade === 'left'
892
- ? ['left']
893
- : fade === 'right'
894
- ? ['right']
895
- : fade === 'vertical'
896
- ? ['top', 'bottom']
897
- : fade === 'horizontal'
898
- ? ['left', 'right']
899
- : ['top', 'bottom', 'left', 'right']; // all
900
- edges.forEach(edge);
901
- }
902
- ctx.restore();
903
- }
904
- }
905
- if (!customElements.get('db-grid-background')) {
906
- customElements.define('db-grid-background', DbGridBackground);
907
- }
908
-
909
- export { DbGridBackground };
1
+ const t="designbase-wc-grid-background";class DbGridBackground extends HTMLElement{constructor(){super(...arguments),this.lastTime=0,this.lastPointerMove=0,this.bandAngle=Math.random()*Math.PI*2,this.pointer={x:0,y:0,strength:0,inside:!1},this.cssW=0,this.cssH=0,this.tracePulses=[],this.traceSpawnAccumulator=0,this.onThemeMediaChange=()=>this.scheduleDraw(),this.onPointerMove=t=>this.handlePointerMove(t)}static get observedAttributes(){return["type","size","thickness","color","theme","opacity","fade","fade-size","background","overlay","interactive","interaction","interaction-color","interaction-radius","cross-size","cross-style","lines-layout","guide-inset","guide-edges","guide-x","guide-y","guide-span-x","guide-span-y","animated","animation-style","animation-speed","trace-length","trace-direction","width","height","border-radius"]}connectedCallback(){this.ensureCanvas(),this.applyHostStyles(),this.startObserving(),this.observeDocumentTheme(),this.bindPointer(),this.measure(),this.scheduleDraw()}disconnectedCallback(){this.ro?.disconnect(),this.ro=void 0,this.themeObserver?.disconnect(),this.themeObserver=void 0,window.removeEventListener("pointermove",this.onPointerMove),this.themeMedia?.removeEventListener("change",this.onThemeMediaChange),this.themeMedia=void 0,null!=this.rafId&&cancelAnimationFrame(this.rafId),this.rafId=void 0}attributeChangedCallback(){this.isConnected&&(this.applyHostStyles(),this.bindPointer(),this.measure(),this.scheduleDraw())}getBool(t){return this.hasAttribute(t)&&"false"!==this.getAttribute(t)}getNumber(t,e){const i=this.getAttribute(t);if(null===i||""===i)return e;const s=Number(i);return Number.isFinite(s)?s:e}getSizeStyle(t){if(!t)return;const e=Number(t);return Number.isFinite(e)?`${e}px`:t}getColorProbe(){if(!this.colorProbe){const t=document.createElement("span");t.setAttribute("aria-hidden","true"),Object.assign(t.style,{position:"absolute",width:"0",height:"0",overflow:"hidden",visibility:"hidden",pointerEvents:"none"}),this.appendChild(t),this.colorProbe=t}return this.colorProbe}resolveCssColor(t){if(!t.includes("var(")&&!t.startsWith("color-mix"))return t;const e=this.getColorProbe();e.style.color=t;const i=getComputedStyle(e).color;return i&&"rgba(0, 0, 0, 0)"!==i?i:t}isDarkContext(){const t=this.getAttribute("theme");if("dark"===t)return!0;if("light"===t)return!1;const e=document.documentElement,i=e.getAttribute("data-color-mode");if("dark"===i)return!0;if("light"===i)return!1;const s=e.getAttribute("data-theme");return"dark"===s||"light"!==s&&window.matchMedia("(prefers-color-scheme: dark)").matches}resolveThemeColor(t,e,i,s){if(t)return this.resolveCssColor(t);const r=this.getColorProbe(),a=this.isDarkContext();r.style.color=`var(${e}, ${a?s:i})`;const o=getComputedStyle(r).color;return o&&"rgba(0, 0, 0, 0)"!==o?o:a?s:i}effectiveOpacity(){const t=this.getAttribute("opacity");return null!==t&&""!==t?this.getNumber("opacity",.05):this.isDarkContext()?.1:.05}effectiveColor(){const t=this.getAttribute("color");return t?this.resolveCssColor(t):this.isDarkContext()?"#ffffff":"#000000"}effectiveInterColor(){const t=this.getAttribute("interaction-color");return t?this.resolveCssColor(t):this.effectiveColor()}traceDrawColor(t){return this.hasAttribute("interaction-color")?t.interColor:t.color}traceDrawOpacity(t){const e=t.opacity;return this.hasAttribute("interaction-color")?Math.min(1,e+.2):Math.min(1,3*e)}parseColorRgb(t){const e=this.resolveCssColor(t).match(/rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/);return e?[Number(e[1]),Number(e[2]),Number(e[3])]:this.isDarkContext()?[255,255,255]:[0,0,0]}crossArm(t,e,i){return i>0?i<=1?t*i:i:Math.min(.32*t,8+1.5*e)}interactionMode(){const t=this.getAttribute("interaction");if("glow"===t||"magnet"===t||"rotate"===t||"none"===t)return t;if(!this.getBool("interactive"))return"none";const e=this.getAttribute("type")||"lines";return"dots"===e?"magnet":"cross"===e?"rotate":"glow"}animationStyle(){const t=this.getAttribute("animation-style")||"band";return"trace"===t||"both"===t?t:"band"}traceDirection(){const t=this.getAttribute("trace-direction")||"both";return"vertical"===t||"horizontal"===t?t:"both"}linesLayout(){const t=this.getAttribute("lines-layout")||"grid";return"frame"===t||"center"===t?t:"grid"}crossStyle(){return"line"===(this.getAttribute("cross-style")||"plus")?"line":"plus"}parseGuideEdges(t){const e=(t||"top,right,bottom,left").split(",").map(t=>t.trim().toLowerCase()).filter(Boolean);return new Set(e)}guideFraction(t,e){const i=this.getAttribute(t);if(null===i||""===i)return e;const s=Number(i);return Number.isFinite(s)?Math.min(1,Math.max(0,s)):e}isGridLines(t){return"lines"===t.type&&"grid"===t.linesLayout}isTraceableLines(t){return"lines"===t.type&&("grid"===t.linesLayout||"frame"===t.linesLayout)}get cfg(){return{type:this.getAttribute("type")||"lines",linesLayout:this.linesLayout(),crossStyle:this.crossStyle(),size:Math.max(4,this.getNumber("size",32)),thickness:Math.max(.5,this.getNumber("thickness",1)),color:this.effectiveColor(),opacity:this.effectiveOpacity(),fade:this.getAttribute("fade")||"none",fadeSize:this.getNumber("fade-size",.22),interaction:this.interactionMode(),interColor:this.effectiveInterColor(),interRadius:Math.max(20,this.getNumber("interaction-radius",130)),crossSize:this.getNumber("cross-size",0),guideInset:Math.max(0,this.getNumber("guide-inset",16)),guideEdges:this.parseGuideEdges(this.getAttribute("guide-edges")),guideX:this.guideFraction("guide-x",.5),guideY:this.guideFraction("guide-y",.5),guideSpanX:Math.max(0,this.getNumber("guide-span-x",0)),guideSpanY:Math.max(0,this.getNumber("guide-span-y",0)),animate:this.getBool("animated"),animSpeed:this.getNumber("animation-speed",1),animationStyle:this.animationStyle(),traceLength:this.getNumber("trace-length",0),traceDirection:this.traceDirection()}}ensureCanvas(){if(this.canvas)return;const t=document.createElement("canvas");Object.assign(t.style,{display:"block",width:"100%",height:"100%",pointerEvents:"none"}),this.appendChild(t),this.canvas=t,this.ctx=t.getContext("2d")??void 0,this.hlCanvas=document.createElement("canvas"),this.maskCanvas=document.createElement("canvas")}applyHostStyles(){const e=this.getBool("overlay");this.className=[t,`${t}--${this.getAttribute("type")||"lines"}`,e&&`${t}--overlay`].filter(Boolean).join(" "),this.style.display="block",this.style.boxSizing="border-box",this.style.pointerEvents="none";const i=this.getAttribute("background");if(this.style.backgroundColor=i?this.resolveCssColor(i):"transparent",e)this.style.position="absolute",this.style.inset="0",this.style.width="100%",this.style.height="100%",this.style.zIndex||(this.style.zIndex="1");else{"absolute"!==this.style.position&&this.style.position||(this.style.position="relative");const t=this.getSizeStyle(this.getAttribute("width")),e=this.getSizeStyle(this.getAttribute("height"));t&&(this.style.width=t),e&&(this.style.height=e)}const s=this.getSizeStyle(this.getAttribute("border-radius"));this.style.borderRadius=s||"",this.canvas&&(this.canvas.style.borderRadius=s||"")}startObserving(){this.ro||(this.ro=new ResizeObserver(()=>{this.measure(),this.scheduleDraw()}),this.ro.observe(this))}observeDocumentTheme(){this.themeObserver||"undefined"!=typeof MutationObserver&&(this.themeObserver=new MutationObserver(()=>this.scheduleDraw()),this.themeObserver.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme","data-color-mode","class"]}))}bindPointer(){window.removeEventListener("pointermove",this.onPointerMove),this.themeMedia?.removeEventListener("change",this.onThemeMediaChange),this.themeMedia||(this.themeMedia=window.matchMedia("(prefers-color-scheme: dark)")),this.themeMedia.addEventListener("change",this.onThemeMediaChange),"none"!==this.interactionMode()?window.addEventListener("pointermove",this.onPointerMove,{passive:!0}):(this.pointer.strength=0,this.pointer.inside=!1)}handlePointerMove(t){if(!this.canvas)return;const e=this.canvas.getBoundingClientRect(),i=t.clientX-e.left,s=t.clientY-e.top,r=this.cfg.interRadius,a=i>=-r&&i<=e.width+r&&s>=-r&&s<=e.height+r;this.pointer.x=i,this.pointer.y=s,this.pointer.inside=a,a&&(this.pointer.strength=1,this.lastPointerMove=performance.now(),this.ensureLoop())}measure(){if(!this.canvas||!this.ctx)return;const t=this.getBoundingClientRect(),e=Math.max(1,Math.round(t.width)),i=Math.max(1,Math.round(t.height));this.cssW=e,this.cssH=i,this.tracePulses=[],this.traceSpawnAccumulator=0;const s=Math.min(2,window.devicePixelRatio||1);[this.canvas,this.hlCanvas,this.maskCanvas].forEach(t=>{t&&(t.width=Math.round(e*s),t.height=Math.round(i*s))})}axis(t,e){const i=t/2,s=[],r=Math.ceil((0-i)/e),a=Math.floor((t-i)/e);for(let o=r;o<=a;o++){const r=i+o*e;r>.75&&r<t-.75&&s.push(r)}return s}scheduleDraw(){const t=this.cfg;this.drawFrame(performance.now(),0),t.animate||this.pointer.strength>.001?this.ensureLoop():null!=this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=void 0)}ensureLoop(){if(null!=this.rafId)return;this.lastTime=performance.now();const tick=t=>{const e=Math.min(.05,(t-this.lastTime)/1e3);this.lastTime=t,"none"!==this.interactionMode()?t-this.lastPointerMove>90&&(this.pointer.strength=Math.max(0,this.pointer.strength-2.2*e)):this.pointer.strength=0,this.drawFrame(t,e);this.cfg.animate||this.pointer.strength>.001?this.rafId=requestAnimationFrame(tick):(this.rafId=void 0,this.drawFrame(t,e))};this.rafId=requestAnimationFrame(tick)}drawFrame(t,e){const i=this.ctx;if(!i||!this.canvas)return;const{cssW:s,cssH:r}=this;if(s<=0||r<=0)return;const a=this.cfg,o=Math.min(2,window.devicePixelRatio||1),n=t/1e3;i.setTransform(o,0,0,o,0,0),i.clearRect(0,0,s,r),this.drawPattern(i,s,r,a,a.color,a.opacity,n),a.animate&&this.isTraceableLines(a)&&("trace"===a.animationStyle||"both"===a.animationStyle)&&this.drawTraceHighlight(i,s,r,a,e);if(("glow"===a.interaction&&this.pointer.strength>.001||a.animate&&"trace"!==a.animationStyle&&(this.isGridLines(a)||"diagonal"===a.type))&&this.hlCanvas&&this.maskCanvas){const t=this.hlCanvas.getContext("2d"),e=this.maskCanvas.getContext("2d");t&&e&&(t.setTransform(o,0,0,o,0,0),t.clearRect(0,0,s,r),this.drawPattern(t,s,r,a,a.interColor,Math.min(1,a.opacity+.4),n),this.buildEnergyMask(e,s,r,o,a,n),t.setTransform(1,0,0,1,0,0),t.globalCompositeOperation="destination-in",t.drawImage(this.maskCanvas,0,0),t.globalCompositeOperation="source-over",i.setTransform(1,0,0,1,0,0),i.globalCompositeOperation="lighter",i.drawImage(this.hlCanvas,0,0),i.globalCompositeOperation="source-over",i.setTransform(o,0,0,o,0,0))}this.applyFade(i,s,r,a)}easeInOutCubic(t){const e=Math.min(1,Math.max(0,t));return e<.5?4*e*e*e:1-(-2*e+2)**3/2}traceSegmentLength(t){return t.traceLength>0?t.traceLength:28}pulseHead(t,e){const i=this.easeInOutCubic(t.progress);return-t.length+i*(e+2*t.length)}updateTracePulses(t,e,i,s){const r=this.traceSegmentLength(i),a=Math.min(6,Math.max(2,Math.floor(t*e/(i.size*i.size*180)))),o=(.38+.52*Math.random())/Math.max(.25,i.animSpeed);for(this.traceSpawnAccumulator+=s;this.traceSpawnAccumulator>=o&&this.tracePulses.length<a&&(this.traceSpawnAccumulator-=o,this.spawnTracePulse(t,e,i,r),!(Math.random()>.45)););this.tracePulses=this.tracePulses.filter(t=>(t.progress+=s/t.duration,t.progress<.1?t.alpha=t.progress/.1:t.progress>.9?t.alpha=(1-t.progress)/.1:t.alpha=1,t.progress<1&&t.alpha>.02))}traceLinePositions(t,e,i){if("frame"===i.linesLayout){const s=Math.min(i.guideInset,t/2-1,e/2-1),r=i.guideEdges,a=[],o=[];return r.has("left")&&a.push(s),r.has("right")&&a.push(t-s),r.has("top")&&o.push(s),r.has("bottom")&&o.push(e-s),{xs:a,ys:o}}return{xs:this.axis(t,i.size),ys:this.axis(e,i.size)}}spawnTracePulse(t,e,i,s){const{xs:r,ys:a}=this.traceLinePositions(t,e,i),o="horizontal"!==i.traceDirection&&r.length>0,n="vertical"!==i.traceDirection&&a.length>0;if(!o&&!n)return;let h;h=o&&n?Math.random()<.5?"v":"h":o?"v":"h";const l=s,c=(("v"===h?e:t)+2*l)/(88*Math.max(.25,i.animSpeed)),d={axis:h,linePos:"v"===h?r[Math.floor(Math.random()*r.length)]:a[Math.floor(Math.random()*a.length)],progress:0,length:l,duration:c,alpha:0};this.tracePulses.push(d)}drawTracePulseSegment(t,e,i,s,r){const[a,o,n]=this.parseColorRgb(this.traceDrawColor(r)),h=Math.min(1,this.traceDrawOpacity(r)*e.alpha),l="v"===e.axis?s:i,c=this.pulseHead(e,l);let d,u,g,m;if("v"===e.axis?(d=g=e.linePos,m=Math.min(s,c),u=Math.max(0,c-e.length)):(u=m=e.linePos,g=Math.min(i,c),d=Math.max(0,c-e.length)),Math.abs(g-d)<.5&&Math.abs(m-u)<.5)return;const p=t.createLinearGradient(d,u,g,m);p.addColorStop(0,`rgba(${a},${o},${n},0)`),p.addColorStop(.18,`rgba(${a},${o},${n},${.2*h})`),p.addColorStop(.5,`rgba(${a},${o},${n},${h})`),p.addColorStop(.82,`rgba(${a},${o},${n},${.2*h})`),p.addColorStop(1,`rgba(${a},${o},${n},0)`),t.save(),t.strokeStyle=p,t.lineCap="round",t.globalAlpha=e.alpha,t.shadowColor=`rgba(${a},${o},${n},${.9*h})`,t.shadowBlur=12+5*r.thickness,t.lineWidth=Math.max(1.1,r.thickness+.6),t.beginPath(),t.moveTo(d,u),t.lineTo(g,m),t.stroke(),t.shadowBlur=0,t.lineWidth=Math.max(.6,.35*r.thickness),t.globalAlpha=Math.min(1,1.1*e.alpha),t.stroke(),t.restore()}drawTraceHighlight(t,e,i,s,r){this.updateTracePulses(e,i,s,r),t.save(),t.globalCompositeOperation="lighter",this.tracePulses.forEach(r=>this.drawTracePulseSegment(t,r,e,i,s)),t.restore()}drawPattern(t,e,i,s,r,a,o){const{type:n,size:h,thickness:l}=s;if(t.save(),t.globalAlpha=a,t.strokeStyle=r,t.fillStyle=r,t.lineWidth=l,t.lineCap="round","lines"===n)"frame"===s.linesLayout?this.drawFrameGuides(t,e,i,s):"center"===s.linesLayout?this.drawCenterGuides(t,e,i,s):this.drawGridLines(t,e,i,s.size);else if("dots"===n){const r=this.axis(e,h),a=this.axis(i,h),n=Math.max(.75,l),c="magnet"===s.interaction&&this.pointer.strength>.001,d=this.pointer,u=s.interRadius;r.forEach((e,i)=>{a.forEach((r,a)=>{let l=e,g=r,m=n;if(c&&d.inside){const t=d.x-e,i=d.y-r,s=Math.hypot(t,i);if(s<u){const e=(1-s/u)*d.strength;m=n*(1+1.6*e),l+=t/(s||1)*e*h*.28,g+=i/(s||1)*e*h*.28}}if(s.animate){m*=.75+.25*Math.sin(2*o*s.animSpeed+.9*(i+a))}t.beginPath(),t.arc(l,g,m,0,2*Math.PI),t.fill()})})}else if("cross"===n){const r=this.axis(e,h),a=this.axis(i,h),o=this.crossArm(h,l,s.crossSize),n="rotate"===s.interaction&&this.pointer.strength>.001,c=this.pointer;r.forEach(e=>{a.forEach(i=>{if(t.save(),t.translate(e,i),n&&c.inside&&c.strength>.01){const s=c.x-e,r=c.y-i;t.rotate(Math.atan2(r,s))}t.beginPath(),"line"===s.crossStyle?(t.moveTo(-o,0),t.lineTo(o,0)):(t.moveTo(-o,0),t.lineTo(o,0),t.moveTo(0,-o),t.lineTo(0,o)),t.stroke(),t.restore()})})}else{t.save(),t.beginPath(),t.rect(0,0,e,i),t.clip(),t.translate(e/2,i/2),t.rotate(Math.PI/4);const s=Math.hypot(e,i);t.beginPath();for(let e=-s;e<=s;e+=h)t.moveTo(e,-s),t.lineTo(e,s),t.moveTo(-s,e),t.lineTo(s,e);t.stroke(),t.restore()}t.restore()}drawGridLines(t,e,i,s){const r=this.axis(e,s),a=this.axis(i,s);t.beginPath(),r.forEach(e=>{t.moveTo(e,0),t.lineTo(e,i)}),a.forEach(i=>{t.moveTo(0,i),t.lineTo(e,i)}),t.stroke()}drawFrameGuides(t,e,i,s){const r=Math.min(s.guideInset,e/2-1,i/2-1),a=s.guideEdges;t.beginPath(),a.has("top")&&(t.moveTo(0,r),t.lineTo(e,r)),a.has("bottom")&&(t.moveTo(0,i-r),t.lineTo(e,i-r)),a.has("left")&&(t.moveTo(r,0),t.lineTo(r,i)),a.has("right")&&(t.moveTo(e-r,0),t.lineTo(e-r,i)),t.stroke()}drawCenterGuides(t,e,i,s){const r=e*s.guideX,a=i*s.guideY,o=s.guideSpanX>0?s.guideSpanX:e,n=s.guideSpanY>0?s.guideSpanY:i;t.beginPath(),t.moveTo(r-o/2,a),t.lineTo(r+o/2,a),t.moveTo(r,a-n/2),t.lineTo(r,a+n/2),t.stroke()}buildEnergyMask(t,e,i,s,r,a){if(t.setTransform(s,0,0,s,0,0),t.clearRect(0,0,e,i),t.globalCompositeOperation="lighter","glow"===r.interaction&&this.pointer.strength>.001&&this.pointer.inside){const s=r.interRadius,a=t.createRadialGradient(this.pointer.x,this.pointer.y,0,this.pointer.x,this.pointer.y,s);a.addColorStop(0,`rgba(255,255,255,${.9*this.pointer.strength})`),a.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=a,t.fillRect(0,0,e,i)}if(r.animate&&"trace"!==r.animationStyle){t.save(),t.translate(e/2,i/2),t.rotate(this.bandAngle);const s=Math.hypot(e,i),o=Math.max(60,.18*s),n=s+o,h=90*a*r.animSpeed%(2*n)-n,l=t.createLinearGradient(h-o,0,h+o,0);l.addColorStop(0,"rgba(255,255,255,0)"),l.addColorStop(.5,"rgba(255,255,255,0.55)"),l.addColorStop(1,"rgba(255,255,255,0)"),t.fillStyle=l,t.fillRect(-s,-s,2*s,2*s),t.restore()}t.globalCompositeOperation="source-over"}applyFade(t,e,i,s){const r=s.fade;if("none"===r)return;const a=s.fadeSize,o=a>1?a:Math.max(8,e*a),n=a>1?a:Math.max(8,i*a);t.save(),t.globalCompositeOperation="destination-in";const edge=s=>{let r;"top"===s?(r=t.createLinearGradient(0,0,0,n),r.addColorStop(0,"rgba(0,0,0,0)"),r.addColorStop(1,"rgba(0,0,0,1)")):"bottom"===s?(r=t.createLinearGradient(0,i-n,0,i),r.addColorStop(0,"rgba(0,0,0,1)"),r.addColorStop(1,"rgba(0,0,0,0)")):"left"===s?(r=t.createLinearGradient(0,0,o,0),r.addColorStop(0,"rgba(0,0,0,0)"),r.addColorStop(1,"rgba(0,0,0,1)")):(r=t.createLinearGradient(e-o,0,e,0),r.addColorStop(0,"rgba(0,0,0,1)"),r.addColorStop(1,"rgba(0,0,0,0)")),t.fillStyle=r,t.fillRect(0,0,e,i)};if("vignette"===r){const s=t.createRadialGradient(e/2,i/2,.2*Math.min(e,i),e/2,i/2,Math.hypot(e,i)/2);s.addColorStop(0,"rgba(0,0,0,1)"),s.addColorStop(1,"rgba(0,0,0,0)"),t.fillStyle=s,t.fillRect(0,0,e,i)}else{("top"===r?["top"]:"bottom"===r?["bottom"]:"left"===r?["left"]:"right"===r?["right"]:"vertical"===r?["top","bottom"]:"horizontal"===r?["left","right"]:["top","bottom","left","right"]).forEach(edge)}t.restore()}}customElements.get("db-grid-background")||customElements.define("db-grid-background",DbGridBackground);export{DbGridBackground};