@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/CHANGELOG.md +34 -7
- package/README.md +132 -106
- package/dist/capsule.cjs +381 -72
- package/dist/capsule.mjs +436 -147
- package/dist/color.cjs +324 -17
- package/dist/color.mjs +366 -65
- package/dist/index.cjs +680 -165
- package/dist/index.mjs +836 -366
- package/dist/index.umd.js +680 -165
- package/dist/index.umd.min.js +1 -1
- package/dist/progress.cjs +446 -96
- package/dist/progress.mjs +516 -191
- package/dist/vue2.cjs +1026 -162
- package/dist/vue2.mjs +1175 -389
- package/dist/vue3.cjs +1026 -162
- package/dist/vue3.mjs +1175 -389
- package/package.json +1 -1
- package/styles/base.css +12 -18
- package/styles/capsule.css +34 -81
- package/styles/progress.css +37 -57
- package/types/index.d.ts +59 -44
- package/types/vue3.d.ts +59 -20
package/dist/index.mjs
CHANGED
|
@@ -183,64 +183,272 @@ function getPreset(kind, ref) {
|
|
|
183
183
|
return found;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
const DEFAULTS = {
|
|
187
|
-
capsule: {
|
|
188
|
-
width: '100%',
|
|
189
|
-
height: 160,
|
|
186
|
+
const DEFAULTS = {
|
|
187
|
+
capsule: {
|
|
188
|
+
width: '100%',
|
|
189
|
+
height: 160,
|
|
190
190
|
quality: 'auto',
|
|
191
191
|
renderer: 'auto',
|
|
192
192
|
respectReducedMotion: true,
|
|
193
193
|
mouseColor: true,
|
|
194
|
-
|
|
195
|
-
},
|
|
196
|
-
progress: {
|
|
197
|
-
width: 454,
|
|
198
|
-
height: 104,
|
|
199
|
-
min: 0,
|
|
194
|
+
textRatio: 39
|
|
195
|
+
},
|
|
196
|
+
progress: {
|
|
197
|
+
width: 454,
|
|
198
|
+
height: 104,
|
|
199
|
+
min: 0,
|
|
200
200
|
max: 100,
|
|
201
|
-
draggable: true,
|
|
202
|
-
keyboard: true,
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
201
|
+
draggable: true,
|
|
202
|
+
keyboard: true,
|
|
203
|
+
disabled: false,
|
|
204
|
+
valueSuffix: '%',
|
|
205
|
+
edgeStyle: 'flow',
|
|
206
|
+
quality: 'auto',
|
|
207
|
+
renderer: 'auto',
|
|
208
|
+
textRatio: 54,
|
|
209
|
+
showValue: true,
|
|
210
|
+
respectReducedMotion: true
|
|
211
|
+
}
|
|
209
212
|
};
|
|
210
213
|
|
|
211
|
-
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
valueSuffix: '%',
|
|
214
|
+
/**
|
|
215
|
+
* Internal accessibility labels and small UI text. Since the copy API was
|
|
216
|
+
* removed (slots own the text area), these are no longer user-overridable.
|
|
217
|
+
* Templates use {brand} {code} {name} placeholders.
|
|
218
|
+
*/
|
|
219
|
+
const COPY = {
|
|
220
|
+
brandName: '画境观屿',
|
|
221
|
+
valueSuffix: '%',
|
|
219
222
|
progressAria: '{brand} {code} 加载进度',
|
|
220
223
|
capsuleAria: '打开 {name} 沉浸预览'
|
|
221
224
|
};
|
|
222
225
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Color helpers. `normalizeColor` converts any supported CSS color
|
|
228
|
+
* (hex / named / rgb() / rgba()) into a 6-digit hex string, so renderers can
|
|
229
|
+
* keep working with #rrggbb only. rgba() alpha is intentionally dropped:
|
|
230
|
+
* the WebGL shader has no per-color alpha channel, and the component is
|
|
231
|
+
* opaque by design — use component-level `opacity` for transparency.
|
|
232
|
+
*/
|
|
233
|
+
|
|
234
|
+
const NAMED_COLORS = {
|
|
235
|
+
aliceblue: '#f0f8ff',
|
|
236
|
+
antiquewhite: '#faebd7',
|
|
237
|
+
aqua: '#00ffff',
|
|
238
|
+
aquamarine: '#7fffd4',
|
|
239
|
+
azure: '#f0ffff',
|
|
240
|
+
beige: '#f5f5dc',
|
|
241
|
+
bisque: '#ffe4c4',
|
|
242
|
+
black: '#000000',
|
|
243
|
+
blanchedalmond: '#ffebcd',
|
|
244
|
+
blue: '#0000ff',
|
|
245
|
+
blueviolet: '#8a2be2',
|
|
246
|
+
brown: '#a52a2a',
|
|
247
|
+
burlywood: '#deb887',
|
|
248
|
+
cadetblue: '#5f9ea0',
|
|
249
|
+
chartreuse: '#7fff00',
|
|
250
|
+
chocolate: '#d2691e',
|
|
251
|
+
coral: '#ff7f50',
|
|
252
|
+
cornflowerblue: '#6495ed',
|
|
253
|
+
cornsilk: '#fff8dc',
|
|
254
|
+
crimson: '#dc143c',
|
|
255
|
+
cyan: '#00ffff',
|
|
256
|
+
darkblue: '#00008b',
|
|
257
|
+
darkcyan: '#008b8b',
|
|
258
|
+
darkgoldenrod: '#b8860b',
|
|
259
|
+
darkgray: '#a9a9a9',
|
|
260
|
+
darkgreen: '#006400',
|
|
261
|
+
darkgrey: '#a9a9a9',
|
|
262
|
+
darkkhaki: '#bdb76b',
|
|
263
|
+
darkmagenta: '#8b008b',
|
|
264
|
+
darkolivegreen: '#556b2f',
|
|
265
|
+
darkorange: '#ff8c00',
|
|
266
|
+
darkorchid: '#9932cc',
|
|
267
|
+
darkred: '#8b0000',
|
|
268
|
+
darksalmon: '#e9967a',
|
|
269
|
+
darkseagreen: '#8fbc8f',
|
|
270
|
+
darkslateblue: '#483d8b',
|
|
271
|
+
darkslategray: '#2f4f4f',
|
|
272
|
+
darkslategrey: '#2f4f4f',
|
|
273
|
+
darkturquoise: '#00ced1',
|
|
274
|
+
darkviolet: '#9400d3',
|
|
275
|
+
deeppink: '#ff1493',
|
|
276
|
+
deepskyblue: '#00bfff',
|
|
277
|
+
dimgray: '#696969',
|
|
278
|
+
dimgrey: '#696969',
|
|
279
|
+
dodgerblue: '#1e90ff',
|
|
280
|
+
firebrick: '#b22222',
|
|
281
|
+
floralwhite: '#fffaf0',
|
|
282
|
+
forestgreen: '#228b22',
|
|
283
|
+
fuchsia: '#ff00ff',
|
|
284
|
+
gainsboro: '#dcdcdc',
|
|
285
|
+
ghostwhite: '#f8f8ff',
|
|
286
|
+
gold: '#ffd700',
|
|
287
|
+
goldenrod: '#daa520',
|
|
288
|
+
gray: '#808080',
|
|
289
|
+
green: '#008000',
|
|
290
|
+
greenyellow: '#adff2f',
|
|
291
|
+
grey: '#808080',
|
|
292
|
+
honeydew: '#f0fff0',
|
|
293
|
+
hotpink: '#ff69b4',
|
|
294
|
+
indianred: '#cd5c5c',
|
|
295
|
+
indigo: '#4b0082',
|
|
296
|
+
ivory: '#fffff0',
|
|
297
|
+
khaki: '#f0e68c',
|
|
298
|
+
lavender: '#e6e6fa',
|
|
299
|
+
lavenderblush: '#fff0f5',
|
|
300
|
+
lawngreen: '#7cfc00',
|
|
301
|
+
lemonchiffon: '#fffacd',
|
|
302
|
+
lightblue: '#add8e6',
|
|
303
|
+
lightcoral: '#f08080',
|
|
304
|
+
lightcyan: '#e0ffff',
|
|
305
|
+
lightgoldenrodyellow: '#fafad2',
|
|
306
|
+
lightgray: '#d3d3d3',
|
|
307
|
+
lightgreen: '#90ee90',
|
|
308
|
+
lightgrey: '#d3d3d3',
|
|
309
|
+
lightpink: '#ffb6c1',
|
|
310
|
+
lightsalmon: '#ffa07a',
|
|
311
|
+
lightseagreen: '#20b2aa',
|
|
312
|
+
lightskyblue: '#87cefa',
|
|
313
|
+
lightslategray: '#778899',
|
|
314
|
+
lightslategrey: '#778899',
|
|
315
|
+
lightsteelblue: '#b0c4de',
|
|
316
|
+
lightyellow: '#ffffe0',
|
|
317
|
+
lime: '#00ff00',
|
|
318
|
+
limegreen: '#32cd32',
|
|
319
|
+
linen: '#faf0e6',
|
|
320
|
+
magenta: '#ff00ff',
|
|
321
|
+
maroon: '#800000',
|
|
322
|
+
mediumaquamarine: '#66cdaa',
|
|
323
|
+
mediumblue: '#0000cd',
|
|
324
|
+
mediumorchid: '#ba55d3',
|
|
325
|
+
mediumpurple: '#9370db',
|
|
326
|
+
mediumseagreen: '#3cb371',
|
|
327
|
+
mediumslateblue: '#7b68ee',
|
|
328
|
+
mediumspringgreen: '#00fa9a',
|
|
329
|
+
mediumturquoise: '#48d1cc',
|
|
330
|
+
mediumvioletred: '#c71585',
|
|
331
|
+
midnightblue: '#191970',
|
|
332
|
+
mintcream: '#f5fffa',
|
|
333
|
+
mistyrose: '#ffe4e1',
|
|
334
|
+
moccasin: '#ffe4b5',
|
|
335
|
+
navajowhite: '#ffdead',
|
|
336
|
+
navy: '#000080',
|
|
337
|
+
oldlace: '#fdf5e6',
|
|
338
|
+
olive: '#808000',
|
|
339
|
+
olivedrab: '#6b8e23',
|
|
340
|
+
orange: '#ffa500',
|
|
341
|
+
orangered: '#ff4500',
|
|
342
|
+
orchid: '#da70d6',
|
|
343
|
+
palegoldenrod: '#eee8aa',
|
|
344
|
+
palegreen: '#98fb98',
|
|
345
|
+
paleturquoise: '#afeeee',
|
|
346
|
+
palevioletred: '#db7093',
|
|
347
|
+
papayawhip: '#ffefd5',
|
|
348
|
+
peachpuff: '#ffdab9',
|
|
349
|
+
peru: '#cd853f',
|
|
350
|
+
pink: '#ffc0cb',
|
|
351
|
+
plum: '#dda0dd',
|
|
352
|
+
powderblue: '#b0e0e6',
|
|
353
|
+
purple: '#800080',
|
|
354
|
+
rebeccapurple: '#663399',
|
|
355
|
+
red: '#ff0000',
|
|
356
|
+
rosybrown: '#bc8f8f',
|
|
357
|
+
royalblue: '#4169e1',
|
|
358
|
+
saddlebrown: '#8b4513',
|
|
359
|
+
salmon: '#fa8072',
|
|
360
|
+
sandybrown: '#f4a460',
|
|
361
|
+
seagreen: '#2e8b57',
|
|
362
|
+
seashell: '#fff5ee',
|
|
363
|
+
sienna: '#a0522d',
|
|
364
|
+
silver: '#c0c0c0',
|
|
365
|
+
skyblue: '#87ceeb',
|
|
366
|
+
slateblue: '#6a5acd',
|
|
367
|
+
slategray: '#708090',
|
|
368
|
+
slategrey: '#708090',
|
|
369
|
+
snow: '#fffafa',
|
|
370
|
+
springgreen: '#00ff7f',
|
|
371
|
+
steelblue: '#4682b4',
|
|
372
|
+
tan: '#d2b48c',
|
|
373
|
+
teal: '#008080',
|
|
374
|
+
thistle: '#d8bfd8',
|
|
375
|
+
tomato: '#ff6347',
|
|
376
|
+
turquoise: '#40e0d0',
|
|
377
|
+
violet: '#ee82ee',
|
|
378
|
+
wheat: '#f5deb3',
|
|
379
|
+
white: '#ffffff',
|
|
380
|
+
whitesmoke: '#f5f5f5',
|
|
381
|
+
yellow: '#ffff00',
|
|
382
|
+
yellowgreen: '#9acd32'
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
function clampChannel(value) {
|
|
386
|
+
return Math.min(255, Math.max(0, Math.round(value)));
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function normalizeRgbArgs(args) {
|
|
390
|
+
const parts = args.split(/[,\s/]+/).filter(Boolean);
|
|
391
|
+
if (parts.length < 3) return null;
|
|
392
|
+
const to255 = (value) => {
|
|
393
|
+
if (typeof value !== 'string' || !value) return null;
|
|
394
|
+
if (value.endsWith('%')) return clampChannel((parseFloat(value) / 100) * 255);
|
|
395
|
+
const number = Number(value);
|
|
396
|
+
return Number.isFinite(number) ? clampChannel(number) : null;
|
|
397
|
+
};
|
|
398
|
+
const red = to255(parts[0]);
|
|
399
|
+
const green = to255(parts[1]);
|
|
400
|
+
const blue = to255(parts[2]);
|
|
401
|
+
if (red === null || green === null || blue === null) return null;
|
|
402
|
+
return `#${[red, green, blue].map((n) => n.toString(16).padStart(2, '0')).join('')}`;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Convert any supported CSS color into `#rrggbb`. Returns null when the
|
|
407
|
+
* value cannot be parsed. Supports: #rgb / #rrggbb (case-insensitive),
|
|
408
|
+
* CSS named colors, rgb() / rgba() with comma or modern space syntax,
|
|
409
|
+
* including percentages. Alpha in rgba() is ignored.
|
|
410
|
+
*/
|
|
411
|
+
function normalizeColor(value) {
|
|
412
|
+
if (typeof value !== 'string') return null;
|
|
413
|
+
const input = value.trim();
|
|
414
|
+
if (!input) return null;
|
|
415
|
+
const lower = input.toLowerCase();
|
|
416
|
+
if (NAMED_COLORS[lower]) return NAMED_COLORS[lower];
|
|
417
|
+
if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(input)) {
|
|
418
|
+
const hex = input.slice(1).toLowerCase();
|
|
419
|
+
if (hex.length === 3) return `#${hex.split('').map((c) => c + c).join('')}`;
|
|
420
|
+
return `#${hex}`;
|
|
421
|
+
}
|
|
422
|
+
const match = input.match(/^rgba?\((.*)\)$/i);
|
|
423
|
+
if (match) return normalizeRgbArgs(match[1]);
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function hexToRgb01$1(color) {
|
|
428
|
+
const hex = normalizeColor(color);
|
|
429
|
+
if (!hex) return [0, 0, 0];
|
|
430
|
+
const value = Number.parseInt(hex.slice(1), 16);
|
|
431
|
+
return [
|
|
432
|
+
((value >> 16) & 255) / 255,
|
|
433
|
+
((value >> 8) & 255) / 255,
|
|
434
|
+
(value & 255) / 255
|
|
435
|
+
];
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function hexToRgba(color, alpha = 1) {
|
|
439
|
+
const hex = normalizeColor(color);
|
|
440
|
+
if (!hex) return 'rgba(0, 0, 0, 0)';
|
|
441
|
+
const value = Number.parseInt(hex.slice(1), 16);
|
|
442
|
+
const red = (value >> 16) & 255;
|
|
443
|
+
const green = (value >> 8) & 255;
|
|
444
|
+
const blue = value & 255;
|
|
445
|
+
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function validateColors(colors) {
|
|
449
|
+
return Array.isArray(colors)
|
|
450
|
+
&& colors.length === 4
|
|
451
|
+
&& colors.every((color) => normalizeColor(color) !== null);
|
|
244
452
|
}
|
|
245
453
|
|
|
246
454
|
const VERTEX_SHADER$1 = `#version 300 es
|
|
@@ -468,12 +676,31 @@ class CosmicRenderer {
|
|
|
468
676
|
this.timeOffset = preset.seed * 0.73;
|
|
469
677
|
}
|
|
470
678
|
|
|
471
|
-
setDprCap(cap) {
|
|
472
|
-
this.options.dprCap = cap;
|
|
473
|
-
this.resize();
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
|
|
679
|
+
setDprCap(cap) {
|
|
680
|
+
this.options.dprCap = cap;
|
|
681
|
+
this.resize();
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
setMouseColor(enabled) {
|
|
685
|
+
this.options.mouseColor = enabled !== false;
|
|
686
|
+
if (this.options.mouseColor) {
|
|
687
|
+
if (this.onPointerMove) return this;
|
|
688
|
+
this.#bindEvents();
|
|
689
|
+
return this;
|
|
690
|
+
}
|
|
691
|
+
if (this.onPointerMove) {
|
|
692
|
+
const target = this.eventTarget;
|
|
693
|
+
target.removeEventListener('pointermove', this.onPointerMove);
|
|
694
|
+
target.removeEventListener('pointerdown', this.onPointerMove);
|
|
695
|
+
target.removeEventListener('pointerleave', this.onPointerLeave);
|
|
696
|
+
this.onPointerMove = null;
|
|
697
|
+
this.onPointerLeave = null;
|
|
698
|
+
}
|
|
699
|
+
this.motionTarget = 0;
|
|
700
|
+
return this;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
randomize() {
|
|
477
704
|
this.preset.seed = Math.random() * 100;
|
|
478
705
|
this.timeOffset = Math.random() * 40;
|
|
479
706
|
}
|
|
@@ -553,10 +780,12 @@ class FallbackRenderer {
|
|
|
553
780
|
}
|
|
554
781
|
}
|
|
555
782
|
|
|
556
|
-
drawNebula(time) {
|
|
557
|
-
const ctx = this.context;
|
|
558
|
-
const { width, height } = this.canvas;
|
|
559
|
-
const
|
|
783
|
+
drawNebula(time) {
|
|
784
|
+
const ctx = this.context;
|
|
785
|
+
const { width, height } = this.canvas;
|
|
786
|
+
const t = time * (this.preset.speed || 1);
|
|
787
|
+
const phase = (this.preset.seed || 0) * 0.7;
|
|
788
|
+
const gradient = ctx.createLinearGradient(0, 0, width, height);
|
|
560
789
|
gradient.addColorStop(0, this.preset.colors[0]);
|
|
561
790
|
gradient.addColorStop(0.38, this.preset.colors[1]);
|
|
562
791
|
gradient.addColorStop(0.72, this.preset.colors[2]);
|
|
@@ -564,10 +793,10 @@ class FallbackRenderer {
|
|
|
564
793
|
ctx.fillStyle = gradient;
|
|
565
794
|
ctx.fillRect(0, 0, width, height);
|
|
566
795
|
|
|
567
|
-
ctx.globalCompositeOperation = 'screen';
|
|
568
|
-
for (let index = 0; index < 6; index += 1) {
|
|
569
|
-
const x = (0.5 + 0.45 * Math.sin(
|
|
570
|
-
const y = (0.5 + 0.4 * Math.cos(
|
|
796
|
+
ctx.globalCompositeOperation = 'screen';
|
|
797
|
+
for (let index = 0; index < 6; index += 1) {
|
|
798
|
+
const x = (0.5 + 0.45 * Math.sin(t * 0.32 + index * 1.7 + phase)) * width;
|
|
799
|
+
const y = (0.5 + 0.4 * Math.cos(t * 0.25 + index + phase)) * height;
|
|
571
800
|
const radius = Math.max(width, height) * (0.08 + (index % 3) * 0.04);
|
|
572
801
|
const glow = ctx.createRadialGradient(x, y, 0, x, y, radius);
|
|
573
802
|
glow.addColorStop(0, rgb(this.preset.colors[(index + 1) % 4], 0.24));
|
|
@@ -588,8 +817,11 @@ class FallbackRenderer {
|
|
|
588
817
|
this.preset = preset;
|
|
589
818
|
}
|
|
590
819
|
|
|
591
|
-
randomize() {
|
|
592
|
-
|
|
820
|
+
randomize() {
|
|
821
|
+
if (this.preset) this.preset.seed = Math.random() * 100;
|
|
822
|
+
}
|
|
823
|
+
setMouseColor() {}
|
|
824
|
+
dispose() {}
|
|
593
825
|
}
|
|
594
826
|
|
|
595
827
|
/**
|
|
@@ -698,78 +930,105 @@ function createVisibilityGuard(element) {
|
|
|
698
930
|
};
|
|
699
931
|
}
|
|
700
932
|
|
|
933
|
+
/**
|
|
934
|
+
* Run a callback asynchronously, right after the current task and before the
|
|
935
|
+
* next paint. Used for mount-time events (ready / error) so listeners that
|
|
936
|
+
* are attached after create() still receive them. Falls back for legacy
|
|
937
|
+
* browsers that lack queueMicrotask / Promise.
|
|
938
|
+
*/
|
|
939
|
+
function nextTick(fn) {
|
|
940
|
+
if (typeof queueMicrotask === 'function') {
|
|
941
|
+
queueMicrotask(fn);
|
|
942
|
+
} else if (typeof Promise !== 'undefined' && typeof Promise.resolve === 'function') {
|
|
943
|
+
Promise.resolve().then(fn);
|
|
944
|
+
} else {
|
|
945
|
+
setTimeout(fn, 0);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
701
949
|
function prefersReducedMotion$2() {
|
|
702
950
|
return typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
703
951
|
}
|
|
704
952
|
|
|
705
953
|
/**
|
|
706
|
-
* Mount a cosmic (nebula) capsule into `container`.
|
|
707
|
-
*
|
|
708
|
-
* Options: preset (
|
|
709
|
-
*
|
|
710
|
-
*
|
|
711
|
-
|
|
712
|
-
|
|
954
|
+
* Mount a cosmic (nebula) capsule into `container`.
|
|
955
|
+
*
|
|
956
|
+
* Options: preset (literary name), width, height (number=px or string with
|
|
957
|
+
* px/%/vw/vh/em/rem), colors, seed, speed, textRatio (0-100, text region
|
|
958
|
+
* width in percent), text (HTML string or DOM nodes for the text slot),
|
|
959
|
+
* colorContent (HTML string or DOM nodes for the color slot), quality,
|
|
960
|
+
* renderer, respectReducedMotion, mouseColor, cssVars.
|
|
961
|
+
*/
|
|
962
|
+
function createCapsule(container, options = {}) {
|
|
713
963
|
if (!container || typeof container.appendChild !== 'function') {
|
|
714
964
|
throw new Error('createCapsule: container element is required');
|
|
715
965
|
}
|
|
716
966
|
|
|
717
967
|
const preset = { ...getPreset('capsule', options.preset ?? '初光') };
|
|
718
|
-
const merged = normalizeOptions(DEFAULTS.capsule, preset, options);
|
|
719
|
-
const
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
root
|
|
726
|
-
root.
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
};
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
if (
|
|
743
|
-
if (
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
canvas.
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
root.appendChild(canvas);
|
|
769
|
-
container.appendChild(root);
|
|
770
|
-
|
|
771
|
-
const emitter = createEmitter();
|
|
772
|
-
let paused = merged.respectReducedMotion && prefersReducedMotion$2();
|
|
968
|
+
const merged = normalizeOptions(DEFAULTS.capsule, preset, options);
|
|
969
|
+
const normalizedColors = merged.colors.map(normalizeColor);
|
|
970
|
+
if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
|
|
971
|
+
preset.seed = merged.seed;
|
|
972
|
+
preset.speed = merged.speed;
|
|
973
|
+
let customLabel = typeof options.label === 'string' && options.label ? options.label : null;
|
|
974
|
+
|
|
975
|
+
const root = document.createElement('div');
|
|
976
|
+
root.className = 'hj-capsule-root hj-capsule-cosmic';
|
|
977
|
+
root.dataset.group = preset.group;
|
|
978
|
+
root.dataset.mode = 'nebula';
|
|
979
|
+
root.dataset.theme = preset.theme || 'light';
|
|
980
|
+
root.setAttribute('role', 'img');
|
|
981
|
+
const updateAria = () => {
|
|
982
|
+
root.setAttribute('aria-label', customLabel || COPY.capsuleAria.replace('{name}', preset.name));
|
|
983
|
+
};
|
|
984
|
+
updateAria();
|
|
985
|
+
|
|
986
|
+
// Slot containers are always present but transparent by default: without
|
|
987
|
+
// content they are invisible, so "no slot" behaves like the old
|
|
988
|
+
// showCopy=false. They only provide geometry — no typography, padding or
|
|
989
|
+
// background is imposed on user content (see docs: 插槽 CSS 约定).
|
|
990
|
+
const fillContent = (layer, content) => {
|
|
991
|
+
layer.innerHTML = '';
|
|
992
|
+
if (content == null) return;
|
|
993
|
+
if (typeof content === 'string') {
|
|
994
|
+
layer.innerHTML = content;
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
const nodes = Array.isArray(content) ? content : [content];
|
|
998
|
+
for (const node of nodes) {
|
|
999
|
+
if (node && typeof node.nodeType === 'number') layer.appendChild(node);
|
|
1000
|
+
}
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
const textLayer = document.createElement('div');
|
|
1004
|
+
textLayer.className = 'hj-capsule-text hj-capsule-copy';
|
|
1005
|
+
|
|
1006
|
+
const visualLayer = document.createElement('div');
|
|
1007
|
+
visualLayer.className = 'hj-capsule-visual';
|
|
1008
|
+
|
|
1009
|
+
fillContent(textLayer, options.text);
|
|
1010
|
+
fillContent(visualLayer, options.colorContent);
|
|
1011
|
+
|
|
1012
|
+
const canvas = document.createElement('canvas');
|
|
1013
|
+
canvas.className = 'hj-capsule-canvas';
|
|
1014
|
+
canvas.setAttribute('aria-hidden', 'true');
|
|
1015
|
+
|
|
1016
|
+
root.appendChild(textLayer);
|
|
1017
|
+
root.appendChild(visualLayer);
|
|
1018
|
+
root.appendChild(canvas);
|
|
1019
|
+
container.appendChild(root);
|
|
1020
|
+
|
|
1021
|
+
const emitter = createEmitter();
|
|
1022
|
+
let paused = merged.respectReducedMotion && prefersReducedMotion$2();
|
|
1023
|
+
let disposed = false;
|
|
1024
|
+
const dirty = {
|
|
1025
|
+
preset: false,
|
|
1026
|
+
seed: false,
|
|
1027
|
+
speed: false,
|
|
1028
|
+
colors: false,
|
|
1029
|
+
textRatio: false,
|
|
1030
|
+
cssVars: false
|
|
1031
|
+
};
|
|
773
1032
|
|
|
774
1033
|
let renderer;
|
|
775
1034
|
const useWebgl = merged.renderer !== 'canvas2d';
|
|
@@ -778,19 +1037,25 @@ function createCapsule(container, options = {}) {
|
|
|
778
1037
|
renderer = new CosmicRenderer(canvas, merged, { dprCap: dprCapFor(merged.quality) });
|
|
779
1038
|
} catch (error) {
|
|
780
1039
|
renderer = new FallbackRenderer(canvas, merged);
|
|
781
|
-
|
|
1040
|
+
nextTick(() => {
|
|
1041
|
+
if (disposed) return;
|
|
1042
|
+
emitter.emit('error', { message: String(error && error.message ? error.message : error) });
|
|
1043
|
+
});
|
|
782
1044
|
}
|
|
783
1045
|
} else {
|
|
784
1046
|
renderer = new FallbackRenderer(canvas, merged);
|
|
785
1047
|
}
|
|
786
1048
|
|
|
787
|
-
const applySize = () => {
|
|
788
|
-
root.style.width = parseSize(merged.width);
|
|
789
|
-
root.style.height = parseSize(merged.height);
|
|
790
|
-
const vars = merged.cssVars || {};
|
|
791
|
-
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
792
|
-
|
|
793
|
-
|
|
1049
|
+
const applySize = () => {
|
|
1050
|
+
root.style.width = parseSize(merged.width);
|
|
1051
|
+
root.style.height = parseSize(merged.height);
|
|
1052
|
+
const vars = merged.cssVars || {};
|
|
1053
|
+
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
1054
|
+
if (merged.textRatio !== undefined) {
|
|
1055
|
+
root.style.setProperty('--hj-text-width', `${merged.textRatio}%`);
|
|
1056
|
+
}
|
|
1057
|
+
renderer.resize();
|
|
1058
|
+
};
|
|
794
1059
|
applySize();
|
|
795
1060
|
|
|
796
1061
|
const resizeObserver = typeof ResizeObserver !== 'undefined'
|
|
@@ -819,38 +1084,47 @@ function createCapsule(container, options = {}) {
|
|
|
819
1084
|
() => paused
|
|
820
1085
|
);
|
|
821
1086
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
root.dataset.theme = preset.theme || 'light';
|
|
827
|
-
renderCopy();
|
|
828
|
-
};
|
|
1087
|
+
nextTick(() => {
|
|
1088
|
+
if (disposed) return;
|
|
1089
|
+
emitter.emit('ready', { preset: { ...preset } });
|
|
1090
|
+
});
|
|
829
1091
|
|
|
830
|
-
|
|
1092
|
+
const syncTheme = () => {
|
|
1093
|
+
root.dataset.mode = 'nebula';
|
|
1094
|
+
root.dataset.theme = preset.theme || 'light';
|
|
1095
|
+
updateAria();
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
return {
|
|
831
1099
|
element: root,
|
|
832
1100
|
canvas,
|
|
833
1101
|
preset,
|
|
834
1102
|
on: emitter.on,
|
|
835
1103
|
off: emitter.off,
|
|
836
|
-
setPreset(ref) {
|
|
837
|
-
const next = getPreset('capsule', ref);
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1104
|
+
setPreset(ref) {
|
|
1105
|
+
const next = getPreset('capsule', ref);
|
|
1106
|
+
dirty.preset = true;
|
|
1107
|
+
Object.assign(preset, next);
|
|
1108
|
+
const nextColors = preset.colors.map(normalizeColor);
|
|
1109
|
+
if (nextColors.every(Boolean)) preset.colors = nextColors;
|
|
1110
|
+
renderer.setPreset({ ...preset });
|
|
1111
|
+
syncTheme();
|
|
1112
|
+
renderer.resize();
|
|
1113
|
+
emitter.emit('presetchange', { preset: { ...next } });
|
|
1114
|
+
return this;
|
|
844
1115
|
},
|
|
845
1116
|
setColors(colors) {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
1117
|
+
const next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
|
|
1118
|
+
if (next.length !== 4 || next.some((color) => !color)) return this;
|
|
1119
|
+
dirty.colors = true;
|
|
1120
|
+
preset.colors = next;
|
|
1121
|
+
renderer.setPreset({ ...preset, colors: next });
|
|
849
1122
|
return this;
|
|
850
1123
|
},
|
|
851
1124
|
setSeed(seed) {
|
|
852
1125
|
const value = Number(seed);
|
|
853
1126
|
if (!Number.isFinite(value)) return this;
|
|
1127
|
+
dirty.seed = true;
|
|
854
1128
|
preset.seed = value;
|
|
855
1129
|
renderer.setPreset({ ...preset });
|
|
856
1130
|
return this;
|
|
@@ -858,20 +1132,49 @@ function createCapsule(container, options = {}) {
|
|
|
858
1132
|
setSpeed(speed) {
|
|
859
1133
|
const value = Number(speed);
|
|
860
1134
|
if (!Number.isFinite(value)) return this;
|
|
1135
|
+
dirty.speed = true;
|
|
861
1136
|
preset.speed = value;
|
|
862
1137
|
renderer.setPreset({ ...preset });
|
|
863
1138
|
return this;
|
|
864
1139
|
},
|
|
865
|
-
|
|
1140
|
+
setText(content) {
|
|
1141
|
+
fillContent(textLayer, content);
|
|
1142
|
+
return this;
|
|
1143
|
+
},
|
|
1144
|
+
setColorContent(content) {
|
|
1145
|
+
fillContent(visualLayer, content);
|
|
1146
|
+
return this;
|
|
1147
|
+
},
|
|
1148
|
+
setTextRatio(ratio) {
|
|
1149
|
+
const value = Number(ratio);
|
|
1150
|
+
if (!Number.isFinite(value) || value < 0 || value > 100) return this;
|
|
1151
|
+
dirty.textRatio = true;
|
|
1152
|
+
merged.textRatio = value;
|
|
1153
|
+
root.style.setProperty('--hj-text-width', `${value}%`);
|
|
1154
|
+
return this;
|
|
1155
|
+
},
|
|
1156
|
+
setCssVars(vars) {
|
|
1157
|
+
if (!vars || typeof vars !== 'object') return this;
|
|
1158
|
+
dirty.cssVars = true;
|
|
1159
|
+
merged.cssVars = { ...(merged.cssVars || {}), ...vars };
|
|
1160
|
+
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
1161
|
+
return this;
|
|
1162
|
+
},
|
|
1163
|
+
setLabel(label) {
|
|
1164
|
+
customLabel = typeof label === 'string' && label ? label : null;
|
|
1165
|
+
updateAria();
|
|
1166
|
+
return this;
|
|
1167
|
+
},
|
|
1168
|
+
setSize(width, height) {
|
|
866
1169
|
if (width !== undefined) merged.width = width;
|
|
867
1170
|
if (height !== undefined) merged.height = height;
|
|
868
1171
|
applySize();
|
|
869
1172
|
return this;
|
|
870
1173
|
},
|
|
871
|
-
randomize() {
|
|
872
|
-
|
|
873
|
-
return this;
|
|
874
|
-
},
|
|
1174
|
+
randomize() {
|
|
1175
|
+
this.setSeed(Math.random() * 100);
|
|
1176
|
+
return this;
|
|
1177
|
+
},
|
|
875
1178
|
pause() {
|
|
876
1179
|
paused = true;
|
|
877
1180
|
return this;
|
|
@@ -885,15 +1188,19 @@ function createCapsule(container, options = {}) {
|
|
|
885
1188
|
if (typeof renderer.setDprCap === 'function') renderer.setDprCap(dprCapFor(quality));
|
|
886
1189
|
return this;
|
|
887
1190
|
},
|
|
888
|
-
dispose() {
|
|
889
|
-
|
|
1191
|
+
dispose() {
|
|
1192
|
+
disposed = true;
|
|
1193
|
+
unsubscribe();
|
|
890
1194
|
visibility.dispose();
|
|
891
1195
|
if (resizeObserver) resizeObserver.disconnect();
|
|
892
1196
|
else window.removeEventListener('resize', renderer.resize);
|
|
893
|
-
renderer.dispose();
|
|
894
|
-
root.remove();
|
|
895
|
-
}
|
|
896
|
-
|
|
1197
|
+
renderer.dispose();
|
|
1198
|
+
root.remove();
|
|
1199
|
+
},
|
|
1200
|
+
textRatio: merged.textRatio,
|
|
1201
|
+
cssVars: merged.cssVars,
|
|
1202
|
+
dirty
|
|
1203
|
+
};
|
|
897
1204
|
}
|
|
898
1205
|
|
|
899
1206
|
const PROGRESS_MOTION_WIDTH = 240;
|
|
@@ -1581,14 +1888,16 @@ const FLOW_PROFILES = {
|
|
|
1581
1888
|
};
|
|
1582
1889
|
|
|
1583
1890
|
class ProgressCapsuleController {
|
|
1584
|
-
constructor({ root, canvas, valueElement, preset, emitter, options, copy }) {
|
|
1891
|
+
constructor({ root, canvas, valueElement, preset, emitter, options, copy, dirty }) {
|
|
1585
1892
|
this.root = root;
|
|
1586
1893
|
this.canvas = canvas;
|
|
1587
1894
|
this.valueElement = valueElement;
|
|
1588
1895
|
this.preset = preset;
|
|
1589
1896
|
this.emitter = emitter;
|
|
1590
|
-
this.options = options;
|
|
1591
|
-
this.copy = copy;
|
|
1897
|
+
this.options = options;
|
|
1898
|
+
this.copy = copy;
|
|
1899
|
+
this.dirty = dirty;
|
|
1900
|
+
this.valueSuffix = options.valueSuffix ?? copy.valueSuffix;
|
|
1592
1901
|
this.profile = preset.edgeStyle === 'tide'
|
|
1593
1902
|
? FLOW_PROFILES.tide
|
|
1594
1903
|
: (FLOW_PROFILES[preset.id] || FLOW_PROFILES['visual-training']);
|
|
@@ -1629,11 +1938,22 @@ class ProgressCapsuleController {
|
|
|
1629
1938
|
this.value = clamp(nextValue, this.min, this.max);
|
|
1630
1939
|
const rounded = Math.round(this.value);
|
|
1631
1940
|
this.root.style.setProperty('--progress', this.value.toFixed(2));
|
|
1632
|
-
this.root.setAttribute('aria-valuenow', String(rounded));
|
|
1633
|
-
this.root.setAttribute('aria-valuetext', `${rounded}${this.
|
|
1634
|
-
this.valueElement.textContent = `${rounded}${this.
|
|
1635
|
-
if (!this.suppressEvents)
|
|
1636
|
-
|
|
1941
|
+
this.root.setAttribute('aria-valuenow', String(rounded));
|
|
1942
|
+
this.root.setAttribute('aria-valuetext', `${rounded}${this.valueSuffix}`);
|
|
1943
|
+
if (this.valueElement) this.valueElement.textContent = `${rounded}${this.valueSuffix}`;
|
|
1944
|
+
if (!this.suppressEvents) {
|
|
1945
|
+
if (this.dirty) this.dirty.value = true;
|
|
1946
|
+
this.emitter.emit('change', { value: this.value, source });
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
setValueSuffix(suffix) {
|
|
1951
|
+
this.valueSuffix = String(suffix == null ? '' : suffix);
|
|
1952
|
+
const rounded = Math.round(this.value);
|
|
1953
|
+
if (this.valueElement) this.valueElement.textContent = `${rounded}${this.valueSuffix}`;
|
|
1954
|
+
this.root.setAttribute('aria-valuetext', `${rounded}${this.valueSuffix}`);
|
|
1955
|
+
return this;
|
|
1956
|
+
}
|
|
1637
1957
|
|
|
1638
1958
|
resizeCanvas() {
|
|
1639
1959
|
const bounds = this.root.getBoundingClientRect();
|
|
@@ -1806,13 +2126,14 @@ class ProgressCapsuleController {
|
|
|
1806
2126
|
if (clamped !== this.value) this.setProgress(clamped, 'prop');
|
|
1807
2127
|
}
|
|
1808
2128
|
|
|
1809
|
-
drawReferenceFlow() {
|
|
2129
|
+
drawReferenceFlow() {
|
|
1810
2130
|
const ctx = this.ctx;
|
|
1811
2131
|
const width = this.width;
|
|
1812
2132
|
const height = this.height;
|
|
1813
2133
|
if (!ctx || width <= 0 || height <= 0) return;
|
|
1814
2134
|
|
|
1815
|
-
const
|
|
2135
|
+
const range = Math.max(this.max - this.min, 1);
|
|
2136
|
+
const shoreline = width * Math.min(Math.max((this.value - this.min) / range, 0), 1);
|
|
1816
2137
|
const time = this.flowTime;
|
|
1817
2138
|
const [dark, accentA, accentB, glow] = this.preset.colors;
|
|
1818
2139
|
|
|
@@ -1930,32 +2251,49 @@ class ProgressCapsuleController {
|
|
|
1930
2251
|
}
|
|
1931
2252
|
}
|
|
1932
2253
|
|
|
1933
|
-
updateFromPointer(event) {
|
|
1934
|
-
const bounds = this.root.getBoundingClientRect();
|
|
1935
|
-
const ratio = bounds.width > 0 ? (event.clientX - bounds.left) / bounds.width : 0;
|
|
1936
|
-
this.setProgress(ratio *
|
|
1937
|
-
}
|
|
1938
|
-
|
|
1939
|
-
beginDrag(event) {
|
|
1940
|
-
if (event.button !== undefined && event.button !== 0) return;
|
|
1941
|
-
event.preventDefault();
|
|
1942
|
-
this.dragging = true;
|
|
1943
|
-
this.
|
|
1944
|
-
|
|
1945
|
-
this.
|
|
1946
|
-
this.
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
this.
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
if (
|
|
1957
|
-
this.
|
|
1958
|
-
|
|
2254
|
+
updateFromPointer(event) {
|
|
2255
|
+
const bounds = this.root.getBoundingClientRect();
|
|
2256
|
+
const ratio = bounds.width > 0 ? (event.clientX - bounds.left) / bounds.width : 0;
|
|
2257
|
+
this.setProgress(this.min + ratio * (this.max - this.min), 'drag');
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
beginDrag(event) {
|
|
2261
|
+
if (event.button !== undefined && event.button !== 0) return;
|
|
2262
|
+
event.preventDefault();
|
|
2263
|
+
this.dragging = true;
|
|
2264
|
+
this.pendingPointer = null;
|
|
2265
|
+
this._dragRaf = 0;
|
|
2266
|
+
this.root.classList.add('is-dragging');
|
|
2267
|
+
try { this.root.setPointerCapture?.(event.pointerId); } catch {}
|
|
2268
|
+
this.emitter.emit('dragstart', { value: this.value });
|
|
2269
|
+
this.updateFromPointer(event);
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
moveDrag(event) {
|
|
2273
|
+
if (!this.dragging) return;
|
|
2274
|
+
event.preventDefault();
|
|
2275
|
+
// rAF 合并:一帧最多一次 setProgress/change,视觉仍每帧更新。
|
|
2276
|
+
this.pendingPointer = event;
|
|
2277
|
+
if (this._dragRaf) return;
|
|
2278
|
+
this._dragRaf = requestAnimationFrame(() => {
|
|
2279
|
+
this._dragRaf = 0;
|
|
2280
|
+
const pending = this.pendingPointer;
|
|
2281
|
+
this.pendingPointer = null;
|
|
2282
|
+
if (pending) this.updateFromPointer(pending);
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
endDrag(event) {
|
|
2287
|
+
if (!this.dragging) return;
|
|
2288
|
+
this.dragging = false;
|
|
2289
|
+
if (this._dragRaf) {
|
|
2290
|
+
cancelAnimationFrame(this._dragRaf);
|
|
2291
|
+
this._dragRaf = 0;
|
|
2292
|
+
}
|
|
2293
|
+
const pending = this.pendingPointer;
|
|
2294
|
+
this.pendingPointer = null;
|
|
2295
|
+
if (pending) this.updateFromPointer(pending);
|
|
2296
|
+
this.root.classList.remove('is-dragging');
|
|
1959
2297
|
try {
|
|
1960
2298
|
if (event?.pointerId !== undefined && this.root.hasPointerCapture?.(event.pointerId)) {
|
|
1961
2299
|
this.root.releasePointerCapture(event.pointerId);
|
|
@@ -1977,15 +2315,16 @@ class ProgressCapsuleController {
|
|
|
1977
2315
|
}
|
|
1978
2316
|
|
|
1979
2317
|
if (this.options.keyboard !== false) {
|
|
1980
|
-
this.handlers.keydown = (event) => {
|
|
1981
|
-
const
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
2318
|
+
this.handlers.keydown = (event) => {
|
|
2319
|
+
const step = (this.max - this.min) * 0.02;
|
|
2320
|
+
const actions = {
|
|
2321
|
+
ArrowLeft: -step,
|
|
2322
|
+
ArrowDown: -step,
|
|
2323
|
+
ArrowRight: step,
|
|
2324
|
+
ArrowUp: step,
|
|
2325
|
+
PageDown: -step * 5,
|
|
2326
|
+
PageUp: step * 5
|
|
2327
|
+
};
|
|
1989
2328
|
if (event.key === 'Home') {
|
|
1990
2329
|
event.preventDefault();
|
|
1991
2330
|
this.setProgress(this.min, 'keyboard');
|
|
@@ -2023,8 +2362,9 @@ class ProgressCapsuleController {
|
|
|
2023
2362
|
this.preset = { ...this.preset, colors };
|
|
2024
2363
|
}
|
|
2025
2364
|
|
|
2026
|
-
dispose() {
|
|
2027
|
-
if (this.
|
|
2365
|
+
dispose() {
|
|
2366
|
+
if (this._dragRaf) cancelAnimationFrame(this._dragRaf);
|
|
2367
|
+
if (this.resizeObserver) this.resizeObserver.disconnect();
|
|
2028
2368
|
else window.removeEventListener('resize', this.resizeCanvas);
|
|
2029
2369
|
for (const name of Object.keys(this.handlers)) {
|
|
2030
2370
|
const handler = this.handlers[name];
|
|
@@ -2035,103 +2375,127 @@ class ProgressCapsuleController {
|
|
|
2035
2375
|
}
|
|
2036
2376
|
|
|
2037
2377
|
/**
|
|
2038
|
-
* Mount a fluid progress capsule into `container`.
|
|
2039
|
-
*
|
|
2040
|
-
* Options: preset (id/code/name or object), width, height, value, min, max,
|
|
2041
|
-
* draggable, keyboard, colors, edgeStyle,
|
|
2042
|
-
*
|
|
2043
|
-
|
|
2044
|
-
|
|
2378
|
+
* Mount a fluid progress capsule into `container`.
|
|
2379
|
+
*
|
|
2380
|
+
* Options: preset (id/code/name or object), width, height, value, min, max,
|
|
2381
|
+
* draggable, keyboard, colors, edgeStyle, textRatio (0-100, text region
|
|
2382
|
+
* width in percent), text (HTML string or DOM nodes for the text slot),
|
|
2383
|
+
* colorContent (HTML string or DOM nodes for the color slot),
|
|
2384
|
+
* showValue (show/hide the right-side percentage), quality,
|
|
2385
|
+
* respectReducedMotion, cssVars.
|
|
2386
|
+
*/
|
|
2387
|
+
function createProgressCapsule(container, options = {}) {
|
|
2045
2388
|
if (!container || typeof container.appendChild !== 'function') {
|
|
2046
2389
|
throw new Error('createProgressCapsule: container element is required');
|
|
2047
2390
|
}
|
|
2048
2391
|
|
|
2049
2392
|
const preset = { ...getPreset('progress', options.preset ?? '星火') };
|
|
2050
|
-
const merged = normalizeOptions(DEFAULTS.progress, preset, options);
|
|
2051
|
-
const
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2393
|
+
const merged = normalizeOptions(DEFAULTS.progress, preset, options);
|
|
2394
|
+
const normalizedColors = merged.colors.map(normalizeColor);
|
|
2395
|
+
if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
|
|
2396
|
+
preset.edgeStyle = merged.edgeStyle;
|
|
2397
|
+
const copy = COPY;
|
|
2398
|
+
let customLabel = typeof options.label === 'string' && options.label ? options.label : null;
|
|
2399
|
+
// Vue 的裸布尔属性(<ProgressCapsule disabled />)会传成空字符串,
|
|
2400
|
+
// 这里统一按 true 处理。
|
|
2401
|
+
const disabled = merged.disabled === true || merged.disabled === '' || merged.disabled === 'true';
|
|
2402
|
+
const effectiveDraggable = disabled ? false : merged.draggable !== false;
|
|
2403
|
+
const effectiveKeyboard = disabled ? false : merged.keyboard !== false;
|
|
2404
|
+
const dirty = {
|
|
2405
|
+
preset: false,
|
|
2406
|
+
colors: false,
|
|
2407
|
+
textRatio: false,
|
|
2408
|
+
cssVars: false,
|
|
2409
|
+
edgeStyle: false,
|
|
2410
|
+
value: false
|
|
2411
|
+
};
|
|
2412
|
+
|
|
2413
|
+
const root = document.createElement('div');
|
|
2414
|
+
root.className = 'hj-capsule-root hj-progress-root';
|
|
2415
|
+
root.setAttribute('role', 'slider');
|
|
2416
|
+
root.setAttribute('tabindex', '0');
|
|
2417
|
+
root.setAttribute('data-draggable', String(effectiveDraggable));
|
|
2418
|
+
if (disabled) {
|
|
2419
|
+
root.setAttribute('aria-disabled', 'true');
|
|
2420
|
+
root.classList.add('is-disabled');
|
|
2421
|
+
}
|
|
2058
2422
|
root.setAttribute('aria-valuemin', String(merged.min ?? 0));
|
|
2059
2423
|
root.setAttribute('aria-valuemax', String(merged.max ?? 100));
|
|
2060
2424
|
root.setAttribute('aria-valuenow', String(preset.initialProgress));
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2425
|
+
const updateAria = () => {
|
|
2426
|
+
root.setAttribute(
|
|
2427
|
+
'aria-label',
|
|
2428
|
+
customLabel || interpolate(copy.progressAria, { brand: copy.brandName, code: preset.code, name: preset.name })
|
|
2429
|
+
);
|
|
2430
|
+
};
|
|
2431
|
+
updateAria();
|
|
2432
|
+
if (!effectiveKeyboard) root.setAttribute('tabindex', '-1');
|
|
2066
2433
|
|
|
2067
2434
|
const canvas = document.createElement('canvas');
|
|
2068
2435
|
canvas.className = 'hj-progress-canvas';
|
|
2069
2436
|
canvas.setAttribute('aria-hidden', 'true');
|
|
2070
2437
|
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
const
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
const
|
|
2081
|
-
const
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
const
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
root.style.height = parseSize(merged.height);
|
|
2121
|
-
const vars = merged.cssVars || {};
|
|
2122
|
-
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
2123
|
-
};
|
|
2438
|
+
// Slot containers are always present but transparent by default; they only
|
|
2439
|
+
// provide geometry, never typography/padding/background (docs: 插槽 CSS 约定).
|
|
2440
|
+
const fillContent = (layer, content) => {
|
|
2441
|
+
layer.innerHTML = '';
|
|
2442
|
+
if (content == null) return;
|
|
2443
|
+
if (typeof content === 'string') {
|
|
2444
|
+
layer.innerHTML = content;
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2447
|
+
const nodes = Array.isArray(content) ? content : [content];
|
|
2448
|
+
for (const node of nodes) {
|
|
2449
|
+
if (node && typeof node.nodeType === 'number') layer.appendChild(node);
|
|
2450
|
+
}
|
|
2451
|
+
};
|
|
2452
|
+
|
|
2453
|
+
const textLayer = document.createElement('div');
|
|
2454
|
+
textLayer.className = 'hj-progress-text hj-progress-copy';
|
|
2455
|
+
|
|
2456
|
+
const visualLayer = document.createElement('div');
|
|
2457
|
+
visualLayer.className = 'hj-progress-visual';
|
|
2458
|
+
|
|
2459
|
+
fillContent(textLayer, options.text);
|
|
2460
|
+
fillContent(visualLayer, options.colorContent);
|
|
2461
|
+
|
|
2462
|
+
const valueElement = merged.showValue === false ? null : document.createElement('div');
|
|
2463
|
+
if (valueElement) {
|
|
2464
|
+
valueElement.className = 'hj-progress-value';
|
|
2465
|
+
valueElement.setAttribute('aria-hidden', 'true');
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
root.appendChild(canvas);
|
|
2469
|
+
root.appendChild(textLayer);
|
|
2470
|
+
root.appendChild(visualLayer);
|
|
2471
|
+
if (valueElement) root.appendChild(valueElement);
|
|
2472
|
+
container.appendChild(root);
|
|
2473
|
+
|
|
2474
|
+
const emitter = createEmitter();
|
|
2475
|
+
let paused = merged.respectReducedMotion && prefersReducedMotion$1();
|
|
2476
|
+
let disposed = false;
|
|
2477
|
+
|
|
2478
|
+
const applySize = () => {
|
|
2479
|
+
root.style.width = parseSize(merged.width);
|
|
2480
|
+
root.style.height = parseSize(merged.height);
|
|
2481
|
+
const vars = merged.cssVars || {};
|
|
2482
|
+
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
2483
|
+
if (merged.textRatio !== undefined) {
|
|
2484
|
+
root.style.setProperty('--hj-text-width', `${merged.textRatio}%`);
|
|
2485
|
+
}
|
|
2486
|
+
};
|
|
2124
2487
|
applySize();
|
|
2125
2488
|
|
|
2126
|
-
const controller = new ProgressCapsuleController({
|
|
2127
|
-
root,
|
|
2128
|
-
canvas,
|
|
2129
|
-
valueElement,
|
|
2130
|
-
preset,
|
|
2131
|
-
emitter,
|
|
2132
|
-
options: merged,
|
|
2133
|
-
copy
|
|
2134
|
-
|
|
2489
|
+
const controller = new ProgressCapsuleController({
|
|
2490
|
+
root,
|
|
2491
|
+
canvas,
|
|
2492
|
+
valueElement,
|
|
2493
|
+
preset,
|
|
2494
|
+
emitter,
|
|
2495
|
+
options: { ...merged, draggable: effectiveDraggable, keyboard: effectiveKeyboard },
|
|
2496
|
+
copy,
|
|
2497
|
+
dirty
|
|
2498
|
+
});
|
|
2135
2499
|
|
|
2136
2500
|
let overlay = null;
|
|
2137
2501
|
if (merged.renderer !== 'canvas2d') {
|
|
@@ -2142,7 +2506,13 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2142
2506
|
getProgress: () => controller.value
|
|
2143
2507
|
});
|
|
2144
2508
|
}
|
|
2145
|
-
if (overlay) controller.webglActive = true;
|
|
2509
|
+
if (overlay) controller.webglActive = true;
|
|
2510
|
+
else if (merged.renderer !== 'canvas2d') {
|
|
2511
|
+
nextTick(() => {
|
|
2512
|
+
if (disposed) return;
|
|
2513
|
+
emitter.emit('error', { message: 'WebGL2 unavailable, using Canvas2D fallback' });
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2146
2516
|
|
|
2147
2517
|
const visibility = createVisibilityGuard(root);
|
|
2148
2518
|
let flowTime = 0;
|
|
@@ -2158,15 +2528,14 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2158
2528
|
() => paused
|
|
2159
2529
|
);
|
|
2160
2530
|
|
|
2161
|
-
|
|
2531
|
+
nextTick(() => {
|
|
2532
|
+
if (disposed) return;
|
|
2533
|
+
emitter.emit('ready', { preset: { ...preset } });
|
|
2534
|
+
});
|
|
2162
2535
|
|
|
2163
|
-
const syncDom = () => {
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
interpolate(copy.progressAria, { brand: copy.brandName, code: preset.code, name: preset.name })
|
|
2167
|
-
);
|
|
2168
|
-
renderCopy();
|
|
2169
|
-
};
|
|
2536
|
+
const syncDom = () => {
|
|
2537
|
+
updateAria();
|
|
2538
|
+
};
|
|
2170
2539
|
|
|
2171
2540
|
return {
|
|
2172
2541
|
element: root,
|
|
@@ -2187,26 +2556,30 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2187
2556
|
},
|
|
2188
2557
|
setPreset(ref) {
|
|
2189
2558
|
const next = getPreset('progress', ref);
|
|
2559
|
+
dirty.preset = true;
|
|
2190
2560
|
Object.assign(preset, next);
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2561
|
+
const nextColors = preset.colors.map(normalizeColor);
|
|
2562
|
+
if (nextColors.every(Boolean)) preset.colors = nextColors;
|
|
2563
|
+
controller.preset = preset;
|
|
2564
|
+
controller.profile = next.edgeStyle === 'tide'
|
|
2565
|
+
? FLOW_PROFILES.tide
|
|
2566
|
+
: (FLOW_PROFILES[next.id] || FLOW_PROFILES['visual-training']);
|
|
2567
|
+
controller.flowTime = stringSeed(next.id) * 31;
|
|
2568
|
+
controller.seed = stringSeed(`${next.id}-reference`) * Math.PI * 2;
|
|
2569
|
+
syncDom();
|
|
2570
|
+
if (overlay) {
|
|
2571
|
+
overlay.dispose();
|
|
2572
|
+
overlay = attachProgressFlowOverlay({ root, canvas, preset, getProgress: () => controller.value });
|
|
2573
|
+
}
|
|
2574
|
+
controller.webglActive = Boolean(overlay);
|
|
2575
|
+
controller.resizeCanvas();
|
|
2576
|
+
emitter.emit('presetchange', { preset: { ...preset } });
|
|
2205
2577
|
return this;
|
|
2206
2578
|
},
|
|
2207
2579
|
setEdgeStyle(edgeStyle) {
|
|
2208
2580
|
const value = String(edgeStyle || '').toLowerCase();
|
|
2209
2581
|
if (value !== 'flow' && value !== 'tide') return this;
|
|
2582
|
+
dirty.edgeStyle = true;
|
|
2210
2583
|
preset.edgeStyle = value;
|
|
2211
2584
|
controller.profile = value === 'tide'
|
|
2212
2585
|
? FLOW_PROFILES.tide
|
|
@@ -2219,12 +2592,46 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2219
2592
|
controller.resizeCanvas();
|
|
2220
2593
|
return this;
|
|
2221
2594
|
},
|
|
2595
|
+
setText(content) {
|
|
2596
|
+
fillContent(textLayer, content);
|
|
2597
|
+
return this;
|
|
2598
|
+
},
|
|
2599
|
+
setColorContent(content) {
|
|
2600
|
+
fillContent(visualLayer, content);
|
|
2601
|
+
return this;
|
|
2602
|
+
},
|
|
2603
|
+
setTextRatio(ratio) {
|
|
2604
|
+
const value = Number(ratio);
|
|
2605
|
+
if (!Number.isFinite(value) || value < 0 || value > 100) return this;
|
|
2606
|
+
dirty.textRatio = true;
|
|
2607
|
+
merged.textRatio = value;
|
|
2608
|
+
root.style.setProperty('--hj-text-width', `${value}%`);
|
|
2609
|
+
return this;
|
|
2610
|
+
},
|
|
2611
|
+
setCssVars(vars) {
|
|
2612
|
+
if (!vars || typeof vars !== 'object') return this;
|
|
2613
|
+
dirty.cssVars = true;
|
|
2614
|
+
merged.cssVars = { ...(merged.cssVars || {}), ...vars };
|
|
2615
|
+
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
2616
|
+
return this;
|
|
2617
|
+
},
|
|
2618
|
+
setLabel(label) {
|
|
2619
|
+
customLabel = typeof label === 'string' && label ? label : null;
|
|
2620
|
+
updateAria();
|
|
2621
|
+
return this;
|
|
2622
|
+
},
|
|
2623
|
+
setValueSuffix(suffix) {
|
|
2624
|
+
controller.setValueSuffix(suffix);
|
|
2625
|
+
return this;
|
|
2626
|
+
},
|
|
2222
2627
|
setColors(colors) {
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
controller.
|
|
2227
|
-
|
|
2628
|
+
const next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
|
|
2629
|
+
if (next.length !== 4 || next.some((color) => !color)) return this;
|
|
2630
|
+
dirty.colors = true;
|
|
2631
|
+
controller.setColors(next);
|
|
2632
|
+
if (overlay) overlay.setColors(next);
|
|
2633
|
+
controller.resizeCanvas();
|
|
2634
|
+
return this;
|
|
2228
2635
|
},
|
|
2229
2636
|
setSize(width, height) {
|
|
2230
2637
|
if (width !== undefined) merged.width = width;
|
|
@@ -2251,14 +2658,18 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2251
2658
|
controller.resizeCanvas();
|
|
2252
2659
|
return this;
|
|
2253
2660
|
},
|
|
2254
|
-
dispose() {
|
|
2255
|
-
|
|
2661
|
+
dispose() {
|
|
2662
|
+
disposed = true;
|
|
2663
|
+
unsubscribe();
|
|
2256
2664
|
visibility.dispose();
|
|
2257
2665
|
if (overlay) overlay.dispose();
|
|
2258
|
-
controller.dispose();
|
|
2259
|
-
root.remove();
|
|
2260
|
-
}
|
|
2261
|
-
|
|
2666
|
+
controller.dispose();
|
|
2667
|
+
root.remove();
|
|
2668
|
+
},
|
|
2669
|
+
textRatio: merged.textRatio,
|
|
2670
|
+
cssVars: merged.cssVars,
|
|
2671
|
+
dirty
|
|
2672
|
+
};
|
|
2262
2673
|
}
|
|
2263
2674
|
|
|
2264
2675
|
function prefersReducedMotion() {
|
|
@@ -2284,7 +2695,7 @@ function createColorBackground(host, options = {}) {
|
|
|
2284
2695
|
}
|
|
2285
2696
|
|
|
2286
2697
|
const preset = { ...getPreset('capsule', options.preset ?? '初光') };
|
|
2287
|
-
const merged = normalizeOptions(
|
|
2698
|
+
const merged = normalizeOptions(
|
|
2288
2699
|
{
|
|
2289
2700
|
quality: DEFAULTS.capsule.quality,
|
|
2290
2701
|
renderer: DEFAULTS.capsule.renderer,
|
|
@@ -2294,8 +2705,12 @@ function createColorBackground(host, options = {}) {
|
|
|
2294
2705
|
fallbackColor: true
|
|
2295
2706
|
},
|
|
2296
2707
|
preset,
|
|
2297
|
-
options
|
|
2298
|
-
);
|
|
2708
|
+
options
|
|
2709
|
+
);
|
|
2710
|
+
const normalizedColors = merged.colors.map(normalizeColor);
|
|
2711
|
+
if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
|
|
2712
|
+
preset.seed = merged.seed;
|
|
2713
|
+
preset.speed = merged.speed;
|
|
2299
2714
|
|
|
2300
2715
|
const computed = getComputedStyle(host);
|
|
2301
2716
|
if (computed.position === 'static' || computed.position === '') {
|
|
@@ -2324,8 +2739,18 @@ function createColorBackground(host, options = {}) {
|
|
|
2324
2739
|
layer.appendChild(canvas);
|
|
2325
2740
|
host.appendChild(layer);
|
|
2326
2741
|
|
|
2327
|
-
const emitter = createEmitter();
|
|
2328
|
-
let paused = merged.respectReducedMotion && prefersReducedMotion();
|
|
2742
|
+
const emitter = createEmitter();
|
|
2743
|
+
let paused = merged.respectReducedMotion && prefersReducedMotion();
|
|
2744
|
+
let disposed = false;
|
|
2745
|
+
const dirty = {
|
|
2746
|
+
preset: false,
|
|
2747
|
+
seed: false,
|
|
2748
|
+
speed: false,
|
|
2749
|
+
colors: false,
|
|
2750
|
+
opacity: false,
|
|
2751
|
+
fallbackColor: false,
|
|
2752
|
+
mouseColor: false
|
|
2753
|
+
};
|
|
2329
2754
|
|
|
2330
2755
|
let renderer;
|
|
2331
2756
|
const useWebgl = merged.renderer !== 'canvas2d';
|
|
@@ -2338,9 +2763,12 @@ function createColorBackground(host, options = {}) {
|
|
|
2338
2763
|
});
|
|
2339
2764
|
} catch (error) {
|
|
2340
2765
|
renderer = new FallbackRenderer(canvas, merged);
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2766
|
+
nextTick(() => {
|
|
2767
|
+
if (disposed) return;
|
|
2768
|
+
emitter.emit('error', {
|
|
2769
|
+
message: String(error && error.message ? error.message : error)
|
|
2770
|
+
});
|
|
2771
|
+
});
|
|
2344
2772
|
}
|
|
2345
2773
|
} else {
|
|
2346
2774
|
renderer = new FallbackRenderer(canvas, merged);
|
|
@@ -2364,7 +2792,10 @@ function createColorBackground(host, options = {}) {
|
|
|
2364
2792
|
() => paused
|
|
2365
2793
|
);
|
|
2366
2794
|
|
|
2367
|
-
|
|
2795
|
+
nextTick(() => {
|
|
2796
|
+
if (disposed) return;
|
|
2797
|
+
emitter.emit('ready', { preset: { ...preset } });
|
|
2798
|
+
});
|
|
2368
2799
|
|
|
2369
2800
|
return {
|
|
2370
2801
|
element: host,
|
|
@@ -2373,38 +2804,72 @@ function createColorBackground(host, options = {}) {
|
|
|
2373
2804
|
preset,
|
|
2374
2805
|
on: emitter.on,
|
|
2375
2806
|
off: emitter.off,
|
|
2376
|
-
setPreset(ref) {
|
|
2377
|
-
const next = getPreset('capsule', ref);
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
return this;
|
|
2391
|
-
|
|
2807
|
+
setPreset(ref) {
|
|
2808
|
+
const next = getPreset('capsule', ref);
|
|
2809
|
+
dirty.preset = true;
|
|
2810
|
+
Object.assign(preset, next);
|
|
2811
|
+
const nextColors = preset.colors.map(normalizeColor);
|
|
2812
|
+
if (nextColors.every(Boolean)) preset.colors = nextColors;
|
|
2813
|
+
renderer.setPreset({ ...preset });
|
|
2814
|
+
renderer.resize();
|
|
2815
|
+
syncLayerVars();
|
|
2816
|
+
emitter.emit('presetchange', { preset: { ...preset } });
|
|
2817
|
+
return this;
|
|
2818
|
+
},
|
|
2819
|
+
setColors(colors) {
|
|
2820
|
+
const next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
|
|
2821
|
+
if (next.length !== 4 || next.some((color) => !color)) return this;
|
|
2822
|
+
dirty.colors = true;
|
|
2823
|
+
preset.colors = next;
|
|
2824
|
+
renderer.setPreset({ ...preset, colors: next });
|
|
2825
|
+
syncLayerVars();
|
|
2826
|
+
return this;
|
|
2827
|
+
},
|
|
2828
|
+
setSeed(seed) {
|
|
2829
|
+
const value = Number(seed);
|
|
2830
|
+
if (!Number.isFinite(value)) return this;
|
|
2831
|
+
dirty.seed = true;
|
|
2832
|
+
preset.seed = value;
|
|
2833
|
+
renderer.setPreset({ ...preset });
|
|
2834
|
+
return this;
|
|
2835
|
+
},
|
|
2836
|
+
setSpeed(speed) {
|
|
2837
|
+
const value = Number(speed);
|
|
2838
|
+
if (!Number.isFinite(value)) return this;
|
|
2839
|
+
dirty.speed = true;
|
|
2840
|
+
preset.speed = value;
|
|
2841
|
+
renderer.setPreset({ ...preset });
|
|
2842
|
+
return this;
|
|
2843
|
+
},
|
|
2844
|
+
setFallbackColor(value) {
|
|
2845
|
+
dirty.fallbackColor = true;
|
|
2846
|
+
merged.fallbackColor = value;
|
|
2847
|
+
syncLayerVars();
|
|
2848
|
+
return this;
|
|
2849
|
+
},
|
|
2850
|
+
setMouseColor(value) {
|
|
2851
|
+
dirty.mouseColor = true;
|
|
2852
|
+
merged.mouseColor = value !== false;
|
|
2853
|
+
if (typeof renderer.setMouseColor === 'function') renderer.setMouseColor(merged.mouseColor);
|
|
2854
|
+
return this;
|
|
2855
|
+
},
|
|
2392
2856
|
setQuality(quality) {
|
|
2393
2857
|
merged.quality = quality;
|
|
2394
2858
|
if (typeof renderer.setDprCap === 'function') renderer.setDprCap(dprCapFor(quality));
|
|
2395
2859
|
return this;
|
|
2396
2860
|
},
|
|
2397
|
-
setOpacity(value) {
|
|
2398
|
-
const opacity = Number(value);
|
|
2399
|
-
if (!Number.isFinite(opacity)) return this;
|
|
2400
|
-
|
|
2861
|
+
setOpacity(value) {
|
|
2862
|
+
const opacity = Number(value);
|
|
2863
|
+
if (!Number.isFinite(opacity)) return this;
|
|
2864
|
+
dirty.opacity = true;
|
|
2865
|
+
merged.opacity = Math.min(1, Math.max(0, opacity));
|
|
2401
2866
|
layer.style.setProperty('--dlc-color-opacity', String(merged.opacity));
|
|
2402
2867
|
return this;
|
|
2403
2868
|
},
|
|
2404
|
-
randomize() {
|
|
2405
|
-
|
|
2406
|
-
return this;
|
|
2407
|
-
},
|
|
2869
|
+
randomize() {
|
|
2870
|
+
this.setSeed(Math.random() * 100);
|
|
2871
|
+
return this;
|
|
2872
|
+
},
|
|
2408
2873
|
pause() {
|
|
2409
2874
|
paused = true;
|
|
2410
2875
|
return this;
|
|
@@ -2413,15 +2878,20 @@ function createColorBackground(host, options = {}) {
|
|
|
2413
2878
|
paused = false;
|
|
2414
2879
|
return this;
|
|
2415
2880
|
},
|
|
2416
|
-
dispose() {
|
|
2417
|
-
|
|
2881
|
+
dispose() {
|
|
2882
|
+
disposed = true;
|
|
2883
|
+
unsubscribe();
|
|
2418
2884
|
visibility.dispose();
|
|
2419
2885
|
if (resizeObserver) resizeObserver.disconnect();
|
|
2420
2886
|
else window.removeEventListener('resize', renderer.resize);
|
|
2421
|
-
renderer.dispose();
|
|
2422
|
-
layer.remove();
|
|
2423
|
-
}
|
|
2424
|
-
|
|
2887
|
+
renderer.dispose();
|
|
2888
|
+
layer.remove();
|
|
2889
|
+
},
|
|
2890
|
+
opacity: merged.opacity,
|
|
2891
|
+
fallbackColor: merged.fallbackColor,
|
|
2892
|
+
mouseColor: merged.mouseColor,
|
|
2893
|
+
dirty
|
|
2894
|
+
};
|
|
2425
2895
|
}
|
|
2426
2896
|
|
|
2427
|
-
export { ALL_PRESETS, COPY, CosmicRenderer, DEFAULTS, FallbackRenderer, PRESETS, PROGRESS_PRESETS, ProgressCapsuleController, ProgressFlowRenderer, QUALITY_TIERS, createCapsule, createColorBackground, createProgressCapsule, dprCapFor, getPreset, hexToRgb01$1 as hexToRgb01, hexToRgba, parseSize, pauseAll, resumeAll, validateColors };
|
|
2897
|
+
export { ALL_PRESETS, COPY, CosmicRenderer, DEFAULTS, FallbackRenderer, PRESETS, PROGRESS_PRESETS, ProgressCapsuleController, ProgressFlowRenderer, QUALITY_TIERS, createCapsule, createColorBackground, createProgressCapsule, dprCapFor, getPreset, hexToRgb01$1 as hexToRgb01, hexToRgba, normalizeColor, parseSize, pauseAll, resumeAll, validateColors };
|