@5even7/dlc-ui 0.2.0 → 0.2.2

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/color.mjs CHANGED
@@ -116,20 +116,222 @@ function getPreset(kind, ref) {
116
116
  return found;
117
117
  }
118
118
 
119
- const DEFAULTS = {
120
- capsule: {
119
+ const DEFAULTS = {
120
+ capsule: {
121
121
  quality: 'auto',
122
122
  renderer: 'auto',
123
123
  respectReducedMotion: true}};
124
124
 
125
- function hexToRgb01(hex) {
126
- const normalized = hex.replace('#', '');
127
- const value = Number.parseInt(normalized, 16);
128
- return [
129
- ((value >> 16) & 255) / 255,
130
- ((value >> 8) & 255) / 255,
131
- (value & 255) / 255
132
- ];
125
+ /**
126
+ * Color helpers. `normalizeColor` converts any supported CSS color
127
+ * (hex / named / rgb() / rgba()) into a 6-digit hex string, so renderers can
128
+ * keep working with #rrggbb only. rgba() alpha is intentionally dropped:
129
+ * the WebGL shader has no per-color alpha channel, and the component is
130
+ * opaque by design use component-level `opacity` for transparency.
131
+ */
132
+
133
+ const NAMED_COLORS = {
134
+ aliceblue: '#f0f8ff',
135
+ antiquewhite: '#faebd7',
136
+ aqua: '#00ffff',
137
+ aquamarine: '#7fffd4',
138
+ azure: '#f0ffff',
139
+ beige: '#f5f5dc',
140
+ bisque: '#ffe4c4',
141
+ black: '#000000',
142
+ blanchedalmond: '#ffebcd',
143
+ blue: '#0000ff',
144
+ blueviolet: '#8a2be2',
145
+ brown: '#a52a2a',
146
+ burlywood: '#deb887',
147
+ cadetblue: '#5f9ea0',
148
+ chartreuse: '#7fff00',
149
+ chocolate: '#d2691e',
150
+ coral: '#ff7f50',
151
+ cornflowerblue: '#6495ed',
152
+ cornsilk: '#fff8dc',
153
+ crimson: '#dc143c',
154
+ cyan: '#00ffff',
155
+ darkblue: '#00008b',
156
+ darkcyan: '#008b8b',
157
+ darkgoldenrod: '#b8860b',
158
+ darkgray: '#a9a9a9',
159
+ darkgreen: '#006400',
160
+ darkgrey: '#a9a9a9',
161
+ darkkhaki: '#bdb76b',
162
+ darkmagenta: '#8b008b',
163
+ darkolivegreen: '#556b2f',
164
+ darkorange: '#ff8c00',
165
+ darkorchid: '#9932cc',
166
+ darkred: '#8b0000',
167
+ darksalmon: '#e9967a',
168
+ darkseagreen: '#8fbc8f',
169
+ darkslateblue: '#483d8b',
170
+ darkslategray: '#2f4f4f',
171
+ darkslategrey: '#2f4f4f',
172
+ darkturquoise: '#00ced1',
173
+ darkviolet: '#9400d3',
174
+ deeppink: '#ff1493',
175
+ deepskyblue: '#00bfff',
176
+ dimgray: '#696969',
177
+ dimgrey: '#696969',
178
+ dodgerblue: '#1e90ff',
179
+ firebrick: '#b22222',
180
+ floralwhite: '#fffaf0',
181
+ forestgreen: '#228b22',
182
+ fuchsia: '#ff00ff',
183
+ gainsboro: '#dcdcdc',
184
+ ghostwhite: '#f8f8ff',
185
+ gold: '#ffd700',
186
+ goldenrod: '#daa520',
187
+ gray: '#808080',
188
+ green: '#008000',
189
+ greenyellow: '#adff2f',
190
+ grey: '#808080',
191
+ honeydew: '#f0fff0',
192
+ hotpink: '#ff69b4',
193
+ indianred: '#cd5c5c',
194
+ indigo: '#4b0082',
195
+ ivory: '#fffff0',
196
+ khaki: '#f0e68c',
197
+ lavender: '#e6e6fa',
198
+ lavenderblush: '#fff0f5',
199
+ lawngreen: '#7cfc00',
200
+ lemonchiffon: '#fffacd',
201
+ lightblue: '#add8e6',
202
+ lightcoral: '#f08080',
203
+ lightcyan: '#e0ffff',
204
+ lightgoldenrodyellow: '#fafad2',
205
+ lightgray: '#d3d3d3',
206
+ lightgreen: '#90ee90',
207
+ lightgrey: '#d3d3d3',
208
+ lightpink: '#ffb6c1',
209
+ lightsalmon: '#ffa07a',
210
+ lightseagreen: '#20b2aa',
211
+ lightskyblue: '#87cefa',
212
+ lightslategray: '#778899',
213
+ lightslategrey: '#778899',
214
+ lightsteelblue: '#b0c4de',
215
+ lightyellow: '#ffffe0',
216
+ lime: '#00ff00',
217
+ limegreen: '#32cd32',
218
+ linen: '#faf0e6',
219
+ magenta: '#ff00ff',
220
+ maroon: '#800000',
221
+ mediumaquamarine: '#66cdaa',
222
+ mediumblue: '#0000cd',
223
+ mediumorchid: '#ba55d3',
224
+ mediumpurple: '#9370db',
225
+ mediumseagreen: '#3cb371',
226
+ mediumslateblue: '#7b68ee',
227
+ mediumspringgreen: '#00fa9a',
228
+ mediumturquoise: '#48d1cc',
229
+ mediumvioletred: '#c71585',
230
+ midnightblue: '#191970',
231
+ mintcream: '#f5fffa',
232
+ mistyrose: '#ffe4e1',
233
+ moccasin: '#ffe4b5',
234
+ navajowhite: '#ffdead',
235
+ navy: '#000080',
236
+ oldlace: '#fdf5e6',
237
+ olive: '#808000',
238
+ olivedrab: '#6b8e23',
239
+ orange: '#ffa500',
240
+ orangered: '#ff4500',
241
+ orchid: '#da70d6',
242
+ palegoldenrod: '#eee8aa',
243
+ palegreen: '#98fb98',
244
+ paleturquoise: '#afeeee',
245
+ palevioletred: '#db7093',
246
+ papayawhip: '#ffefd5',
247
+ peachpuff: '#ffdab9',
248
+ peru: '#cd853f',
249
+ pink: '#ffc0cb',
250
+ plum: '#dda0dd',
251
+ powderblue: '#b0e0e6',
252
+ purple: '#800080',
253
+ rebeccapurple: '#663399',
254
+ red: '#ff0000',
255
+ rosybrown: '#bc8f8f',
256
+ royalblue: '#4169e1',
257
+ saddlebrown: '#8b4513',
258
+ salmon: '#fa8072',
259
+ sandybrown: '#f4a460',
260
+ seagreen: '#2e8b57',
261
+ seashell: '#fff5ee',
262
+ sienna: '#a0522d',
263
+ silver: '#c0c0c0',
264
+ skyblue: '#87ceeb',
265
+ slateblue: '#6a5acd',
266
+ slategray: '#708090',
267
+ slategrey: '#708090',
268
+ snow: '#fffafa',
269
+ springgreen: '#00ff7f',
270
+ steelblue: '#4682b4',
271
+ tan: '#d2b48c',
272
+ teal: '#008080',
273
+ thistle: '#d8bfd8',
274
+ tomato: '#ff6347',
275
+ turquoise: '#40e0d0',
276
+ violet: '#ee82ee',
277
+ wheat: '#f5deb3',
278
+ white: '#ffffff',
279
+ whitesmoke: '#f5f5f5',
280
+ yellow: '#ffff00',
281
+ yellowgreen: '#9acd32'
282
+ };
283
+
284
+ function clampChannel(value) {
285
+ return Math.min(255, Math.max(0, Math.round(value)));
286
+ }
287
+
288
+ function normalizeRgbArgs(args) {
289
+ const parts = args.split(/[,\s/]+/).filter(Boolean);
290
+ if (parts.length < 3) return null;
291
+ const to255 = (value) => {
292
+ if (typeof value !== 'string' || !value) return null;
293
+ if (value.endsWith('%')) return clampChannel((parseFloat(value) / 100) * 255);
294
+ const number = Number(value);
295
+ return Number.isFinite(number) ? clampChannel(number) : null;
296
+ };
297
+ const red = to255(parts[0]);
298
+ const green = to255(parts[1]);
299
+ const blue = to255(parts[2]);
300
+ if (red === null || green === null || blue === null) return null;
301
+ return `#${[red, green, blue].map((n) => n.toString(16).padStart(2, '0')).join('')}`;
302
+ }
303
+
304
+ /**
305
+ * Convert any supported CSS color into `#rrggbb`. Returns null when the
306
+ * value cannot be parsed. Supports: #rgb / #rrggbb (case-insensitive),
307
+ * CSS named colors, rgb() / rgba() with comma or modern space syntax,
308
+ * including percentages. Alpha in rgba() is ignored.
309
+ */
310
+ function normalizeColor(value) {
311
+ if (typeof value !== 'string') return null;
312
+ const input = value.trim();
313
+ if (!input) return null;
314
+ const lower = input.toLowerCase();
315
+ if (NAMED_COLORS[lower]) return NAMED_COLORS[lower];
316
+ if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(input)) {
317
+ const hex = input.slice(1).toLowerCase();
318
+ if (hex.length === 3) return `#${hex.split('').map((c) => c + c).join('')}`;
319
+ return `#${hex}`;
320
+ }
321
+ const match = input.match(/^rgba?\((.*)\)$/i);
322
+ if (match) return normalizeRgbArgs(match[1]);
323
+ return null;
324
+ }
325
+
326
+ function hexToRgb01(color) {
327
+ const hex = normalizeColor(color);
328
+ if (!hex) return [0, 0, 0];
329
+ const value = Number.parseInt(hex.slice(1), 16);
330
+ return [
331
+ ((value >> 16) & 255) / 255,
332
+ ((value >> 8) & 255) / 255,
333
+ (value & 255) / 255
334
+ ];
133
335
  }
134
336
 
135
337
  const VERTEX_SHADER = `#version 300 es
@@ -357,12 +559,31 @@ class CosmicRenderer {
357
559
  this.timeOffset = preset.seed * 0.73;
358
560
  }
359
561
 
360
- setDprCap(cap) {
361
- this.options.dprCap = cap;
362
- this.resize();
363
- }
364
-
365
- randomize() {
562
+ setDprCap(cap) {
563
+ this.options.dprCap = cap;
564
+ this.resize();
565
+ }
566
+
567
+ setMouseColor(enabled) {
568
+ this.options.mouseColor = enabled !== false;
569
+ if (this.options.mouseColor) {
570
+ if (this.onPointerMove) return this;
571
+ this.#bindEvents();
572
+ return this;
573
+ }
574
+ if (this.onPointerMove) {
575
+ const target = this.eventTarget;
576
+ target.removeEventListener('pointermove', this.onPointerMove);
577
+ target.removeEventListener('pointerdown', this.onPointerMove);
578
+ target.removeEventListener('pointerleave', this.onPointerLeave);
579
+ this.onPointerMove = null;
580
+ this.onPointerLeave = null;
581
+ }
582
+ this.motionTarget = 0;
583
+ return this;
584
+ }
585
+
586
+ randomize() {
366
587
  this.preset.seed = Math.random() * 100;
367
588
  this.timeOffset = Math.random() * 40;
368
589
  }
@@ -442,10 +663,12 @@ class FallbackRenderer {
442
663
  }
443
664
  }
444
665
 
445
- drawNebula(time) {
446
- const ctx = this.context;
447
- const { width, height } = this.canvas;
448
- const gradient = ctx.createLinearGradient(0, 0, width, height);
666
+ drawNebula(time) {
667
+ const ctx = this.context;
668
+ const { width, height } = this.canvas;
669
+ const t = time * (this.preset.speed || 1);
670
+ const phase = (this.preset.seed || 0) * 0.7;
671
+ const gradient = ctx.createLinearGradient(0, 0, width, height);
449
672
  gradient.addColorStop(0, this.preset.colors[0]);
450
673
  gradient.addColorStop(0.38, this.preset.colors[1]);
451
674
  gradient.addColorStop(0.72, this.preset.colors[2]);
@@ -453,10 +676,10 @@ class FallbackRenderer {
453
676
  ctx.fillStyle = gradient;
454
677
  ctx.fillRect(0, 0, width, height);
455
678
 
456
- ctx.globalCompositeOperation = 'screen';
457
- for (let index = 0; index < 6; index += 1) {
458
- const x = (0.5 + 0.45 * Math.sin(time * 0.32 + index * 1.7)) * width;
459
- const y = (0.5 + 0.4 * Math.cos(time * 0.25 + index)) * height;
679
+ ctx.globalCompositeOperation = 'screen';
680
+ for (let index = 0; index < 6; index += 1) {
681
+ const x = (0.5 + 0.45 * Math.sin(t * 0.32 + index * 1.7 + phase)) * width;
682
+ const y = (0.5 + 0.4 * Math.cos(t * 0.25 + index + phase)) * height;
460
683
  const radius = Math.max(width, height) * (0.08 + (index % 3) * 0.04);
461
684
  const glow = ctx.createRadialGradient(x, y, 0, x, y, radius);
462
685
  glow.addColorStop(0, rgb(this.preset.colors[(index + 1) % 4], 0.24));
@@ -477,8 +700,11 @@ class FallbackRenderer {
477
700
  this.preset = preset;
478
701
  }
479
702
 
480
- randomize() {}
481
- dispose() {}
703
+ randomize() {
704
+ if (this.preset) this.preset.seed = Math.random() * 100;
705
+ }
706
+ setMouseColor() {}
707
+ dispose() {}
482
708
  }
483
709
 
484
710
  /**
@@ -578,6 +804,22 @@ function createVisibilityGuard(element) {
578
804
  };
579
805
  }
580
806
 
807
+ /**
808
+ * Run a callback asynchronously, right after the current task and before the
809
+ * next paint. Used for mount-time events (ready / error) so listeners that
810
+ * are attached after create() still receive them. Falls back for legacy
811
+ * browsers that lack queueMicrotask / Promise.
812
+ */
813
+ function nextTick(fn) {
814
+ if (typeof queueMicrotask === 'function') {
815
+ queueMicrotask(fn);
816
+ } else if (typeof Promise !== 'undefined' && typeof Promise.resolve === 'function') {
817
+ Promise.resolve().then(fn);
818
+ } else {
819
+ setTimeout(fn, 0);
820
+ }
821
+ }
822
+
581
823
  function prefersReducedMotion() {
582
824
  return typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;
583
825
  }
@@ -601,7 +843,7 @@ function createColorBackground(host, options = {}) {
601
843
  }
602
844
 
603
845
  const preset = { ...getPreset('capsule', options.preset ?? '初光') };
604
- const merged = normalizeOptions(
846
+ const merged = normalizeOptions(
605
847
  {
606
848
  quality: DEFAULTS.capsule.quality,
607
849
  renderer: DEFAULTS.capsule.renderer,
@@ -611,8 +853,12 @@ function createColorBackground(host, options = {}) {
611
853
  fallbackColor: true
612
854
  },
613
855
  preset,
614
- options
615
- );
856
+ options
857
+ );
858
+ const normalizedColors = merged.colors.map(normalizeColor);
859
+ if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
860
+ preset.seed = merged.seed;
861
+ preset.speed = merged.speed;
616
862
 
617
863
  const computed = getComputedStyle(host);
618
864
  if (computed.position === 'static' || computed.position === '') {
@@ -641,8 +887,18 @@ function createColorBackground(host, options = {}) {
641
887
  layer.appendChild(canvas);
642
888
  host.appendChild(layer);
643
889
 
644
- const emitter = createEmitter();
645
- let paused = merged.respectReducedMotion && prefersReducedMotion();
890
+ const emitter = createEmitter();
891
+ let paused = merged.respectReducedMotion && prefersReducedMotion();
892
+ let disposed = false;
893
+ const dirty = {
894
+ preset: false,
895
+ seed: false,
896
+ speed: false,
897
+ colors: false,
898
+ opacity: false,
899
+ fallbackColor: false,
900
+ mouseColor: false
901
+ };
646
902
 
647
903
  let renderer;
648
904
  const useWebgl = merged.renderer !== 'canvas2d';
@@ -655,9 +911,12 @@ function createColorBackground(host, options = {}) {
655
911
  });
656
912
  } catch (error) {
657
913
  renderer = new FallbackRenderer(canvas, merged);
658
- emitter.emit('error', {
659
- message: String(error && error.message ? error.message : error)
660
- });
914
+ nextTick(() => {
915
+ if (disposed) return;
916
+ emitter.emit('error', {
917
+ message: String(error && error.message ? error.message : error)
918
+ });
919
+ });
661
920
  }
662
921
  } else {
663
922
  renderer = new FallbackRenderer(canvas, merged);
@@ -681,7 +940,10 @@ function createColorBackground(host, options = {}) {
681
940
  () => paused
682
941
  );
683
942
 
684
- emitter.emit('ready', { preset: { ...preset } });
943
+ nextTick(() => {
944
+ if (disposed) return;
945
+ emitter.emit('ready', { preset: { ...preset } });
946
+ });
685
947
 
686
948
  return {
687
949
  element: host,
@@ -690,38 +952,72 @@ function createColorBackground(host, options = {}) {
690
952
  preset,
691
953
  on: emitter.on,
692
954
  off: emitter.off,
693
- setPreset(ref) {
694
- const next = getPreset('capsule', ref);
695
- Object.assign(preset, next);
696
- renderer.setPreset(next);
697
- renderer.resize();
698
- syncLayerVars();
699
- emitter.emit('presetchange', { preset: { ...next } });
700
- return this;
701
- },
702
- setColors(colors) {
703
- if (!Array.isArray(colors) || colors.length !== 4) return this;
704
- preset.colors = colors;
705
- renderer.setPreset({ ...preset, colors });
706
- syncLayerVars();
707
- return this;
708
- },
955
+ setPreset(ref) {
956
+ const next = getPreset('capsule', ref);
957
+ dirty.preset = true;
958
+ Object.assign(preset, next);
959
+ const nextColors = preset.colors.map(normalizeColor);
960
+ if (nextColors.every(Boolean)) preset.colors = nextColors;
961
+ renderer.setPreset({ ...preset });
962
+ renderer.resize();
963
+ syncLayerVars();
964
+ emitter.emit('presetchange', { preset: { ...preset } });
965
+ return this;
966
+ },
967
+ setColors(colors) {
968
+ const next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
969
+ if (next.length !== 4 || next.some((color) => !color)) return this;
970
+ dirty.colors = true;
971
+ preset.colors = next;
972
+ renderer.setPreset({ ...preset, colors: next });
973
+ syncLayerVars();
974
+ return this;
975
+ },
976
+ setSeed(seed) {
977
+ const value = Number(seed);
978
+ if (!Number.isFinite(value)) return this;
979
+ dirty.seed = true;
980
+ preset.seed = value;
981
+ renderer.setPreset({ ...preset });
982
+ return this;
983
+ },
984
+ setSpeed(speed) {
985
+ const value = Number(speed);
986
+ if (!Number.isFinite(value)) return this;
987
+ dirty.speed = true;
988
+ preset.speed = value;
989
+ renderer.setPreset({ ...preset });
990
+ return this;
991
+ },
992
+ setFallbackColor(value) {
993
+ dirty.fallbackColor = true;
994
+ merged.fallbackColor = value;
995
+ syncLayerVars();
996
+ return this;
997
+ },
998
+ setMouseColor(value) {
999
+ dirty.mouseColor = true;
1000
+ merged.mouseColor = value !== false;
1001
+ if (typeof renderer.setMouseColor === 'function') renderer.setMouseColor(merged.mouseColor);
1002
+ return this;
1003
+ },
709
1004
  setQuality(quality) {
710
1005
  merged.quality = quality;
711
1006
  if (typeof renderer.setDprCap === 'function') renderer.setDprCap(dprCapFor(quality));
712
1007
  return this;
713
1008
  },
714
- setOpacity(value) {
715
- const opacity = Number(value);
716
- if (!Number.isFinite(opacity)) return this;
717
- merged.opacity = Math.min(1, Math.max(0, opacity));
1009
+ setOpacity(value) {
1010
+ const opacity = Number(value);
1011
+ if (!Number.isFinite(opacity)) return this;
1012
+ dirty.opacity = true;
1013
+ merged.opacity = Math.min(1, Math.max(0, opacity));
718
1014
  layer.style.setProperty('--dlc-color-opacity', String(merged.opacity));
719
1015
  return this;
720
1016
  },
721
- randomize() {
722
- renderer.randomize();
723
- return this;
724
- },
1017
+ randomize() {
1018
+ this.setSeed(Math.random() * 100);
1019
+ return this;
1020
+ },
725
1021
  pause() {
726
1022
  paused = true;
727
1023
  return this;
@@ -730,15 +1026,20 @@ function createColorBackground(host, options = {}) {
730
1026
  paused = false;
731
1027
  return this;
732
1028
  },
733
- dispose() {
734
- unsubscribe();
1029
+ dispose() {
1030
+ disposed = true;
1031
+ unsubscribe();
735
1032
  visibility.dispose();
736
1033
  if (resizeObserver) resizeObserver.disconnect();
737
1034
  else window.removeEventListener('resize', renderer.resize);
738
- renderer.dispose();
739
- layer.remove();
740
- }
741
- };
1035
+ renderer.dispose();
1036
+ layer.remove();
1037
+ },
1038
+ opacity: merged.opacity,
1039
+ fallbackColor: merged.fallbackColor,
1040
+ mouseColor: merged.mouseColor,
1041
+ dirty
1042
+ };
742
1043
  }
743
1044
 
744
1045
  export { createColorBackground };