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