@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/vue3.mjs
CHANGED
|
@@ -184,60 +184,266 @@ function getPreset(kind, ref) {
|
|
|
184
184
|
return found;
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
const DEFAULTS = {
|
|
188
|
-
capsule: {
|
|
189
|
-
width: '100%',
|
|
190
|
-
height: 160,
|
|
187
|
+
const DEFAULTS = {
|
|
188
|
+
capsule: {
|
|
189
|
+
width: '100%',
|
|
190
|
+
height: 160,
|
|
191
191
|
quality: 'auto',
|
|
192
192
|
renderer: 'auto',
|
|
193
193
|
respectReducedMotion: true,
|
|
194
194
|
mouseColor: true,
|
|
195
|
-
|
|
196
|
-
},
|
|
197
|
-
progress: {
|
|
198
|
-
width: 454,
|
|
199
|
-
height: 104,
|
|
200
|
-
min: 0,
|
|
195
|
+
textRatio: 39
|
|
196
|
+
},
|
|
197
|
+
progress: {
|
|
198
|
+
width: 454,
|
|
199
|
+
height: 104,
|
|
200
|
+
min: 0,
|
|
201
201
|
max: 100,
|
|
202
|
-
draggable: true,
|
|
203
|
-
keyboard: true,
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
202
|
+
draggable: true,
|
|
203
|
+
keyboard: true,
|
|
204
|
+
disabled: false,
|
|
205
|
+
valueSuffix: '%',
|
|
206
|
+
edgeStyle: 'flow',
|
|
207
|
+
quality: 'auto',
|
|
208
|
+
renderer: 'auto',
|
|
209
|
+
textRatio: 54,
|
|
210
|
+
showValue: true,
|
|
211
|
+
respectReducedMotion: true
|
|
212
|
+
}
|
|
210
213
|
};
|
|
211
214
|
|
|
212
|
-
/**
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
valueSuffix: '%',
|
|
215
|
+
/**
|
|
216
|
+
* Internal accessibility labels and small UI text. Since the copy API was
|
|
217
|
+
* removed (slots own the text area), these are no longer user-overridable.
|
|
218
|
+
* Templates use {brand} {code} {name} placeholders.
|
|
219
|
+
*/
|
|
220
|
+
const COPY = {
|
|
221
|
+
brandName: '画境观屿',
|
|
222
|
+
valueSuffix: '%',
|
|
220
223
|
progressAria: '{brand} {code} 加载进度',
|
|
221
224
|
capsuleAria: '打开 {name} 沉浸预览'
|
|
222
225
|
};
|
|
223
226
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
227
|
+
/**
|
|
228
|
+
* Color helpers. `normalizeColor` converts any supported CSS color
|
|
229
|
+
* (hex / named / rgb() / rgba()) into a 6-digit hex string, so renderers can
|
|
230
|
+
* keep working with #rrggbb only. rgba() alpha is intentionally dropped:
|
|
231
|
+
* the WebGL shader has no per-color alpha channel, and the component is
|
|
232
|
+
* opaque by design — use component-level `opacity` for transparency.
|
|
233
|
+
*/
|
|
234
|
+
|
|
235
|
+
const NAMED_COLORS = {
|
|
236
|
+
aliceblue: '#f0f8ff',
|
|
237
|
+
antiquewhite: '#faebd7',
|
|
238
|
+
aqua: '#00ffff',
|
|
239
|
+
aquamarine: '#7fffd4',
|
|
240
|
+
azure: '#f0ffff',
|
|
241
|
+
beige: '#f5f5dc',
|
|
242
|
+
bisque: '#ffe4c4',
|
|
243
|
+
black: '#000000',
|
|
244
|
+
blanchedalmond: '#ffebcd',
|
|
245
|
+
blue: '#0000ff',
|
|
246
|
+
blueviolet: '#8a2be2',
|
|
247
|
+
brown: '#a52a2a',
|
|
248
|
+
burlywood: '#deb887',
|
|
249
|
+
cadetblue: '#5f9ea0',
|
|
250
|
+
chartreuse: '#7fff00',
|
|
251
|
+
chocolate: '#d2691e',
|
|
252
|
+
coral: '#ff7f50',
|
|
253
|
+
cornflowerblue: '#6495ed',
|
|
254
|
+
cornsilk: '#fff8dc',
|
|
255
|
+
crimson: '#dc143c',
|
|
256
|
+
cyan: '#00ffff',
|
|
257
|
+
darkblue: '#00008b',
|
|
258
|
+
darkcyan: '#008b8b',
|
|
259
|
+
darkgoldenrod: '#b8860b',
|
|
260
|
+
darkgray: '#a9a9a9',
|
|
261
|
+
darkgreen: '#006400',
|
|
262
|
+
darkgrey: '#a9a9a9',
|
|
263
|
+
darkkhaki: '#bdb76b',
|
|
264
|
+
darkmagenta: '#8b008b',
|
|
265
|
+
darkolivegreen: '#556b2f',
|
|
266
|
+
darkorange: '#ff8c00',
|
|
267
|
+
darkorchid: '#9932cc',
|
|
268
|
+
darkred: '#8b0000',
|
|
269
|
+
darksalmon: '#e9967a',
|
|
270
|
+
darkseagreen: '#8fbc8f',
|
|
271
|
+
darkslateblue: '#483d8b',
|
|
272
|
+
darkslategray: '#2f4f4f',
|
|
273
|
+
darkslategrey: '#2f4f4f',
|
|
274
|
+
darkturquoise: '#00ced1',
|
|
275
|
+
darkviolet: '#9400d3',
|
|
276
|
+
deeppink: '#ff1493',
|
|
277
|
+
deepskyblue: '#00bfff',
|
|
278
|
+
dimgray: '#696969',
|
|
279
|
+
dimgrey: '#696969',
|
|
280
|
+
dodgerblue: '#1e90ff',
|
|
281
|
+
firebrick: '#b22222',
|
|
282
|
+
floralwhite: '#fffaf0',
|
|
283
|
+
forestgreen: '#228b22',
|
|
284
|
+
fuchsia: '#ff00ff',
|
|
285
|
+
gainsboro: '#dcdcdc',
|
|
286
|
+
ghostwhite: '#f8f8ff',
|
|
287
|
+
gold: '#ffd700',
|
|
288
|
+
goldenrod: '#daa520',
|
|
289
|
+
gray: '#808080',
|
|
290
|
+
green: '#008000',
|
|
291
|
+
greenyellow: '#adff2f',
|
|
292
|
+
grey: '#808080',
|
|
293
|
+
honeydew: '#f0fff0',
|
|
294
|
+
hotpink: '#ff69b4',
|
|
295
|
+
indianred: '#cd5c5c',
|
|
296
|
+
indigo: '#4b0082',
|
|
297
|
+
ivory: '#fffff0',
|
|
298
|
+
khaki: '#f0e68c',
|
|
299
|
+
lavender: '#e6e6fa',
|
|
300
|
+
lavenderblush: '#fff0f5',
|
|
301
|
+
lawngreen: '#7cfc00',
|
|
302
|
+
lemonchiffon: '#fffacd',
|
|
303
|
+
lightblue: '#add8e6',
|
|
304
|
+
lightcoral: '#f08080',
|
|
305
|
+
lightcyan: '#e0ffff',
|
|
306
|
+
lightgoldenrodyellow: '#fafad2',
|
|
307
|
+
lightgray: '#d3d3d3',
|
|
308
|
+
lightgreen: '#90ee90',
|
|
309
|
+
lightgrey: '#d3d3d3',
|
|
310
|
+
lightpink: '#ffb6c1',
|
|
311
|
+
lightsalmon: '#ffa07a',
|
|
312
|
+
lightseagreen: '#20b2aa',
|
|
313
|
+
lightskyblue: '#87cefa',
|
|
314
|
+
lightslategray: '#778899',
|
|
315
|
+
lightslategrey: '#778899',
|
|
316
|
+
lightsteelblue: '#b0c4de',
|
|
317
|
+
lightyellow: '#ffffe0',
|
|
318
|
+
lime: '#00ff00',
|
|
319
|
+
limegreen: '#32cd32',
|
|
320
|
+
linen: '#faf0e6',
|
|
321
|
+
magenta: '#ff00ff',
|
|
322
|
+
maroon: '#800000',
|
|
323
|
+
mediumaquamarine: '#66cdaa',
|
|
324
|
+
mediumblue: '#0000cd',
|
|
325
|
+
mediumorchid: '#ba55d3',
|
|
326
|
+
mediumpurple: '#9370db',
|
|
327
|
+
mediumseagreen: '#3cb371',
|
|
328
|
+
mediumslateblue: '#7b68ee',
|
|
329
|
+
mediumspringgreen: '#00fa9a',
|
|
330
|
+
mediumturquoise: '#48d1cc',
|
|
331
|
+
mediumvioletred: '#c71585',
|
|
332
|
+
midnightblue: '#191970',
|
|
333
|
+
mintcream: '#f5fffa',
|
|
334
|
+
mistyrose: '#ffe4e1',
|
|
335
|
+
moccasin: '#ffe4b5',
|
|
336
|
+
navajowhite: '#ffdead',
|
|
337
|
+
navy: '#000080',
|
|
338
|
+
oldlace: '#fdf5e6',
|
|
339
|
+
olive: '#808000',
|
|
340
|
+
olivedrab: '#6b8e23',
|
|
341
|
+
orange: '#ffa500',
|
|
342
|
+
orangered: '#ff4500',
|
|
343
|
+
orchid: '#da70d6',
|
|
344
|
+
palegoldenrod: '#eee8aa',
|
|
345
|
+
palegreen: '#98fb98',
|
|
346
|
+
paleturquoise: '#afeeee',
|
|
347
|
+
palevioletred: '#db7093',
|
|
348
|
+
papayawhip: '#ffefd5',
|
|
349
|
+
peachpuff: '#ffdab9',
|
|
350
|
+
peru: '#cd853f',
|
|
351
|
+
pink: '#ffc0cb',
|
|
352
|
+
plum: '#dda0dd',
|
|
353
|
+
powderblue: '#b0e0e6',
|
|
354
|
+
purple: '#800080',
|
|
355
|
+
rebeccapurple: '#663399',
|
|
356
|
+
red: '#ff0000',
|
|
357
|
+
rosybrown: '#bc8f8f',
|
|
358
|
+
royalblue: '#4169e1',
|
|
359
|
+
saddlebrown: '#8b4513',
|
|
360
|
+
salmon: '#fa8072',
|
|
361
|
+
sandybrown: '#f4a460',
|
|
362
|
+
seagreen: '#2e8b57',
|
|
363
|
+
seashell: '#fff5ee',
|
|
364
|
+
sienna: '#a0522d',
|
|
365
|
+
silver: '#c0c0c0',
|
|
366
|
+
skyblue: '#87ceeb',
|
|
367
|
+
slateblue: '#6a5acd',
|
|
368
|
+
slategray: '#708090',
|
|
369
|
+
slategrey: '#708090',
|
|
370
|
+
snow: '#fffafa',
|
|
371
|
+
springgreen: '#00ff7f',
|
|
372
|
+
steelblue: '#4682b4',
|
|
373
|
+
tan: '#d2b48c',
|
|
374
|
+
teal: '#008080',
|
|
375
|
+
thistle: '#d8bfd8',
|
|
376
|
+
tomato: '#ff6347',
|
|
377
|
+
turquoise: '#40e0d0',
|
|
378
|
+
violet: '#ee82ee',
|
|
379
|
+
wheat: '#f5deb3',
|
|
380
|
+
white: '#ffffff',
|
|
381
|
+
whitesmoke: '#f5f5f5',
|
|
382
|
+
yellow: '#ffff00',
|
|
383
|
+
yellowgreen: '#9acd32'
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
function clampChannel(value) {
|
|
387
|
+
return Math.min(255, Math.max(0, Math.round(value)));
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function normalizeRgbArgs(args) {
|
|
391
|
+
const parts = args.split(/[,\s/]+/).filter(Boolean);
|
|
392
|
+
if (parts.length < 3) return null;
|
|
393
|
+
const to255 = (value) => {
|
|
394
|
+
if (typeof value !== 'string' || !value) return null;
|
|
395
|
+
if (value.endsWith('%')) return clampChannel((parseFloat(value) / 100) * 255);
|
|
396
|
+
const number = Number(value);
|
|
397
|
+
return Number.isFinite(number) ? clampChannel(number) : null;
|
|
398
|
+
};
|
|
399
|
+
const red = to255(parts[0]);
|
|
400
|
+
const green = to255(parts[1]);
|
|
401
|
+
const blue = to255(parts[2]);
|
|
402
|
+
if (red === null || green === null || blue === null) return null;
|
|
403
|
+
return `#${[red, green, blue].map((n) => n.toString(16).padStart(2, '0')).join('')}`;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Convert any supported CSS color into `#rrggbb`. Returns null when the
|
|
408
|
+
* value cannot be parsed. Supports: #rgb / #rrggbb (case-insensitive),
|
|
409
|
+
* CSS named colors, rgb() / rgba() with comma or modern space syntax,
|
|
410
|
+
* including percentages. Alpha in rgba() is ignored.
|
|
411
|
+
*/
|
|
412
|
+
function normalizeColor(value) {
|
|
413
|
+
if (typeof value !== 'string') return null;
|
|
414
|
+
const input = value.trim();
|
|
415
|
+
if (!input) return null;
|
|
416
|
+
const lower = input.toLowerCase();
|
|
417
|
+
if (NAMED_COLORS[lower]) return NAMED_COLORS[lower];
|
|
418
|
+
if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(input)) {
|
|
419
|
+
const hex = input.slice(1).toLowerCase();
|
|
420
|
+
if (hex.length === 3) return `#${hex.split('').map((c) => c + c).join('')}`;
|
|
421
|
+
return `#${hex}`;
|
|
422
|
+
}
|
|
423
|
+
const match = input.match(/^rgba?\((.*)\)$/i);
|
|
424
|
+
if (match) return normalizeRgbArgs(match[1]);
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function hexToRgb01$1(color) {
|
|
429
|
+
const hex = normalizeColor(color);
|
|
430
|
+
if (!hex) return [0, 0, 0];
|
|
431
|
+
const value = Number.parseInt(hex.slice(1), 16);
|
|
432
|
+
return [
|
|
433
|
+
((value >> 16) & 255) / 255,
|
|
434
|
+
((value >> 8) & 255) / 255,
|
|
435
|
+
(value & 255) / 255
|
|
436
|
+
];
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function hexToRgba(color, alpha = 1) {
|
|
440
|
+
const hex = normalizeColor(color);
|
|
441
|
+
if (!hex) return 'rgba(0, 0, 0, 0)';
|
|
442
|
+
const value = Number.parseInt(hex.slice(1), 16);
|
|
443
|
+
const red = (value >> 16) & 255;
|
|
444
|
+
const green = (value >> 8) & 255;
|
|
445
|
+
const blue = value & 255;
|
|
446
|
+
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
|
241
447
|
}
|
|
242
448
|
|
|
243
449
|
const VERTEX_SHADER$1 = `#version 300 es
|
|
@@ -465,12 +671,31 @@ class CosmicRenderer {
|
|
|
465
671
|
this.timeOffset = preset.seed * 0.73;
|
|
466
672
|
}
|
|
467
673
|
|
|
468
|
-
setDprCap(cap) {
|
|
469
|
-
this.options.dprCap = cap;
|
|
470
|
-
this.resize();
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
|
|
674
|
+
setDprCap(cap) {
|
|
675
|
+
this.options.dprCap = cap;
|
|
676
|
+
this.resize();
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
setMouseColor(enabled) {
|
|
680
|
+
this.options.mouseColor = enabled !== false;
|
|
681
|
+
if (this.options.mouseColor) {
|
|
682
|
+
if (this.onPointerMove) return this;
|
|
683
|
+
this.#bindEvents();
|
|
684
|
+
return this;
|
|
685
|
+
}
|
|
686
|
+
if (this.onPointerMove) {
|
|
687
|
+
const target = this.eventTarget;
|
|
688
|
+
target.removeEventListener('pointermove', this.onPointerMove);
|
|
689
|
+
target.removeEventListener('pointerdown', this.onPointerMove);
|
|
690
|
+
target.removeEventListener('pointerleave', this.onPointerLeave);
|
|
691
|
+
this.onPointerMove = null;
|
|
692
|
+
this.onPointerLeave = null;
|
|
693
|
+
}
|
|
694
|
+
this.motionTarget = 0;
|
|
695
|
+
return this;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
randomize() {
|
|
474
699
|
this.preset.seed = Math.random() * 100;
|
|
475
700
|
this.timeOffset = Math.random() * 40;
|
|
476
701
|
}
|
|
@@ -550,10 +775,12 @@ class FallbackRenderer {
|
|
|
550
775
|
}
|
|
551
776
|
}
|
|
552
777
|
|
|
553
|
-
drawNebula(time) {
|
|
554
|
-
const ctx = this.context;
|
|
555
|
-
const { width, height } = this.canvas;
|
|
556
|
-
const
|
|
778
|
+
drawNebula(time) {
|
|
779
|
+
const ctx = this.context;
|
|
780
|
+
const { width, height } = this.canvas;
|
|
781
|
+
const t = time * (this.preset.speed || 1);
|
|
782
|
+
const phase = (this.preset.seed || 0) * 0.7;
|
|
783
|
+
const gradient = ctx.createLinearGradient(0, 0, width, height);
|
|
557
784
|
gradient.addColorStop(0, this.preset.colors[0]);
|
|
558
785
|
gradient.addColorStop(0.38, this.preset.colors[1]);
|
|
559
786
|
gradient.addColorStop(0.72, this.preset.colors[2]);
|
|
@@ -561,10 +788,10 @@ class FallbackRenderer {
|
|
|
561
788
|
ctx.fillStyle = gradient;
|
|
562
789
|
ctx.fillRect(0, 0, width, height);
|
|
563
790
|
|
|
564
|
-
ctx.globalCompositeOperation = 'screen';
|
|
565
|
-
for (let index = 0; index < 6; index += 1) {
|
|
566
|
-
const x = (0.5 + 0.45 * Math.sin(
|
|
567
|
-
const y = (0.5 + 0.4 * Math.cos(
|
|
791
|
+
ctx.globalCompositeOperation = 'screen';
|
|
792
|
+
for (let index = 0; index < 6; index += 1) {
|
|
793
|
+
const x = (0.5 + 0.45 * Math.sin(t * 0.32 + index * 1.7 + phase)) * width;
|
|
794
|
+
const y = (0.5 + 0.4 * Math.cos(t * 0.25 + index + phase)) * height;
|
|
568
795
|
const radius = Math.max(width, height) * (0.08 + (index % 3) * 0.04);
|
|
569
796
|
const glow = ctx.createRadialGradient(x, y, 0, x, y, radius);
|
|
570
797
|
glow.addColorStop(0, rgb(this.preset.colors[(index + 1) % 4], 0.24));
|
|
@@ -585,8 +812,11 @@ class FallbackRenderer {
|
|
|
585
812
|
this.preset = preset;
|
|
586
813
|
}
|
|
587
814
|
|
|
588
|
-
randomize() {
|
|
589
|
-
|
|
815
|
+
randomize() {
|
|
816
|
+
if (this.preset) this.preset.seed = Math.random() * 100;
|
|
817
|
+
}
|
|
818
|
+
setMouseColor() {}
|
|
819
|
+
dispose() {}
|
|
590
820
|
}
|
|
591
821
|
|
|
592
822
|
/**
|
|
@@ -686,78 +916,105 @@ function createVisibilityGuard(element) {
|
|
|
686
916
|
};
|
|
687
917
|
}
|
|
688
918
|
|
|
689
|
-
|
|
919
|
+
/**
|
|
920
|
+
* Run a callback asynchronously, right after the current task and before the
|
|
921
|
+
* next paint. Used for mount-time events (ready / error) so listeners that
|
|
922
|
+
* are attached after create() still receive them. Falls back for legacy
|
|
923
|
+
* browsers that lack queueMicrotask / Promise.
|
|
924
|
+
*/
|
|
925
|
+
function nextTick(fn) {
|
|
926
|
+
if (typeof queueMicrotask === 'function') {
|
|
927
|
+
queueMicrotask(fn);
|
|
928
|
+
} else if (typeof Promise !== 'undefined' && typeof Promise.resolve === 'function') {
|
|
929
|
+
Promise.resolve().then(fn);
|
|
930
|
+
} else {
|
|
931
|
+
setTimeout(fn, 0);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function prefersReducedMotion$2() {
|
|
690
936
|
return typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
691
937
|
}
|
|
692
938
|
|
|
693
939
|
/**
|
|
694
|
-
* Mount a cosmic (nebula) capsule into `container`.
|
|
695
|
-
*
|
|
696
|
-
* Options: preset (
|
|
697
|
-
*
|
|
698
|
-
*
|
|
699
|
-
|
|
700
|
-
|
|
940
|
+
* Mount a cosmic (nebula) capsule into `container`.
|
|
941
|
+
*
|
|
942
|
+
* Options: preset (literary name), width, height (number=px or string with
|
|
943
|
+
* px/%/vw/vh/em/rem), colors, seed, speed, textRatio (0-100, text region
|
|
944
|
+
* width in percent), text (HTML string or DOM nodes for the text slot),
|
|
945
|
+
* colorContent (HTML string or DOM nodes for the color slot), quality,
|
|
946
|
+
* renderer, respectReducedMotion, mouseColor, cssVars.
|
|
947
|
+
*/
|
|
948
|
+
function createCapsule(container, options = {}) {
|
|
701
949
|
if (!container || typeof container.appendChild !== 'function') {
|
|
702
950
|
throw new Error('createCapsule: container element is required');
|
|
703
951
|
}
|
|
704
952
|
|
|
705
953
|
const preset = { ...getPreset('capsule', options.preset ?? '初光') };
|
|
706
|
-
const merged = normalizeOptions(DEFAULTS.capsule, preset, options);
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
root
|
|
714
|
-
root.
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
};
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
if (
|
|
731
|
-
if (
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
canvas.
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
root.appendChild(canvas);
|
|
757
|
-
container.appendChild(root);
|
|
758
|
-
|
|
759
|
-
const emitter = createEmitter();
|
|
760
|
-
let paused = merged.respectReducedMotion && prefersReducedMotion$
|
|
954
|
+
const merged = normalizeOptions(DEFAULTS.capsule, preset, options);
|
|
955
|
+
const normalizedColors = merged.colors.map(normalizeColor);
|
|
956
|
+
if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
|
|
957
|
+
preset.seed = merged.seed;
|
|
958
|
+
preset.speed = merged.speed;
|
|
959
|
+
let customLabel = typeof options.label === 'string' && options.label ? options.label : null;
|
|
960
|
+
|
|
961
|
+
const root = document.createElement('div');
|
|
962
|
+
root.className = 'hj-capsule-root hj-capsule-cosmic';
|
|
963
|
+
root.dataset.group = preset.group;
|
|
964
|
+
root.dataset.mode = 'nebula';
|
|
965
|
+
root.dataset.theme = preset.theme || 'light';
|
|
966
|
+
root.setAttribute('role', 'img');
|
|
967
|
+
const updateAria = () => {
|
|
968
|
+
root.setAttribute('aria-label', customLabel || COPY.capsuleAria.replace('{name}', preset.name));
|
|
969
|
+
};
|
|
970
|
+
updateAria();
|
|
971
|
+
|
|
972
|
+
// Slot containers are always present but transparent by default: without
|
|
973
|
+
// content they are invisible, so "no slot" behaves like the old
|
|
974
|
+
// showCopy=false. They only provide geometry — no typography, padding or
|
|
975
|
+
// background is imposed on user content (see docs: 插槽 CSS 约定).
|
|
976
|
+
const fillContent = (layer, content) => {
|
|
977
|
+
layer.innerHTML = '';
|
|
978
|
+
if (content == null) return;
|
|
979
|
+
if (typeof content === 'string') {
|
|
980
|
+
layer.innerHTML = content;
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
const nodes = Array.isArray(content) ? content : [content];
|
|
984
|
+
for (const node of nodes) {
|
|
985
|
+
if (node && typeof node.nodeType === 'number') layer.appendChild(node);
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
const textLayer = document.createElement('div');
|
|
990
|
+
textLayer.className = 'hj-capsule-text hj-capsule-copy';
|
|
991
|
+
|
|
992
|
+
const visualLayer = document.createElement('div');
|
|
993
|
+
visualLayer.className = 'hj-capsule-visual';
|
|
994
|
+
|
|
995
|
+
fillContent(textLayer, options.text);
|
|
996
|
+
fillContent(visualLayer, options.colorContent);
|
|
997
|
+
|
|
998
|
+
const canvas = document.createElement('canvas');
|
|
999
|
+
canvas.className = 'hj-capsule-canvas';
|
|
1000
|
+
canvas.setAttribute('aria-hidden', 'true');
|
|
1001
|
+
|
|
1002
|
+
root.appendChild(textLayer);
|
|
1003
|
+
root.appendChild(visualLayer);
|
|
1004
|
+
root.appendChild(canvas);
|
|
1005
|
+
container.appendChild(root);
|
|
1006
|
+
|
|
1007
|
+
const emitter = createEmitter();
|
|
1008
|
+
let paused = merged.respectReducedMotion && prefersReducedMotion$2();
|
|
1009
|
+
let disposed = false;
|
|
1010
|
+
const dirty = {
|
|
1011
|
+
preset: false,
|
|
1012
|
+
seed: false,
|
|
1013
|
+
speed: false,
|
|
1014
|
+
colors: false,
|
|
1015
|
+
textRatio: false,
|
|
1016
|
+
cssVars: false
|
|
1017
|
+
};
|
|
761
1018
|
|
|
762
1019
|
let renderer;
|
|
763
1020
|
const useWebgl = merged.renderer !== 'canvas2d';
|
|
@@ -766,19 +1023,25 @@ function createCapsule(container, options = {}) {
|
|
|
766
1023
|
renderer = new CosmicRenderer(canvas, merged, { dprCap: dprCapFor(merged.quality) });
|
|
767
1024
|
} catch (error) {
|
|
768
1025
|
renderer = new FallbackRenderer(canvas, merged);
|
|
769
|
-
|
|
1026
|
+
nextTick(() => {
|
|
1027
|
+
if (disposed) return;
|
|
1028
|
+
emitter.emit('error', { message: String(error && error.message ? error.message : error) });
|
|
1029
|
+
});
|
|
770
1030
|
}
|
|
771
1031
|
} else {
|
|
772
1032
|
renderer = new FallbackRenderer(canvas, merged);
|
|
773
1033
|
}
|
|
774
1034
|
|
|
775
|
-
const applySize = () => {
|
|
776
|
-
root.style.width = parseSize(merged.width);
|
|
777
|
-
root.style.height = parseSize(merged.height);
|
|
778
|
-
const vars = merged.cssVars || {};
|
|
779
|
-
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
780
|
-
|
|
781
|
-
|
|
1035
|
+
const applySize = () => {
|
|
1036
|
+
root.style.width = parseSize(merged.width);
|
|
1037
|
+
root.style.height = parseSize(merged.height);
|
|
1038
|
+
const vars = merged.cssVars || {};
|
|
1039
|
+
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
1040
|
+
if (merged.textRatio !== undefined) {
|
|
1041
|
+
root.style.setProperty('--hj-text-width', `${merged.textRatio}%`);
|
|
1042
|
+
}
|
|
1043
|
+
renderer.resize();
|
|
1044
|
+
};
|
|
782
1045
|
applySize();
|
|
783
1046
|
|
|
784
1047
|
const resizeObserver = typeof ResizeObserver !== 'undefined'
|
|
@@ -807,38 +1070,47 @@ function createCapsule(container, options = {}) {
|
|
|
807
1070
|
() => paused
|
|
808
1071
|
);
|
|
809
1072
|
|
|
810
|
-
|
|
1073
|
+
nextTick(() => {
|
|
1074
|
+
if (disposed) return;
|
|
1075
|
+
emitter.emit('ready', { preset: { ...preset } });
|
|
1076
|
+
});
|
|
811
1077
|
|
|
812
|
-
const
|
|
813
|
-
root.dataset.mode = 'nebula';
|
|
814
|
-
root.dataset.theme = preset.theme || 'light';
|
|
815
|
-
|
|
816
|
-
};
|
|
817
|
-
|
|
818
|
-
return {
|
|
1078
|
+
const syncTheme = () => {
|
|
1079
|
+
root.dataset.mode = 'nebula';
|
|
1080
|
+
root.dataset.theme = preset.theme || 'light';
|
|
1081
|
+
updateAria();
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
return {
|
|
819
1085
|
element: root,
|
|
820
1086
|
canvas,
|
|
821
1087
|
preset,
|
|
822
1088
|
on: emitter.on,
|
|
823
1089
|
off: emitter.off,
|
|
824
|
-
setPreset(ref) {
|
|
825
|
-
const next = getPreset('capsule', ref);
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
1090
|
+
setPreset(ref) {
|
|
1091
|
+
const next = getPreset('capsule', ref);
|
|
1092
|
+
dirty.preset = true;
|
|
1093
|
+
Object.assign(preset, next);
|
|
1094
|
+
const nextColors = preset.colors.map(normalizeColor);
|
|
1095
|
+
if (nextColors.every(Boolean)) preset.colors = nextColors;
|
|
1096
|
+
renderer.setPreset({ ...preset });
|
|
1097
|
+
syncTheme();
|
|
1098
|
+
renderer.resize();
|
|
1099
|
+
emitter.emit('presetchange', { preset: { ...next } });
|
|
1100
|
+
return this;
|
|
832
1101
|
},
|
|
833
1102
|
setColors(colors) {
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1103
|
+
const next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
|
|
1104
|
+
if (next.length !== 4 || next.some((color) => !color)) return this;
|
|
1105
|
+
dirty.colors = true;
|
|
1106
|
+
preset.colors = next;
|
|
1107
|
+
renderer.setPreset({ ...preset, colors: next });
|
|
837
1108
|
return this;
|
|
838
1109
|
},
|
|
839
1110
|
setSeed(seed) {
|
|
840
1111
|
const value = Number(seed);
|
|
841
1112
|
if (!Number.isFinite(value)) return this;
|
|
1113
|
+
dirty.seed = true;
|
|
842
1114
|
preset.seed = value;
|
|
843
1115
|
renderer.setPreset({ ...preset });
|
|
844
1116
|
return this;
|
|
@@ -846,20 +1118,49 @@ function createCapsule(container, options = {}) {
|
|
|
846
1118
|
setSpeed(speed) {
|
|
847
1119
|
const value = Number(speed);
|
|
848
1120
|
if (!Number.isFinite(value)) return this;
|
|
1121
|
+
dirty.speed = true;
|
|
849
1122
|
preset.speed = value;
|
|
850
1123
|
renderer.setPreset({ ...preset });
|
|
851
1124
|
return this;
|
|
852
1125
|
},
|
|
853
|
-
|
|
1126
|
+
setText(content) {
|
|
1127
|
+
fillContent(textLayer, content);
|
|
1128
|
+
return this;
|
|
1129
|
+
},
|
|
1130
|
+
setColorContent(content) {
|
|
1131
|
+
fillContent(visualLayer, content);
|
|
1132
|
+
return this;
|
|
1133
|
+
},
|
|
1134
|
+
setTextRatio(ratio) {
|
|
1135
|
+
const value = Number(ratio);
|
|
1136
|
+
if (!Number.isFinite(value) || value < 0 || value > 100) return this;
|
|
1137
|
+
dirty.textRatio = true;
|
|
1138
|
+
merged.textRatio = value;
|
|
1139
|
+
root.style.setProperty('--hj-text-width', `${value}%`);
|
|
1140
|
+
return this;
|
|
1141
|
+
},
|
|
1142
|
+
setCssVars(vars) {
|
|
1143
|
+
if (!vars || typeof vars !== 'object') return this;
|
|
1144
|
+
dirty.cssVars = true;
|
|
1145
|
+
merged.cssVars = { ...(merged.cssVars || {}), ...vars };
|
|
1146
|
+
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
1147
|
+
return this;
|
|
1148
|
+
},
|
|
1149
|
+
setLabel(label) {
|
|
1150
|
+
customLabel = typeof label === 'string' && label ? label : null;
|
|
1151
|
+
updateAria();
|
|
1152
|
+
return this;
|
|
1153
|
+
},
|
|
1154
|
+
setSize(width, height) {
|
|
854
1155
|
if (width !== undefined) merged.width = width;
|
|
855
1156
|
if (height !== undefined) merged.height = height;
|
|
856
1157
|
applySize();
|
|
857
1158
|
return this;
|
|
858
1159
|
},
|
|
859
|
-
randomize() {
|
|
860
|
-
|
|
861
|
-
return this;
|
|
862
|
-
},
|
|
1160
|
+
randomize() {
|
|
1161
|
+
this.setSeed(Math.random() * 100);
|
|
1162
|
+
return this;
|
|
1163
|
+
},
|
|
863
1164
|
pause() {
|
|
864
1165
|
paused = true;
|
|
865
1166
|
return this;
|
|
@@ -873,15 +1174,19 @@ function createCapsule(container, options = {}) {
|
|
|
873
1174
|
if (typeof renderer.setDprCap === 'function') renderer.setDprCap(dprCapFor(quality));
|
|
874
1175
|
return this;
|
|
875
1176
|
},
|
|
876
|
-
dispose() {
|
|
877
|
-
|
|
1177
|
+
dispose() {
|
|
1178
|
+
disposed = true;
|
|
1179
|
+
unsubscribe();
|
|
878
1180
|
visibility.dispose();
|
|
879
1181
|
if (resizeObserver) resizeObserver.disconnect();
|
|
880
1182
|
else window.removeEventListener('resize', renderer.resize);
|
|
881
|
-
renderer.dispose();
|
|
882
|
-
root.remove();
|
|
883
|
-
}
|
|
884
|
-
|
|
1183
|
+
renderer.dispose();
|
|
1184
|
+
root.remove();
|
|
1185
|
+
},
|
|
1186
|
+
textRatio: merged.textRatio,
|
|
1187
|
+
cssVars: merged.cssVars,
|
|
1188
|
+
dirty
|
|
1189
|
+
};
|
|
885
1190
|
}
|
|
886
1191
|
|
|
887
1192
|
const PROGRESS_MOTION_WIDTH = 240;
|
|
@@ -1506,7 +1811,7 @@ function interpolate(template, values) {
|
|
|
1506
1811
|
return template.replace(/\{(\w+)\}/g, (_, key) => (values[key] !== undefined ? values[key] : ''));
|
|
1507
1812
|
}
|
|
1508
1813
|
|
|
1509
|
-
function prefersReducedMotion() {
|
|
1814
|
+
function prefersReducedMotion$1() {
|
|
1510
1815
|
return typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
1511
1816
|
}
|
|
1512
1817
|
|
|
@@ -1569,14 +1874,16 @@ const FLOW_PROFILES = {
|
|
|
1569
1874
|
};
|
|
1570
1875
|
|
|
1571
1876
|
class ProgressCapsuleController {
|
|
1572
|
-
constructor({ root, canvas, valueElement, preset, emitter, options, copy }) {
|
|
1877
|
+
constructor({ root, canvas, valueElement, preset, emitter, options, copy, dirty }) {
|
|
1573
1878
|
this.root = root;
|
|
1574
1879
|
this.canvas = canvas;
|
|
1575
1880
|
this.valueElement = valueElement;
|
|
1576
1881
|
this.preset = preset;
|
|
1577
1882
|
this.emitter = emitter;
|
|
1578
|
-
this.options = options;
|
|
1579
|
-
this.copy = copy;
|
|
1883
|
+
this.options = options;
|
|
1884
|
+
this.copy = copy;
|
|
1885
|
+
this.dirty = dirty;
|
|
1886
|
+
this.valueSuffix = options.valueSuffix ?? copy.valueSuffix;
|
|
1580
1887
|
this.profile = preset.edgeStyle === 'tide'
|
|
1581
1888
|
? FLOW_PROFILES.tide
|
|
1582
1889
|
: (FLOW_PROFILES[preset.id] || FLOW_PROFILES['visual-training']);
|
|
@@ -1617,11 +1924,22 @@ class ProgressCapsuleController {
|
|
|
1617
1924
|
this.value = clamp(nextValue, this.min, this.max);
|
|
1618
1925
|
const rounded = Math.round(this.value);
|
|
1619
1926
|
this.root.style.setProperty('--progress', this.value.toFixed(2));
|
|
1620
|
-
this.root.setAttribute('aria-valuenow', String(rounded));
|
|
1621
|
-
this.root.setAttribute('aria-valuetext', `${rounded}${this.
|
|
1622
|
-
this.valueElement.textContent = `${rounded}${this.
|
|
1623
|
-
if (!this.suppressEvents)
|
|
1624
|
-
|
|
1927
|
+
this.root.setAttribute('aria-valuenow', String(rounded));
|
|
1928
|
+
this.root.setAttribute('aria-valuetext', `${rounded}${this.valueSuffix}`);
|
|
1929
|
+
if (this.valueElement) this.valueElement.textContent = `${rounded}${this.valueSuffix}`;
|
|
1930
|
+
if (!this.suppressEvents) {
|
|
1931
|
+
if (this.dirty) this.dirty.value = true;
|
|
1932
|
+
this.emitter.emit('change', { value: this.value, source });
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
setValueSuffix(suffix) {
|
|
1937
|
+
this.valueSuffix = String(suffix == null ? '' : suffix);
|
|
1938
|
+
const rounded = Math.round(this.value);
|
|
1939
|
+
if (this.valueElement) this.valueElement.textContent = `${rounded}${this.valueSuffix}`;
|
|
1940
|
+
this.root.setAttribute('aria-valuetext', `${rounded}${this.valueSuffix}`);
|
|
1941
|
+
return this;
|
|
1942
|
+
}
|
|
1625
1943
|
|
|
1626
1944
|
resizeCanvas() {
|
|
1627
1945
|
const bounds = this.root.getBoundingClientRect();
|
|
@@ -1794,13 +2112,14 @@ class ProgressCapsuleController {
|
|
|
1794
2112
|
if (clamped !== this.value) this.setProgress(clamped, 'prop');
|
|
1795
2113
|
}
|
|
1796
2114
|
|
|
1797
|
-
drawReferenceFlow() {
|
|
2115
|
+
drawReferenceFlow() {
|
|
1798
2116
|
const ctx = this.ctx;
|
|
1799
2117
|
const width = this.width;
|
|
1800
2118
|
const height = this.height;
|
|
1801
2119
|
if (!ctx || width <= 0 || height <= 0) return;
|
|
1802
2120
|
|
|
1803
|
-
const
|
|
2121
|
+
const range = Math.max(this.max - this.min, 1);
|
|
2122
|
+
const shoreline = width * Math.min(Math.max((this.value - this.min) / range, 0), 1);
|
|
1804
2123
|
const time = this.flowTime;
|
|
1805
2124
|
const [dark, accentA, accentB, glow] = this.preset.colors;
|
|
1806
2125
|
|
|
@@ -1918,32 +2237,49 @@ class ProgressCapsuleController {
|
|
|
1918
2237
|
}
|
|
1919
2238
|
}
|
|
1920
2239
|
|
|
1921
|
-
updateFromPointer(event) {
|
|
1922
|
-
const bounds = this.root.getBoundingClientRect();
|
|
1923
|
-
const ratio = bounds.width > 0 ? (event.clientX - bounds.left) / bounds.width : 0;
|
|
1924
|
-
this.setProgress(ratio *
|
|
1925
|
-
}
|
|
1926
|
-
|
|
1927
|
-
beginDrag(event) {
|
|
1928
|
-
if (event.button !== undefined && event.button !== 0) return;
|
|
1929
|
-
event.preventDefault();
|
|
1930
|
-
this.dragging = true;
|
|
1931
|
-
this.
|
|
1932
|
-
|
|
1933
|
-
this.
|
|
1934
|
-
this.
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
this.
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
if (
|
|
1945
|
-
this.
|
|
1946
|
-
|
|
2240
|
+
updateFromPointer(event) {
|
|
2241
|
+
const bounds = this.root.getBoundingClientRect();
|
|
2242
|
+
const ratio = bounds.width > 0 ? (event.clientX - bounds.left) / bounds.width : 0;
|
|
2243
|
+
this.setProgress(this.min + ratio * (this.max - this.min), 'drag');
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
beginDrag(event) {
|
|
2247
|
+
if (event.button !== undefined && event.button !== 0) return;
|
|
2248
|
+
event.preventDefault();
|
|
2249
|
+
this.dragging = true;
|
|
2250
|
+
this.pendingPointer = null;
|
|
2251
|
+
this._dragRaf = 0;
|
|
2252
|
+
this.root.classList.add('is-dragging');
|
|
2253
|
+
try { this.root.setPointerCapture?.(event.pointerId); } catch {}
|
|
2254
|
+
this.emitter.emit('dragstart', { value: this.value });
|
|
2255
|
+
this.updateFromPointer(event);
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
moveDrag(event) {
|
|
2259
|
+
if (!this.dragging) return;
|
|
2260
|
+
event.preventDefault();
|
|
2261
|
+
// rAF 合并:一帧最多一次 setProgress/change,视觉仍每帧更新。
|
|
2262
|
+
this.pendingPointer = event;
|
|
2263
|
+
if (this._dragRaf) return;
|
|
2264
|
+
this._dragRaf = requestAnimationFrame(() => {
|
|
2265
|
+
this._dragRaf = 0;
|
|
2266
|
+
const pending = this.pendingPointer;
|
|
2267
|
+
this.pendingPointer = null;
|
|
2268
|
+
if (pending) this.updateFromPointer(pending);
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
endDrag(event) {
|
|
2273
|
+
if (!this.dragging) return;
|
|
2274
|
+
this.dragging = false;
|
|
2275
|
+
if (this._dragRaf) {
|
|
2276
|
+
cancelAnimationFrame(this._dragRaf);
|
|
2277
|
+
this._dragRaf = 0;
|
|
2278
|
+
}
|
|
2279
|
+
const pending = this.pendingPointer;
|
|
2280
|
+
this.pendingPointer = null;
|
|
2281
|
+
if (pending) this.updateFromPointer(pending);
|
|
2282
|
+
this.root.classList.remove('is-dragging');
|
|
1947
2283
|
try {
|
|
1948
2284
|
if (event?.pointerId !== undefined && this.root.hasPointerCapture?.(event.pointerId)) {
|
|
1949
2285
|
this.root.releasePointerCapture(event.pointerId);
|
|
@@ -1965,15 +2301,16 @@ class ProgressCapsuleController {
|
|
|
1965
2301
|
}
|
|
1966
2302
|
|
|
1967
2303
|
if (this.options.keyboard !== false) {
|
|
1968
|
-
this.handlers.keydown = (event) => {
|
|
1969
|
-
const
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2304
|
+
this.handlers.keydown = (event) => {
|
|
2305
|
+
const step = (this.max - this.min) * 0.02;
|
|
2306
|
+
const actions = {
|
|
2307
|
+
ArrowLeft: -step,
|
|
2308
|
+
ArrowDown: -step,
|
|
2309
|
+
ArrowRight: step,
|
|
2310
|
+
ArrowUp: step,
|
|
2311
|
+
PageDown: -step * 5,
|
|
2312
|
+
PageUp: step * 5
|
|
2313
|
+
};
|
|
1977
2314
|
if (event.key === 'Home') {
|
|
1978
2315
|
event.preventDefault();
|
|
1979
2316
|
this.setProgress(this.min, 'keyboard');
|
|
@@ -2011,8 +2348,9 @@ class ProgressCapsuleController {
|
|
|
2011
2348
|
this.preset = { ...this.preset, colors };
|
|
2012
2349
|
}
|
|
2013
2350
|
|
|
2014
|
-
dispose() {
|
|
2015
|
-
if (this.
|
|
2351
|
+
dispose() {
|
|
2352
|
+
if (this._dragRaf) cancelAnimationFrame(this._dragRaf);
|
|
2353
|
+
if (this.resizeObserver) this.resizeObserver.disconnect();
|
|
2016
2354
|
else window.removeEventListener('resize', this.resizeCanvas);
|
|
2017
2355
|
for (const name of Object.keys(this.handlers)) {
|
|
2018
2356
|
const handler = this.handlers[name];
|
|
@@ -2023,103 +2361,127 @@ class ProgressCapsuleController {
|
|
|
2023
2361
|
}
|
|
2024
2362
|
|
|
2025
2363
|
/**
|
|
2026
|
-
* Mount a fluid progress capsule into `container`.
|
|
2027
|
-
*
|
|
2028
|
-
* Options: preset (id/code/name or object), width, height, value, min, max,
|
|
2029
|
-
* draggable, keyboard, colors, edgeStyle,
|
|
2030
|
-
*
|
|
2031
|
-
|
|
2032
|
-
|
|
2364
|
+
* Mount a fluid progress capsule into `container`.
|
|
2365
|
+
*
|
|
2366
|
+
* Options: preset (id/code/name or object), width, height, value, min, max,
|
|
2367
|
+
* draggable, keyboard, colors, edgeStyle, textRatio (0-100, text region
|
|
2368
|
+
* width in percent), text (HTML string or DOM nodes for the text slot),
|
|
2369
|
+
* colorContent (HTML string or DOM nodes for the color slot),
|
|
2370
|
+
* showValue (show/hide the right-side percentage), quality,
|
|
2371
|
+
* respectReducedMotion, cssVars.
|
|
2372
|
+
*/
|
|
2373
|
+
function createProgressCapsule(container, options = {}) {
|
|
2033
2374
|
if (!container || typeof container.appendChild !== 'function') {
|
|
2034
2375
|
throw new Error('createProgressCapsule: container element is required');
|
|
2035
2376
|
}
|
|
2036
2377
|
|
|
2037
2378
|
const preset = { ...getPreset('progress', options.preset ?? '星火') };
|
|
2038
|
-
const merged = normalizeOptions(DEFAULTS.progress, preset, options);
|
|
2039
|
-
const
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2379
|
+
const merged = normalizeOptions(DEFAULTS.progress, preset, options);
|
|
2380
|
+
const normalizedColors = merged.colors.map(normalizeColor);
|
|
2381
|
+
if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
|
|
2382
|
+
preset.edgeStyle = merged.edgeStyle;
|
|
2383
|
+
const copy = COPY;
|
|
2384
|
+
let customLabel = typeof options.label === 'string' && options.label ? options.label : null;
|
|
2385
|
+
// Vue 的裸布尔属性(<ProgressCapsule disabled />)会传成空字符串,
|
|
2386
|
+
// 这里统一按 true 处理。
|
|
2387
|
+
const disabled = merged.disabled === true || merged.disabled === '' || merged.disabled === 'true';
|
|
2388
|
+
const effectiveDraggable = disabled ? false : merged.draggable !== false;
|
|
2389
|
+
const effectiveKeyboard = disabled ? false : merged.keyboard !== false;
|
|
2390
|
+
const dirty = {
|
|
2391
|
+
preset: false,
|
|
2392
|
+
colors: false,
|
|
2393
|
+
textRatio: false,
|
|
2394
|
+
cssVars: false,
|
|
2395
|
+
edgeStyle: false,
|
|
2396
|
+
value: false
|
|
2397
|
+
};
|
|
2398
|
+
|
|
2399
|
+
const root = document.createElement('div');
|
|
2400
|
+
root.className = 'hj-capsule-root hj-progress-root';
|
|
2401
|
+
root.setAttribute('role', 'slider');
|
|
2402
|
+
root.setAttribute('tabindex', '0');
|
|
2403
|
+
root.setAttribute('data-draggable', String(effectiveDraggable));
|
|
2404
|
+
if (disabled) {
|
|
2405
|
+
root.setAttribute('aria-disabled', 'true');
|
|
2406
|
+
root.classList.add('is-disabled');
|
|
2407
|
+
}
|
|
2046
2408
|
root.setAttribute('aria-valuemin', String(merged.min ?? 0));
|
|
2047
2409
|
root.setAttribute('aria-valuemax', String(merged.max ?? 100));
|
|
2048
2410
|
root.setAttribute('aria-valuenow', String(preset.initialProgress));
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2411
|
+
const updateAria = () => {
|
|
2412
|
+
root.setAttribute(
|
|
2413
|
+
'aria-label',
|
|
2414
|
+
customLabel || interpolate(copy.progressAria, { brand: copy.brandName, code: preset.code, name: preset.name })
|
|
2415
|
+
);
|
|
2416
|
+
};
|
|
2417
|
+
updateAria();
|
|
2418
|
+
if (!effectiveKeyboard) root.setAttribute('tabindex', '-1');
|
|
2054
2419
|
|
|
2055
2420
|
const canvas = document.createElement('canvas');
|
|
2056
2421
|
canvas.className = 'hj-progress-canvas';
|
|
2057
2422
|
canvas.setAttribute('aria-hidden', 'true');
|
|
2058
2423
|
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
const
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
const
|
|
2069
|
-
const
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
const
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
root.style.height = parseSize(merged.height);
|
|
2109
|
-
const vars = merged.cssVars || {};
|
|
2110
|
-
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
2111
|
-
};
|
|
2424
|
+
// Slot containers are always present but transparent by default; they only
|
|
2425
|
+
// provide geometry, never typography/padding/background (docs: 插槽 CSS 约定).
|
|
2426
|
+
const fillContent = (layer, content) => {
|
|
2427
|
+
layer.innerHTML = '';
|
|
2428
|
+
if (content == null) return;
|
|
2429
|
+
if (typeof content === 'string') {
|
|
2430
|
+
layer.innerHTML = content;
|
|
2431
|
+
return;
|
|
2432
|
+
}
|
|
2433
|
+
const nodes = Array.isArray(content) ? content : [content];
|
|
2434
|
+
for (const node of nodes) {
|
|
2435
|
+
if (node && typeof node.nodeType === 'number') layer.appendChild(node);
|
|
2436
|
+
}
|
|
2437
|
+
};
|
|
2438
|
+
|
|
2439
|
+
const textLayer = document.createElement('div');
|
|
2440
|
+
textLayer.className = 'hj-progress-text hj-progress-copy';
|
|
2441
|
+
|
|
2442
|
+
const visualLayer = document.createElement('div');
|
|
2443
|
+
visualLayer.className = 'hj-progress-visual';
|
|
2444
|
+
|
|
2445
|
+
fillContent(textLayer, options.text);
|
|
2446
|
+
fillContent(visualLayer, options.colorContent);
|
|
2447
|
+
|
|
2448
|
+
const valueElement = merged.showValue === false ? null : document.createElement('div');
|
|
2449
|
+
if (valueElement) {
|
|
2450
|
+
valueElement.className = 'hj-progress-value';
|
|
2451
|
+
valueElement.setAttribute('aria-hidden', 'true');
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
root.appendChild(canvas);
|
|
2455
|
+
root.appendChild(textLayer);
|
|
2456
|
+
root.appendChild(visualLayer);
|
|
2457
|
+
if (valueElement) root.appendChild(valueElement);
|
|
2458
|
+
container.appendChild(root);
|
|
2459
|
+
|
|
2460
|
+
const emitter = createEmitter();
|
|
2461
|
+
let paused = merged.respectReducedMotion && prefersReducedMotion$1();
|
|
2462
|
+
let disposed = false;
|
|
2463
|
+
|
|
2464
|
+
const applySize = () => {
|
|
2465
|
+
root.style.width = parseSize(merged.width);
|
|
2466
|
+
root.style.height = parseSize(merged.height);
|
|
2467
|
+
const vars = merged.cssVars || {};
|
|
2468
|
+
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
2469
|
+
if (merged.textRatio !== undefined) {
|
|
2470
|
+
root.style.setProperty('--hj-text-width', `${merged.textRatio}%`);
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
2112
2473
|
applySize();
|
|
2113
2474
|
|
|
2114
|
-
const controller = new ProgressCapsuleController({
|
|
2115
|
-
root,
|
|
2116
|
-
canvas,
|
|
2117
|
-
valueElement,
|
|
2118
|
-
preset,
|
|
2119
|
-
emitter,
|
|
2120
|
-
options: merged,
|
|
2121
|
-
copy
|
|
2122
|
-
|
|
2475
|
+
const controller = new ProgressCapsuleController({
|
|
2476
|
+
root,
|
|
2477
|
+
canvas,
|
|
2478
|
+
valueElement,
|
|
2479
|
+
preset,
|
|
2480
|
+
emitter,
|
|
2481
|
+
options: { ...merged, draggable: effectiveDraggable, keyboard: effectiveKeyboard },
|
|
2482
|
+
copy,
|
|
2483
|
+
dirty
|
|
2484
|
+
});
|
|
2123
2485
|
|
|
2124
2486
|
let overlay = null;
|
|
2125
2487
|
if (merged.renderer !== 'canvas2d') {
|
|
@@ -2130,7 +2492,13 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2130
2492
|
getProgress: () => controller.value
|
|
2131
2493
|
});
|
|
2132
2494
|
}
|
|
2133
|
-
if (overlay) controller.webglActive = true;
|
|
2495
|
+
if (overlay) controller.webglActive = true;
|
|
2496
|
+
else if (merged.renderer !== 'canvas2d') {
|
|
2497
|
+
nextTick(() => {
|
|
2498
|
+
if (disposed) return;
|
|
2499
|
+
emitter.emit('error', { message: 'WebGL2 unavailable, using Canvas2D fallback' });
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2134
2502
|
|
|
2135
2503
|
const visibility = createVisibilityGuard(root);
|
|
2136
2504
|
let flowTime = 0;
|
|
@@ -2146,15 +2514,14 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2146
2514
|
() => paused
|
|
2147
2515
|
);
|
|
2148
2516
|
|
|
2149
|
-
|
|
2517
|
+
nextTick(() => {
|
|
2518
|
+
if (disposed) return;
|
|
2519
|
+
emitter.emit('ready', { preset: { ...preset } });
|
|
2520
|
+
});
|
|
2150
2521
|
|
|
2151
|
-
const syncDom = () => {
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
interpolate(copy.progressAria, { brand: copy.brandName, code: preset.code, name: preset.name })
|
|
2155
|
-
);
|
|
2156
|
-
renderCopy();
|
|
2157
|
-
};
|
|
2522
|
+
const syncDom = () => {
|
|
2523
|
+
updateAria();
|
|
2524
|
+
};
|
|
2158
2525
|
|
|
2159
2526
|
return {
|
|
2160
2527
|
element: root,
|
|
@@ -2175,26 +2542,30 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2175
2542
|
},
|
|
2176
2543
|
setPreset(ref) {
|
|
2177
2544
|
const next = getPreset('progress', ref);
|
|
2545
|
+
dirty.preset = true;
|
|
2178
2546
|
Object.assign(preset, next);
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2547
|
+
const nextColors = preset.colors.map(normalizeColor);
|
|
2548
|
+
if (nextColors.every(Boolean)) preset.colors = nextColors;
|
|
2549
|
+
controller.preset = preset;
|
|
2550
|
+
controller.profile = next.edgeStyle === 'tide'
|
|
2551
|
+
? FLOW_PROFILES.tide
|
|
2552
|
+
: (FLOW_PROFILES[next.id] || FLOW_PROFILES['visual-training']);
|
|
2553
|
+
controller.flowTime = stringSeed(next.id) * 31;
|
|
2554
|
+
controller.seed = stringSeed(`${next.id}-reference`) * Math.PI * 2;
|
|
2555
|
+
syncDom();
|
|
2556
|
+
if (overlay) {
|
|
2557
|
+
overlay.dispose();
|
|
2558
|
+
overlay = attachProgressFlowOverlay({ root, canvas, preset, getProgress: () => controller.value });
|
|
2559
|
+
}
|
|
2560
|
+
controller.webglActive = Boolean(overlay);
|
|
2561
|
+
controller.resizeCanvas();
|
|
2562
|
+
emitter.emit('presetchange', { preset: { ...preset } });
|
|
2193
2563
|
return this;
|
|
2194
2564
|
},
|
|
2195
2565
|
setEdgeStyle(edgeStyle) {
|
|
2196
2566
|
const value = String(edgeStyle || '').toLowerCase();
|
|
2197
2567
|
if (value !== 'flow' && value !== 'tide') return this;
|
|
2568
|
+
dirty.edgeStyle = true;
|
|
2198
2569
|
preset.edgeStyle = value;
|
|
2199
2570
|
controller.profile = value === 'tide'
|
|
2200
2571
|
? FLOW_PROFILES.tide
|
|
@@ -2207,12 +2578,46 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2207
2578
|
controller.resizeCanvas();
|
|
2208
2579
|
return this;
|
|
2209
2580
|
},
|
|
2581
|
+
setText(content) {
|
|
2582
|
+
fillContent(textLayer, content);
|
|
2583
|
+
return this;
|
|
2584
|
+
},
|
|
2585
|
+
setColorContent(content) {
|
|
2586
|
+
fillContent(visualLayer, content);
|
|
2587
|
+
return this;
|
|
2588
|
+
},
|
|
2589
|
+
setTextRatio(ratio) {
|
|
2590
|
+
const value = Number(ratio);
|
|
2591
|
+
if (!Number.isFinite(value) || value < 0 || value > 100) return this;
|
|
2592
|
+
dirty.textRatio = true;
|
|
2593
|
+
merged.textRatio = value;
|
|
2594
|
+
root.style.setProperty('--hj-text-width', `${value}%`);
|
|
2595
|
+
return this;
|
|
2596
|
+
},
|
|
2597
|
+
setCssVars(vars) {
|
|
2598
|
+
if (!vars || typeof vars !== 'object') return this;
|
|
2599
|
+
dirty.cssVars = true;
|
|
2600
|
+
merged.cssVars = { ...(merged.cssVars || {}), ...vars };
|
|
2601
|
+
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
2602
|
+
return this;
|
|
2603
|
+
},
|
|
2604
|
+
setLabel(label) {
|
|
2605
|
+
customLabel = typeof label === 'string' && label ? label : null;
|
|
2606
|
+
updateAria();
|
|
2607
|
+
return this;
|
|
2608
|
+
},
|
|
2609
|
+
setValueSuffix(suffix) {
|
|
2610
|
+
controller.setValueSuffix(suffix);
|
|
2611
|
+
return this;
|
|
2612
|
+
},
|
|
2210
2613
|
setColors(colors) {
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
controller.
|
|
2215
|
-
|
|
2614
|
+
const next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
|
|
2615
|
+
if (next.length !== 4 || next.some((color) => !color)) return this;
|
|
2616
|
+
dirty.colors = true;
|
|
2617
|
+
controller.setColors(next);
|
|
2618
|
+
if (overlay) overlay.setColors(next);
|
|
2619
|
+
controller.resizeCanvas();
|
|
2620
|
+
return this;
|
|
2216
2621
|
},
|
|
2217
2622
|
setSize(width, height) {
|
|
2218
2623
|
if (width !== undefined) merged.width = width;
|
|
@@ -2239,14 +2644,240 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2239
2644
|
controller.resizeCanvas();
|
|
2240
2645
|
return this;
|
|
2241
2646
|
},
|
|
2242
|
-
dispose() {
|
|
2243
|
-
|
|
2647
|
+
dispose() {
|
|
2648
|
+
disposed = true;
|
|
2649
|
+
unsubscribe();
|
|
2244
2650
|
visibility.dispose();
|
|
2245
2651
|
if (overlay) overlay.dispose();
|
|
2246
|
-
controller.dispose();
|
|
2247
|
-
root.remove();
|
|
2248
|
-
}
|
|
2652
|
+
controller.dispose();
|
|
2653
|
+
root.remove();
|
|
2654
|
+
},
|
|
2655
|
+
textRatio: merged.textRatio,
|
|
2656
|
+
cssVars: merged.cssVars,
|
|
2657
|
+
dirty
|
|
2658
|
+
};
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
function prefersReducedMotion() {
|
|
2662
|
+
return typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
/**
|
|
2666
|
+
* Mount the cosmic (nebula) material as a background layer inside `host`.
|
|
2667
|
+
*
|
|
2668
|
+
* The layer is absolutely positioned and defaults to `z-index: -1`, so it
|
|
2669
|
+
* paints behind the host's in-flow content: text/buttons/images inside the
|
|
2670
|
+
* host sit on top with zero extra CSS. Host gets `position: relative;
|
|
2671
|
+
* isolation: isolate` automatically so the layer can never escape its box
|
|
2672
|
+
* (or break stacking outside it).
|
|
2673
|
+
*
|
|
2674
|
+
* Options: preset (id/code/name or object), colors, seed, speed, quality,
|
|
2675
|
+
* renderer, mouseColor, respectReducedMotion, opacity (0-1), fallbackColor
|
|
2676
|
+
* (true=preset base color, a hex string, or false to disable).
|
|
2677
|
+
*/
|
|
2678
|
+
function createColorBackground(host, options = {}) {
|
|
2679
|
+
if (!host || typeof host.appendChild !== 'function') {
|
|
2680
|
+
throw new Error('createColorBackground: host element is required');
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
const preset = { ...getPreset('capsule', options.preset ?? '初光') };
|
|
2684
|
+
const merged = normalizeOptions(
|
|
2685
|
+
{
|
|
2686
|
+
quality: DEFAULTS.capsule.quality,
|
|
2687
|
+
renderer: DEFAULTS.capsule.renderer,
|
|
2688
|
+
respectReducedMotion: DEFAULTS.capsule.respectReducedMotion,
|
|
2689
|
+
mouseColor: true,
|
|
2690
|
+
opacity: 1,
|
|
2691
|
+
fallbackColor: true
|
|
2692
|
+
},
|
|
2693
|
+
preset,
|
|
2694
|
+
options
|
|
2695
|
+
);
|
|
2696
|
+
const normalizedColors = merged.colors.map(normalizeColor);
|
|
2697
|
+
if (normalizedColors.every(Boolean)) preset.colors = normalizedColors;
|
|
2698
|
+
preset.seed = merged.seed;
|
|
2699
|
+
preset.speed = merged.speed;
|
|
2700
|
+
|
|
2701
|
+
const computed = getComputedStyle(host);
|
|
2702
|
+
if (computed.position === 'static' || computed.position === '') {
|
|
2703
|
+
host.style.position = 'relative';
|
|
2704
|
+
}
|
|
2705
|
+
host.style.isolation = 'isolate';
|
|
2706
|
+
|
|
2707
|
+
const layer = document.createElement('div');
|
|
2708
|
+
layer.className = 'dlc-color-layer';
|
|
2709
|
+
|
|
2710
|
+
const syncLayerVars = () => {
|
|
2711
|
+
layer.style.setProperty('--dlc-color-opacity', String(merged.opacity));
|
|
2712
|
+
const fallback =
|
|
2713
|
+
merged.fallbackColor === false || merged.fallbackColor == null
|
|
2714
|
+
? 'transparent'
|
|
2715
|
+
: typeof merged.fallbackColor === 'string'
|
|
2716
|
+
? merged.fallbackColor
|
|
2717
|
+
: preset.colors[0];
|
|
2718
|
+
layer.style.setProperty('--dlc-color-bg', fallback);
|
|
2249
2719
|
};
|
|
2720
|
+
syncLayerVars();
|
|
2721
|
+
|
|
2722
|
+
const canvas = document.createElement('canvas');
|
|
2723
|
+
canvas.className = 'dlc-color-canvas';
|
|
2724
|
+
canvas.setAttribute('aria-hidden', 'true');
|
|
2725
|
+
layer.appendChild(canvas);
|
|
2726
|
+
host.appendChild(layer);
|
|
2727
|
+
|
|
2728
|
+
const emitter = createEmitter();
|
|
2729
|
+
let paused = merged.respectReducedMotion && prefersReducedMotion();
|
|
2730
|
+
let disposed = false;
|
|
2731
|
+
const dirty = {
|
|
2732
|
+
preset: false,
|
|
2733
|
+
seed: false,
|
|
2734
|
+
speed: false,
|
|
2735
|
+
colors: false,
|
|
2736
|
+
opacity: false,
|
|
2737
|
+
fallbackColor: false,
|
|
2738
|
+
mouseColor: false
|
|
2739
|
+
};
|
|
2740
|
+
|
|
2741
|
+
let renderer;
|
|
2742
|
+
const useWebgl = merged.renderer !== 'canvas2d';
|
|
2743
|
+
if (useWebgl) {
|
|
2744
|
+
try {
|
|
2745
|
+
renderer = new CosmicRenderer(canvas, merged, {
|
|
2746
|
+
dprCap: dprCapFor(merged.quality),
|
|
2747
|
+
mouseColor: merged.mouseColor !== false,
|
|
2748
|
+
eventTarget: host
|
|
2749
|
+
});
|
|
2750
|
+
} catch (error) {
|
|
2751
|
+
renderer = new FallbackRenderer(canvas, merged);
|
|
2752
|
+
nextTick(() => {
|
|
2753
|
+
if (disposed) return;
|
|
2754
|
+
emitter.emit('error', {
|
|
2755
|
+
message: String(error && error.message ? error.message : error)
|
|
2756
|
+
});
|
|
2757
|
+
});
|
|
2758
|
+
}
|
|
2759
|
+
} else {
|
|
2760
|
+
renderer = new FallbackRenderer(canvas, merged);
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2763
|
+
const resizeObserver =
|
|
2764
|
+
typeof ResizeObserver !== 'undefined'
|
|
2765
|
+
? new ResizeObserver(() => renderer.resize())
|
|
2766
|
+
: null;
|
|
2767
|
+
if (resizeObserver) resizeObserver.observe(host);
|
|
2768
|
+
else window.addEventListener('resize', renderer.resize);
|
|
2769
|
+
|
|
2770
|
+
const visibility = createVisibilityGuard(layer);
|
|
2771
|
+
|
|
2772
|
+
let animationTime = 0;
|
|
2773
|
+
const unsubscribe = subscribeScheduler(
|
|
2774
|
+
(delta) => {
|
|
2775
|
+
animationTime += delta;
|
|
2776
|
+
if (visibility.isVisible()) renderer.draw(animationTime);
|
|
2777
|
+
},
|
|
2778
|
+
() => paused
|
|
2779
|
+
);
|
|
2780
|
+
|
|
2781
|
+
nextTick(() => {
|
|
2782
|
+
if (disposed) return;
|
|
2783
|
+
emitter.emit('ready', { preset: { ...preset } });
|
|
2784
|
+
});
|
|
2785
|
+
|
|
2786
|
+
return {
|
|
2787
|
+
element: host,
|
|
2788
|
+
layer,
|
|
2789
|
+
canvas,
|
|
2790
|
+
preset,
|
|
2791
|
+
on: emitter.on,
|
|
2792
|
+
off: emitter.off,
|
|
2793
|
+
setPreset(ref) {
|
|
2794
|
+
const next = getPreset('capsule', ref);
|
|
2795
|
+
dirty.preset = true;
|
|
2796
|
+
Object.assign(preset, next);
|
|
2797
|
+
const nextColors = preset.colors.map(normalizeColor);
|
|
2798
|
+
if (nextColors.every(Boolean)) preset.colors = nextColors;
|
|
2799
|
+
renderer.setPreset({ ...preset });
|
|
2800
|
+
renderer.resize();
|
|
2801
|
+
syncLayerVars();
|
|
2802
|
+
emitter.emit('presetchange', { preset: { ...preset } });
|
|
2803
|
+
return this;
|
|
2804
|
+
},
|
|
2805
|
+
setColors(colors) {
|
|
2806
|
+
const next = Array.isArray(colors) ? colors.map(normalizeColor) : [];
|
|
2807
|
+
if (next.length !== 4 || next.some((color) => !color)) return this;
|
|
2808
|
+
dirty.colors = true;
|
|
2809
|
+
preset.colors = next;
|
|
2810
|
+
renderer.setPreset({ ...preset, colors: next });
|
|
2811
|
+
syncLayerVars();
|
|
2812
|
+
return this;
|
|
2813
|
+
},
|
|
2814
|
+
setSeed(seed) {
|
|
2815
|
+
const value = Number(seed);
|
|
2816
|
+
if (!Number.isFinite(value)) return this;
|
|
2817
|
+
dirty.seed = true;
|
|
2818
|
+
preset.seed = value;
|
|
2819
|
+
renderer.setPreset({ ...preset });
|
|
2820
|
+
return this;
|
|
2821
|
+
},
|
|
2822
|
+
setSpeed(speed) {
|
|
2823
|
+
const value = Number(speed);
|
|
2824
|
+
if (!Number.isFinite(value)) return this;
|
|
2825
|
+
dirty.speed = true;
|
|
2826
|
+
preset.speed = value;
|
|
2827
|
+
renderer.setPreset({ ...preset });
|
|
2828
|
+
return this;
|
|
2829
|
+
},
|
|
2830
|
+
setFallbackColor(value) {
|
|
2831
|
+
dirty.fallbackColor = true;
|
|
2832
|
+
merged.fallbackColor = value;
|
|
2833
|
+
syncLayerVars();
|
|
2834
|
+
return this;
|
|
2835
|
+
},
|
|
2836
|
+
setMouseColor(value) {
|
|
2837
|
+
dirty.mouseColor = true;
|
|
2838
|
+
merged.mouseColor = value !== false;
|
|
2839
|
+
if (typeof renderer.setMouseColor === 'function') renderer.setMouseColor(merged.mouseColor);
|
|
2840
|
+
return this;
|
|
2841
|
+
},
|
|
2842
|
+
setQuality(quality) {
|
|
2843
|
+
merged.quality = quality;
|
|
2844
|
+
if (typeof renderer.setDprCap === 'function') renderer.setDprCap(dprCapFor(quality));
|
|
2845
|
+
return this;
|
|
2846
|
+
},
|
|
2847
|
+
setOpacity(value) {
|
|
2848
|
+
const opacity = Number(value);
|
|
2849
|
+
if (!Number.isFinite(opacity)) return this;
|
|
2850
|
+
dirty.opacity = true;
|
|
2851
|
+
merged.opacity = Math.min(1, Math.max(0, opacity));
|
|
2852
|
+
layer.style.setProperty('--dlc-color-opacity', String(merged.opacity));
|
|
2853
|
+
return this;
|
|
2854
|
+
},
|
|
2855
|
+
randomize() {
|
|
2856
|
+
this.setSeed(Math.random() * 100);
|
|
2857
|
+
return this;
|
|
2858
|
+
},
|
|
2859
|
+
pause() {
|
|
2860
|
+
paused = true;
|
|
2861
|
+
return this;
|
|
2862
|
+
},
|
|
2863
|
+
resume() {
|
|
2864
|
+
paused = false;
|
|
2865
|
+
return this;
|
|
2866
|
+
},
|
|
2867
|
+
dispose() {
|
|
2868
|
+
disposed = true;
|
|
2869
|
+
unsubscribe();
|
|
2870
|
+
visibility.dispose();
|
|
2871
|
+
if (resizeObserver) resizeObserver.disconnect();
|
|
2872
|
+
else window.removeEventListener('resize', renderer.resize);
|
|
2873
|
+
renderer.dispose();
|
|
2874
|
+
layer.remove();
|
|
2875
|
+
},
|
|
2876
|
+
opacity: merged.opacity,
|
|
2877
|
+
fallbackColor: merged.fallbackColor,
|
|
2878
|
+
mouseColor: merged.mouseColor,
|
|
2879
|
+
dirty
|
|
2880
|
+
};
|
|
2250
2881
|
}
|
|
2251
2882
|
|
|
2252
2883
|
/**
|
|
@@ -2255,11 +2886,12 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2255
2886
|
* `h` (see vue2.js / vue3.js). Lifecycle options cover both.
|
|
2256
2887
|
*/
|
|
2257
2888
|
|
|
2258
|
-
const CAPSULE_EVENTS = [
|
|
2259
|
-
'ready', 'error', 'click', 'pointerdown', 'pointerup', 'dblclick',
|
|
2260
|
-
'pointerenter', 'pointerleave', 'presetchange'
|
|
2261
|
-
];
|
|
2262
|
-
const PROGRESS_EVENTS = ['ready', 'error', 'change', 'dragstart', 'dragend', 'presetchange'];
|
|
2889
|
+
const CAPSULE_EVENTS = [
|
|
2890
|
+
'ready', 'error', 'click', 'pointerdown', 'pointerup', 'dblclick',
|
|
2891
|
+
'pointerenter', 'pointerleave', 'presetchange'
|
|
2892
|
+
];
|
|
2893
|
+
const PROGRESS_EVENTS = ['ready', 'error', 'change', 'dragstart', 'dragend', 'presetchange'];
|
|
2894
|
+
const COLOR_EVENTS = ['ready', 'error', 'presetchange'];
|
|
2263
2895
|
|
|
2264
2896
|
function hookEvents(controller, events, emit) {
|
|
2265
2897
|
for (const event of events) {
|
|
@@ -2267,42 +2899,92 @@ function hookEvents(controller, events, emit) {
|
|
|
2267
2899
|
}
|
|
2268
2900
|
}
|
|
2269
2901
|
|
|
2270
|
-
function makeWrapper({ name, props, events, create, render, methods, watch }) {
|
|
2271
|
-
const internalMethods = {
|
|
2272
|
-
mountController() {
|
|
2273
|
-
this.controller = create(this.$el, this.$props);
|
|
2274
|
-
hookEvents(this.controller, events, (event, payload) => this.$emit(event, payload));
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
this.
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2902
|
+
function makeWrapper({ name, props, events, create, render, methods, watch }) {
|
|
2903
|
+
const internalMethods = {
|
|
2904
|
+
mountController(extraState = {}) {
|
|
2905
|
+
this.controller = create(this.$el, { ...this.$props, ...extraState });
|
|
2906
|
+
hookEvents(this.controller, events, (event, payload) => this.$emit(event, payload));
|
|
2907
|
+
this.moveSlots();
|
|
2908
|
+
this.applyAttrs();
|
|
2909
|
+
},
|
|
2910
|
+
recreate() {
|
|
2911
|
+
const extraState = this.runtimeState();
|
|
2912
|
+
if (this.controller) this.controller.dispose();
|
|
2913
|
+
this.mountController(extraState);
|
|
2914
|
+
},
|
|
2915
|
+
scheduleRecreate() {
|
|
2916
|
+
if (this._recreateQueued) return;
|
|
2917
|
+
this._recreateQueued = true;
|
|
2284
2918
|
this.$nextTick(() => {
|
|
2285
2919
|
this._recreateQueued = false;
|
|
2286
|
-
this.recreate();
|
|
2287
|
-
});
|
|
2288
|
-
}
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2920
|
+
this.recreate();
|
|
2921
|
+
});
|
|
2922
|
+
},
|
|
2923
|
+
moveSlots() {
|
|
2924
|
+
if (!this.controller || !this.controller.element || !this.$el) return;
|
|
2925
|
+
const root = this.controller.element;
|
|
2926
|
+
const textLayer = root.querySelector('.hj-capsule-text, .hj-progress-text');
|
|
2927
|
+
const visualLayer = root.querySelector('.hj-capsule-visual, .hj-progress-visual');
|
|
2928
|
+
const move = (wrapper, layer) => {
|
|
2929
|
+
if (!wrapper || !layer) return;
|
|
2930
|
+
for (const node of Array.from(wrapper.childNodes)) layer.appendChild(node);
|
|
2931
|
+
};
|
|
2932
|
+
const refs = this.$refs || {};
|
|
2933
|
+
move(refs.slotText, textLayer);
|
|
2934
|
+
move(refs.slotColor, visualLayer);
|
|
2935
|
+
},
|
|
2936
|
+
applyAttrs() {
|
|
2937
|
+
if (!this.controller || !this.controller.element || !this.$attrs) return;
|
|
2938
|
+
const root = this.controller.element;
|
|
2939
|
+
for (const key of Object.keys(this.$attrs)) {
|
|
2940
|
+
if (key === 'class' || key === 'style') continue;
|
|
2941
|
+
const value = this.$attrs[key];
|
|
2942
|
+
if (value == null || value === false) root.removeAttribute(key);
|
|
2943
|
+
else root.setAttribute(key, String(value));
|
|
2944
|
+
}
|
|
2945
|
+
},
|
|
2946
|
+
runtimeState() {
|
|
2947
|
+
const controller = this.controller;
|
|
2948
|
+
if (!controller || !controller.dirty) return {};
|
|
2949
|
+
const dirty = controller.dirty;
|
|
2950
|
+
const state = {};
|
|
2951
|
+
if (dirty.preset && controller.preset) state.preset = controller.preset.name;
|
|
2952
|
+
if (dirty.seed && controller.preset && controller.preset.seed !== undefined) state.seed = controller.preset.seed;
|
|
2953
|
+
if (dirty.speed && controller.preset && controller.preset.speed !== undefined) state.speed = controller.preset.speed;
|
|
2954
|
+
if (dirty.colors && controller.preset && Array.isArray(controller.preset.colors)) {
|
|
2955
|
+
state.colors = [...controller.preset.colors];
|
|
2956
|
+
}
|
|
2957
|
+
if (dirty.textRatio && controller.textRatio !== undefined) state.textRatio = controller.textRatio;
|
|
2958
|
+
if (dirty.cssVars && controller.cssVars) state.cssVars = { ...controller.cssVars };
|
|
2959
|
+
if (dirty.value && typeof controller.getValue === 'function') state.value = controller.getValue();
|
|
2960
|
+
if (dirty.edgeStyle && controller.preset) state.edgeStyle = controller.preset.edgeStyle;
|
|
2961
|
+
if (dirty.opacity) state.opacity = controller.opacity;
|
|
2962
|
+
if (dirty.fallbackColor) state.fallbackColor = controller.fallbackColor;
|
|
2963
|
+
if (dirty.mouseColor) state.mouseColor = controller.mouseColor;
|
|
2964
|
+
return state;
|
|
2965
|
+
}
|
|
2966
|
+
};
|
|
2967
|
+
|
|
2968
|
+
return {
|
|
2969
|
+
name,
|
|
2970
|
+
props,
|
|
2971
|
+
emits: events,
|
|
2972
|
+
render,
|
|
2973
|
+
inheritAttrs: false,
|
|
2296
2974
|
mounted() {
|
|
2297
2975
|
this.mountController();
|
|
2298
2976
|
},
|
|
2299
2977
|
beforeUnmount() {
|
|
2300
2978
|
if (this.controller) this.controller.dispose();
|
|
2301
2979
|
},
|
|
2302
|
-
beforeDestroy() {
|
|
2303
|
-
if (this.controller) this.controller.dispose();
|
|
2304
|
-
},
|
|
2305
|
-
|
|
2980
|
+
beforeDestroy() {
|
|
2981
|
+
if (this.controller) this.controller.dispose();
|
|
2982
|
+
},
|
|
2983
|
+
updated() {
|
|
2984
|
+
this.moveSlots();
|
|
2985
|
+
this.applyAttrs();
|
|
2986
|
+
},
|
|
2987
|
+
watch: {
|
|
2306
2988
|
preset(value) { if (value && this.controller) this.controller.setPreset(value); },
|
|
2307
2989
|
colors(value) { if (value && this.controller) this.controller.setColors(value); },
|
|
2308
2990
|
width(value) { if (value !== undefined && this.controller) this.controller.setSize(value); },
|
|
@@ -2316,11 +2998,11 @@ function makeWrapper({ name, props, events, create, render, methods, watch }) {
|
|
|
2316
2998
|
|
|
2317
2999
|
function makeCapsuleWrapper(createCapsule, render) {
|
|
2318
3000
|
return makeWrapper({
|
|
2319
|
-
name: 'CosmicCapsule',
|
|
3001
|
+
name: 'CosmicCapsule',
|
|
2320
3002
|
props: [
|
|
2321
3003
|
'preset', 'width', 'height', 'colors', 'seed', 'speed',
|
|
2322
|
-
'mouseColor', 'renderer', 'quality', 'respectReducedMotion', '
|
|
2323
|
-
],
|
|
3004
|
+
'textRatio', 'label', 'mouseColor', 'renderer', 'quality', 'respectReducedMotion', 'cssVars'
|
|
3005
|
+
],
|
|
2324
3006
|
events: CAPSULE_EVENTS,
|
|
2325
3007
|
create: createCapsule,
|
|
2326
3008
|
render,
|
|
@@ -2337,6 +3019,15 @@ function makeCapsuleWrapper(createCapsule, render) {
|
|
|
2337
3019
|
this.controller.setSpeed(speed);
|
|
2338
3020
|
}
|
|
2339
3021
|
},
|
|
3022
|
+
textRatio(value) {
|
|
3023
|
+
if (value !== undefined && this.controller) this.controller.setTextRatio(Number(value));
|
|
3024
|
+
},
|
|
3025
|
+
cssVars(value) {
|
|
3026
|
+
if (value && this.controller) this.controller.setCssVars(value);
|
|
3027
|
+
},
|
|
3028
|
+
label(value) {
|
|
3029
|
+
if (this.controller) this.controller.setLabel(value);
|
|
3030
|
+
},
|
|
2340
3031
|
mouseColor() {
|
|
2341
3032
|
this.scheduleRecreate();
|
|
2342
3033
|
},
|
|
@@ -2348,23 +3039,28 @@ function makeCapsuleWrapper(createCapsule, render) {
|
|
|
2348
3039
|
randomize() { return this.controller && this.controller.randomize(); },
|
|
2349
3040
|
pause() { return this.controller && this.controller.pause(); },
|
|
2350
3041
|
resume() { return this.controller && this.controller.resume(); },
|
|
2351
|
-
setPreset(preset) { return this.controller && this.controller.setPreset(preset); },
|
|
2352
|
-
setSize(width, height) { return this.controller && this.controller.setSize(width, height); },
|
|
2353
|
-
setColors(colors) { return this.controller && this.controller.setColors(colors); },
|
|
2354
|
-
|
|
2355
|
-
|
|
3042
|
+
setPreset(preset) { return this.controller && this.controller.setPreset(preset); },
|
|
3043
|
+
setSize(width, height) { return this.controller && this.controller.setSize(width, height); },
|
|
3044
|
+
setColors(colors) { return this.controller && this.controller.setColors(colors); },
|
|
3045
|
+
setText(content) { return this.controller && this.controller.setText(content); },
|
|
3046
|
+
setColorContent(content) { return this.controller && this.controller.setColorContent(content); },
|
|
3047
|
+
setTextRatio(ratio) { return this.controller && this.controller.setTextRatio(ratio); },
|
|
3048
|
+
setCssVars(vars) { return this.controller && this.controller.setCssVars(vars); },
|
|
3049
|
+
setLabel(label) { return this.controller && this.controller.setLabel(label); },
|
|
3050
|
+
setQuality(quality) { return this.controller && this.controller.setQuality(quality); },
|
|
3051
|
+
getController() { return this.controller; }
|
|
2356
3052
|
}
|
|
2357
3053
|
});
|
|
2358
3054
|
}
|
|
2359
3055
|
|
|
2360
|
-
function makeProgressWrapper(createProgressCapsule, render) {
|
|
3056
|
+
function makeProgressWrapper(createProgressCapsule, render) {
|
|
2361
3057
|
return makeWrapper({
|
|
2362
|
-
name: 'ProgressCapsule',
|
|
2363
|
-
props: [
|
|
2364
|
-
'preset', 'width', 'height', 'value', 'min', 'max',
|
|
2365
|
-
'draggable', 'keyboard', 'edgeStyle', 'colors', '
|
|
2366
|
-
'
|
|
2367
|
-
],
|
|
3058
|
+
name: 'ProgressCapsule',
|
|
3059
|
+
props: [
|
|
3060
|
+
'preset', 'width', 'height', 'value', 'min', 'max',
|
|
3061
|
+
'draggable', 'keyboard', 'disabled', 'edgeStyle', 'colors', 'textRatio', 'label', 'showValue', 'valueSuffix',
|
|
3062
|
+
'quality', 'renderer', 'respectReducedMotion', 'cssVars'
|
|
3063
|
+
],
|
|
2368
3064
|
events: PROGRESS_EVENTS,
|
|
2369
3065
|
create: createProgressCapsule,
|
|
2370
3066
|
render,
|
|
@@ -2381,9 +3077,27 @@ function makeProgressWrapper(createProgressCapsule, render) {
|
|
|
2381
3077
|
edgeStyle(value) {
|
|
2382
3078
|
if (value && this.controller) this.controller.setEdgeStyle(value);
|
|
2383
3079
|
},
|
|
2384
|
-
|
|
2385
|
-
this.
|
|
2386
|
-
},
|
|
3080
|
+
textRatio(value) {
|
|
3081
|
+
if (value !== undefined && this.controller) this.controller.setTextRatio(Number(value));
|
|
3082
|
+
},
|
|
3083
|
+
cssVars(value) {
|
|
3084
|
+
if (value && this.controller) this.controller.setCssVars(value);
|
|
3085
|
+
},
|
|
3086
|
+
label(value) {
|
|
3087
|
+
if (this.controller) this.controller.setLabel(value);
|
|
3088
|
+
},
|
|
3089
|
+
valueSuffix(value) {
|
|
3090
|
+
if (value !== undefined && this.controller) this.controller.setValueSuffix(value);
|
|
3091
|
+
},
|
|
3092
|
+
showValue() {
|
|
3093
|
+
this.scheduleRecreate();
|
|
3094
|
+
},
|
|
3095
|
+
disabled() {
|
|
3096
|
+
this.scheduleRecreate();
|
|
3097
|
+
},
|
|
3098
|
+
draggable() {
|
|
3099
|
+
this.scheduleRecreate();
|
|
3100
|
+
},
|
|
2387
3101
|
keyboard() {
|
|
2388
3102
|
this.scheduleRecreate();
|
|
2389
3103
|
},
|
|
@@ -2398,24 +3112,96 @@ function makeProgressWrapper(createProgressCapsule, render) {
|
|
|
2398
3112
|
randomize() { return this.controller && this.controller.randomize(); },
|
|
2399
3113
|
pause() { return this.controller && this.controller.pause(); },
|
|
2400
3114
|
resume() { return this.controller && this.controller.resume(); },
|
|
2401
|
-
setPreset(preset) { return this.controller && this.controller.setPreset(preset); },
|
|
2402
|
-
setSize(width, height) { return this.controller && this.controller.setSize(width, height); },
|
|
2403
|
-
setColors(colors) { return this.controller && this.controller.setColors(colors); },
|
|
2404
|
-
|
|
3115
|
+
setPreset(preset) { return this.controller && this.controller.setPreset(preset); },
|
|
3116
|
+
setSize(width, height) { return this.controller && this.controller.setSize(width, height); },
|
|
3117
|
+
setColors(colors) { return this.controller && this.controller.setColors(colors); },
|
|
3118
|
+
setText(content) { return this.controller && this.controller.setText(content); },
|
|
3119
|
+
setColorContent(content) { return this.controller && this.controller.setColorContent(content); },
|
|
3120
|
+
setTextRatio(ratio) { return this.controller && this.controller.setTextRatio(ratio); },
|
|
3121
|
+
setCssVars(vars) { return this.controller && this.controller.setCssVars(vars); },
|
|
3122
|
+
setLabel(label) { return this.controller && this.controller.setLabel(label); },
|
|
3123
|
+
setValueSuffix(suffix) { return this.controller && this.controller.setValueSuffix(suffix); },
|
|
3124
|
+
setQuality(quality) { return this.controller && this.controller.setQuality(quality); },
|
|
2405
3125
|
getController() { return this.controller; }
|
|
2406
3126
|
}
|
|
2407
|
-
});
|
|
3127
|
+
});
|
|
2408
3128
|
}
|
|
2409
3129
|
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
3130
|
+
function makeColorWrapper(createColorBackground, render) {
|
|
3131
|
+
return makeWrapper({
|
|
3132
|
+
name: 'ColorBackground',
|
|
3133
|
+
props: [
|
|
3134
|
+
'preset', 'colors', 'seed', 'speed', 'quality', 'renderer',
|
|
3135
|
+
'mouseColor', 'respectReducedMotion', 'opacity', 'fallbackColor'
|
|
3136
|
+
],
|
|
3137
|
+
events: COLOR_EVENTS,
|
|
3138
|
+
create: createColorBackground,
|
|
3139
|
+
render,
|
|
3140
|
+
watch: {
|
|
3141
|
+
seed(value) {
|
|
3142
|
+
const seed = Number(value);
|
|
3143
|
+
if (this.controller && Number.isFinite(seed)) this.controller.setSeed(seed);
|
|
3144
|
+
},
|
|
3145
|
+
speed(value) {
|
|
3146
|
+
const speed = Number(value);
|
|
3147
|
+
if (this.controller && Number.isFinite(speed)) this.controller.setSpeed(speed);
|
|
3148
|
+
},
|
|
3149
|
+
opacity(value) {
|
|
3150
|
+
if (value !== undefined && this.controller) this.controller.setOpacity(Number(value));
|
|
3151
|
+
},
|
|
3152
|
+
fallbackColor(value) {
|
|
3153
|
+
if (this.controller) this.controller.setFallbackColor(value);
|
|
3154
|
+
},
|
|
3155
|
+
mouseColor(value) {
|
|
3156
|
+
if (this.controller) this.controller.setMouseColor(value);
|
|
3157
|
+
},
|
|
3158
|
+
renderer() {
|
|
3159
|
+
this.scheduleRecreate();
|
|
3160
|
+
},
|
|
3161
|
+
respectReducedMotion() {
|
|
3162
|
+
this.scheduleRecreate();
|
|
3163
|
+
}
|
|
3164
|
+
},
|
|
3165
|
+
methods: {
|
|
3166
|
+
setPreset(preset) { return this.controller && this.controller.setPreset(preset); },
|
|
3167
|
+
setColors(colors) { return this.controller && this.controller.setColors(colors); },
|
|
3168
|
+
setSeed(seed) { return this.controller && this.controller.setSeed(seed); },
|
|
3169
|
+
setSpeed(speed) { return this.controller && this.controller.setSpeed(speed); },
|
|
3170
|
+
setQuality(quality) { return this.controller && this.controller.setQuality(quality); },
|
|
3171
|
+
setOpacity(value) { return this.controller && this.controller.setOpacity(value); },
|
|
3172
|
+
setFallbackColor(value) { return this.controller && this.controller.setFallbackColor(value); },
|
|
3173
|
+
setMouseColor(value) { return this.controller && this.controller.setMouseColor(value); },
|
|
3174
|
+
randomize() { return this.controller && this.controller.randomize(); },
|
|
3175
|
+
pause() { return this.controller && this.controller.pause(); },
|
|
3176
|
+
resume() { return this.controller && this.controller.resume(); },
|
|
3177
|
+
getController() { return this.controller; }
|
|
3178
|
+
}
|
|
3179
|
+
});
|
|
3180
|
+
}
|
|
3181
|
+
|
|
3182
|
+
// Vue3 does NOT pass `h` to the render option; it must be imported. Slot
|
|
3183
|
+
// content renders into two marker wrappers, then moveSlots moves it into the
|
|
3184
|
+
// component's slot containers.
|
|
3185
|
+
const render = function render() {
|
|
3186
|
+
return h('div', { class: 'hj-vue-host' }, [
|
|
3187
|
+
h('div', { class: 'hj-vue-slot-text', ref: 'slotText' }, this.$slots.default ? this.$slots.default() : []),
|
|
3188
|
+
h('div', { class: 'hj-vue-slot-color', ref: 'slotColor' }, this.$slots.color ? this.$slots.color() : [])
|
|
3189
|
+
]);
|
|
3190
|
+
};
|
|
3191
|
+
|
|
3192
|
+
// Color 的宿主内容就是组件内容:直接放在 host 里,浮在背景层上方。
|
|
3193
|
+
const renderColor = function renderColor() {
|
|
3194
|
+
return h('div', { class: 'hj-vue-host' }, this.$slots.default ? this.$slots.default() : []);
|
|
3195
|
+
};
|
|
3196
|
+
|
|
3197
|
+
const CosmicCapsule = makeCapsuleWrapper(createCapsule, render);
|
|
3198
|
+
const ProgressCapsule = makeProgressWrapper(createProgressCapsule, render);
|
|
3199
|
+
const ColorBackground = makeColorWrapper(createColorBackground, renderColor);
|
|
3200
|
+
|
|
3201
|
+
var vue3 = {
|
|
3202
|
+
CosmicCapsule,
|
|
3203
|
+
ProgressCapsule,
|
|
3204
|
+
ColorBackground
|
|
2419
3205
|
};
|
|
2420
3206
|
|
|
2421
|
-
export { CosmicCapsule, ProgressCapsule, vue3 as default };
|
|
3207
|
+
export { ColorBackground, CosmicCapsule, ProgressCapsule, vue3 as default };
|