@designbasekorea/ui-wc 0.5.6 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/lottie-35a9b0f5.js +1 -0
- package/dist/esm/components/animation-background/canvas-controller.js +1 -121
- package/dist/esm/components/animation-background/controls.js +1 -180
- package/dist/esm/components/animation-background/graphite-controller.js +1 -196
- package/dist/esm/components/animation-background/graphite-presets.js +1 -42
- package/dist/esm/components/animation-background/graphite-shader.js +1 -111
- package/dist/esm/components/animation-background/layers.js +1 -369
- package/dist/esm/components/animation-background/lumina-presets.js +1 -53
- package/dist/esm/components/animation-background/lumina-shaders.js +1 -181
- package/dist/esm/components/animation-background/lumina-utils.js +1 -60
- package/dist/esm/components/animation-background/particle-defaults.js +1 -36
- package/dist/esm/components/animation-background/renderers.js +1 -167
- package/dist/esm/components/animation-background/shader-controller.js +1 -154
- package/dist/esm/components/color-picker-utils.js +1 -134
- package/dist/esm/components/date-picker-utils.js +1 -140
- package/dist/esm/components/db-accordion.js +1 -304
- package/dist/esm/components/db-ad-banner.js +1 -455
- package/dist/esm/components/db-ai-chat-list.d.ts +43 -0
- package/dist/esm/components/db-ai-chat-list.js +1 -0
- package/dist/esm/components/db-ai-chat.d.ts +159 -0
- package/dist/esm/components/db-ai-chat.js +1 -0
- package/dist/esm/components/db-alert.js +1 -149
- package/dist/esm/components/db-animation-background.js +1 -486
- package/dist/esm/components/db-animation-text.js +1 -601
- package/dist/esm/components/db-app-badge.js +1 -88
- package/dist/esm/components/db-audio-player.js +1 -752
- package/dist/esm/components/db-avatar.js +1 -188
- package/dist/esm/components/db-backdrop.js +1 -139
- package/dist/esm/components/db-badge.js +1 -91
- package/dist/esm/components/db-banner.js +1 -188
- package/dist/esm/components/db-bottom-navigation.js +1 -146
- package/dist/esm/components/db-bottom-sheet.js +1 -481
- package/dist/esm/components/db-breadcrumbs.js +1 -329
- package/dist/esm/components/db-button.js +1 -316
- package/dist/esm/components/db-calendar.js +1 -675
- package/dist/esm/components/db-card.js +1 -265
- package/dist/esm/components/db-carousel.js +1 -264
- package/dist/esm/components/db-chat-composer.d.ts +134 -0
- package/dist/esm/components/db-chat-composer.js +1 -0
- package/dist/esm/components/db-chat-list.d.ts +47 -0
- package/dist/esm/components/db-chat-list.js +1 -0
- package/dist/esm/components/db-chat-message.d.ts +39 -0
- package/dist/esm/components/db-chat-message.js +1 -0
- package/dist/esm/components/db-chat-orb.d.ts +99 -0
- package/dist/esm/components/db-chat-orb.js +1 -0
- package/dist/esm/components/db-chat-room.d.ts +71 -0
- package/dist/esm/components/db-chat-room.js +1 -0
- package/dist/esm/components/db-chat-suggestions.d.ts +44 -0
- package/dist/esm/components/db-chat-suggestions.js +1 -0
- package/dist/esm/components/db-chat-typing.d.ts +22 -0
- package/dist/esm/components/db-chat-typing.js +1 -0
- package/dist/esm/components/db-chat-welcome.d.ts +32 -0
- package/dist/esm/components/db-chat-welcome.js +1 -0
- package/dist/esm/components/db-chat-widget.d.ts +146 -0
- package/dist/esm/components/db-chat-widget.js +1 -0
- package/dist/esm/components/db-checkbox.js +1 -202
- package/dist/esm/components/db-chip.js +1 -104
- package/dist/esm/components/db-code-block.js +1 -304
- package/dist/esm/components/db-color-picker.js +1 -746
- package/dist/esm/components/db-confirm.js +1 -191
- package/dist/esm/components/db-container.js +1 -39
- package/dist/esm/components/db-context-menu.js +1 -265
- package/dist/esm/components/db-countdown.js +1 -146
- package/dist/esm/components/db-cursor-follower.js +1 -173
- package/dist/esm/components/db-date-picker.js +1 -837
- package/dist/esm/components/db-divider.js +1 -66
- package/dist/esm/components/db-download-progress.d.ts +89 -0
- package/dist/esm/components/db-download-progress.js +1 -0
- package/dist/esm/components/db-drawer.js +1 -245
- package/dist/esm/components/db-dropdown.js +1 -493
- package/dist/esm/components/db-dropzone.js +1 -266
- package/dist/esm/components/db-email-verify.d.ts +64 -0
- package/dist/esm/components/db-email-verify.js +1 -0
- package/dist/esm/components/db-empty-state.js +1 -97
- package/dist/esm/components/db-file-uploader.js +1 -256
- package/dist/esm/components/db-floating-action-button.js +1 -139
- package/dist/esm/components/db-footer.js +1 -385
- package/dist/esm/components/db-form.js +1 -566
- package/dist/esm/components/db-gantt.js +1 -882
- package/dist/esm/components/db-gradient.js +1 -133
- package/dist/esm/components/db-grid-background.js +1 -909
- package/dist/esm/components/db-grid.js +1 -73
- package/dist/esm/components/db-icons.d.ts +3 -0
- package/dist/esm/components/db-icons.js +1 -82
- package/dist/esm/components/db-image-list.js +1 -214
- package/dist/esm/components/db-image-placeholder.js +1 -96
- package/dist/esm/components/db-image.js +1 -289
- package/dist/esm/components/db-indicator.js +1 -517
- package/dist/esm/components/db-input.js +1 -420
- package/dist/esm/components/db-kanban.js +1 -538
- package/dist/esm/components/db-label.js +1 -66
- package/dist/esm/components/db-lightbox.js +1 -372
- package/dist/esm/components/db-list.js +1 -340
- package/dist/esm/components/db-logo-svg.js +1 -72
- package/dist/esm/components/db-logo.js +1 -106
- package/dist/esm/components/db-lottie.js +1 -109
- package/dist/esm/components/db-markdown-editor.js +1 -458
- package/dist/esm/components/db-marquee.js +1 -402
- package/dist/esm/components/db-masonry.js +1 -94
- package/dist/esm/components/db-menu-item.js +1 -211
- package/dist/esm/components/db-modal.js +1 -522
- package/dist/esm/components/db-navbar.js +1 -680
- package/dist/esm/components/db-onboarding-modal.js +1 -322
- package/dist/esm/components/db-otp-input.d.ts +53 -0
- package/dist/esm/components/db-otp-input.js +1 -0
- package/dist/esm/components/db-page-header.js +1 -312
- package/dist/esm/components/db-pagination.js +1 -271
- package/dist/esm/components/db-phone-verify.d.ts +63 -0
- package/dist/esm/components/db-phone-verify.js +1 -0
- package/dist/esm/components/db-popover.js +1 -580
- package/dist/esm/components/db-progress-step.js +1 -166
- package/dist/esm/components/db-progress.js +1 -171
- package/dist/esm/components/db-progressbar.js +1 -72
- package/dist/esm/components/db-radio.js +1 -172
- package/dist/esm/components/db-random-gradient.js +1 -121
- package/dist/esm/components/db-range-slider.js +1 -811
- package/dist/esm/components/db-rating.js +1 -325
- package/dist/esm/components/db-reorder.js +1 -286
- package/dist/esm/components/db-resizable-panels.js +1 -282
- package/dist/esm/components/db-scroll-area.js +1 -126
- package/dist/esm/components/db-search-bar.js +1 -668
- package/dist/esm/components/db-section-app-download.js +1 -101
- package/dist/esm/components/db-section-contact.js +1 -102
- package/dist/esm/components/db-section-cta.js +1 -150
- package/dist/esm/components/db-section-faq.js +1 -120
- package/dist/esm/components/db-section-feature-grid.js +1 -137
- package/dist/esm/components/db-section-feature.js +1 -179
- package/dist/esm/components/db-section-hero.js +1 -243
- package/dist/esm/components/db-section-logo-cloud.js +1 -138
- package/dist/esm/components/db-section-pricing.js +1 -166
- package/dist/esm/components/db-section-stats.js +1 -129
- package/dist/esm/components/db-section-testimonials.js +1 -252
- package/dist/esm/components/db-section.js +1 -385
- package/dist/esm/components/db-segment-control.js +1 -231
- package/dist/esm/components/db-select.js +1 -571
- package/dist/esm/components/db-share-icons.js +1 -40
- package/dist/esm/components/db-share.js +1 -489
- package/dist/esm/components/db-sidebar.d.ts +20 -0
- package/dist/esm/components/db-sidebar.js +1 -1068
- package/dist/esm/components/db-skeleton.js +1 -94
- package/dist/esm/components/db-spinner.js +1 -102
- package/dist/esm/components/db-split-view.js +1 -297
- package/dist/esm/components/db-stack.js +1 -46
- package/dist/esm/components/db-stat.js +1 -218
- package/dist/esm/components/db-stepper.js +1 -398
- package/dist/esm/components/db-table.js +1 -412
- package/dist/esm/components/db-tabs.js +1 -335
- package/dist/esm/components/db-testimonial.d.ts +2 -0
- package/dist/esm/components/db-testimonial.js +1 -162
- package/dist/esm/components/db-textarea.js +1 -233
- package/dist/esm/components/db-time-picker.js +1 -523
- package/dist/esm/components/db-timeline.js +1 -174
- package/dist/esm/components/db-toast.js +1 -158
- package/dist/esm/components/db-toggle.js +1 -162
- package/dist/esm/components/db-toolbar.js +1 -94
- package/dist/esm/components/db-tooltip.js +1 -440
- package/dist/esm/components/db-top-banner.js +1 -121
- package/dist/esm/components/db-tutorial.js +1 -324
- package/dist/esm/components/db-two-factor.d.ts +71 -0
- package/dist/esm/components/db-two-factor.js +1 -0
- package/dist/esm/components/db-video-player.js +1 -503
- package/dist/esm/components/db-youtube-player.js +1 -194
- package/dist/esm/components/highlight-code.js +1 -65
- package/dist/esm/components/input-validation.js +1 -50
- package/dist/esm/components/marketing-block-host-utils.js +1 -9
- package/dist/esm/components/number-scrub.js +1 -117
- package/dist/esm/components/random-gradient-palettes.js +1 -67
- package/dist/esm/components/section-block-header-utils.js +1 -48
- package/dist/esm/helpers/confirm.js +1 -52
- package/dist/esm/helpers/toast.js +1 -73
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.js +1 -111
- package/dist/esm/node_modules/prismjs/components/prism-bash.js +1 -235
- package/dist/esm/node_modules/prismjs/components/prism-clike.js +1 -31
- package/dist/esm/node_modules/prismjs/components/prism-css.js +1 -64
- package/dist/esm/node_modules/prismjs/components/prism-javascript.js +1 -172
- package/dist/esm/node_modules/prismjs/components/prism-json.js +1 -27
- package/dist/esm/node_modules/prismjs/components/prism-jsx.js +1 -143
- package/dist/esm/node_modules/prismjs/components/prism-markup.js +1 -186
- package/dist/esm/node_modules/prismjs/components/prism-scss.js +1 -81
- package/dist/esm/node_modules/prismjs/components/prism-tsx.js +1 -15
- package/dist/esm/node_modules/prismjs/components/prism-typescript.js +1 -60
- package/dist/index.cjs +1 -55414
- package/dist/index.css +1 -1
- package/dist/index.d.ts +1125 -2
- package/dist/index.esm.js +1 -38284
- package/dist/react/chunks/lottie-14e7addb.js +1 -0
- package/dist/react/index.cjs +1 -56503
- package/dist/react/index.d.ts +1637 -21
- package/dist/react/index.esm.js +1 -39373
- package/package.json +2 -2
- package/dist/chunks/lottie-e273b936.js +0 -17032
- 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};
|