@5even7/dlc-ui 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -3
- package/README.md +277 -248
- package/dist/capsule.cjs +421 -103
- package/dist/capsule.mjs +481 -197
- package/dist/color.cjs +342 -37
- package/dist/color.mjs +390 -105
- package/dist/index.cjs +745 -203
- package/dist/index.mjs +923 -457
- package/dist/index.umd.js +745 -203
- package/dist/index.umd.min.js +1 -1
- package/dist/progress.cjs +487 -116
- package/dist/progress.mjs +568 -245
- package/dist/vue2.cjs +1092 -208
- package/dist/vue2.mjs +1280 -501
- package/dist/vue3.cjs +1092 -208
- package/dist/vue3.mjs +1280 -501
- 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 +97 -81
- package/types/vue3.d.ts +59 -20
package/dist/vue3.mjs
CHANGED
|
@@ -114,50 +114,50 @@ const PALETTES$1 = {
|
|
|
114
114
|
* Capsule 预置:仅星云材质 NC-01~NC-06(NC-07~09 aurora 已移除)。
|
|
115
115
|
* 颜色引用公共色板,seed/speed 决定形态与流速。
|
|
116
116
|
*/
|
|
117
|
-
const CAPSULE_PRESETS = [
|
|
118
|
-
{ id: 'original', code: 'NC-01', name: '
|
|
119
|
-
{ id: 'ocean', code: 'NC-02', name: '
|
|
120
|
-
{ id: 'klein', code: 'NC-03', name: '
|
|
121
|
-
{ id: 'ultraviolet', code: 'NC-04', name: '
|
|
122
|
-
{ id: 'chrome', code: 'NC-05', name: '
|
|
123
|
-
{ id: 'plus', code: 'NC-06', name: '
|
|
117
|
+
const CAPSULE_PRESETS = [
|
|
118
|
+
{ id: 'original', code: 'NC-01', name: '初光', group: 'warm', seed: 1.7, speed: 0.5, colors: [...PALETTES$1.original] },
|
|
119
|
+
{ id: 'ocean', code: 'NC-02', name: '沧溟', group: 'cold', seed: 8.2, speed: 0.48, colors: [...PALETTES$1.ocean] },
|
|
120
|
+
{ id: 'klein', code: 'NC-03', name: '玄夜', group: 'cold', seed: 14.1, speed: 0.49, colors: [...PALETTES$1.klein] },
|
|
121
|
+
{ id: 'ultraviolet', code: 'NC-04', name: '紫霄', group: 'cold', seed: 23.4, speed: 0.47, colors: [...PALETTES$1.ultraviolet] },
|
|
122
|
+
{ id: 'chrome', code: 'NC-05', name: '银汉', group: 'cold', seed: 37.8, speed: 0.42, colors: [...PALETTES$1.chrome] },
|
|
123
|
+
{ id: 'plus', code: 'NC-06', name: '熔金', group: 'warm', seed: 51.3, speed: 0.5, colors: [...PALETTES$1.plus] }
|
|
124
124
|
];
|
|
125
125
|
|
|
126
126
|
const PROGRESS_PRESETS = [
|
|
127
|
-
{
|
|
128
|
-
id: 'model-training',
|
|
129
|
-
code: 'NC-10',
|
|
130
|
-
name: '
|
|
127
|
+
{
|
|
128
|
+
id: 'model-training',
|
|
129
|
+
code: 'NC-10',
|
|
130
|
+
name: '星火',
|
|
131
131
|
subtitle: 'SHA 4.5 + 100 2026 TKN',
|
|
132
132
|
group: 'progress',
|
|
133
133
|
initialProgress: 43,
|
|
134
134
|
edgeStyle: 'flow',
|
|
135
135
|
colors: ['#2B1025', '#FF3F94', '#FF8A3D', '#FFF06A']
|
|
136
136
|
},
|
|
137
|
-
{
|
|
138
|
-
id: 'agent-migration',
|
|
139
|
-
code: 'NC-11',
|
|
140
|
-
name: '
|
|
137
|
+
{
|
|
138
|
+
id: 'agent-migration',
|
|
139
|
+
code: 'NC-11',
|
|
140
|
+
name: '迁流',
|
|
141
141
|
subtitle: 'TRANSFERRING PROTOCOL',
|
|
142
142
|
group: 'progress',
|
|
143
143
|
initialProgress: 33,
|
|
144
144
|
edgeStyle: 'flow',
|
|
145
145
|
colors: ['#101C37', '#245BFF', '#00CFFF', '#5DFFE6']
|
|
146
146
|
},
|
|
147
|
-
{
|
|
148
|
-
id: 'visual-training',
|
|
149
|
-
code: 'NC-12',
|
|
150
|
-
name: '
|
|
147
|
+
{
|
|
148
|
+
id: 'visual-training',
|
|
149
|
+
code: 'NC-12',
|
|
150
|
+
name: '幻境',
|
|
151
151
|
subtitle: 'GENERATING POWER ++',
|
|
152
152
|
group: 'progress',
|
|
153
153
|
initialProgress: 58,
|
|
154
154
|
edgeStyle: 'flow',
|
|
155
155
|
colors: ['#21142D', '#7042FF', '#42F58D', '#C4FF8A']
|
|
156
156
|
},
|
|
157
|
-
{
|
|
158
|
-
id: 'tide',
|
|
159
|
-
code: 'NC-13',
|
|
160
|
-
name: '
|
|
157
|
+
{
|
|
158
|
+
id: 'tide',
|
|
159
|
+
code: 'NC-13',
|
|
160
|
+
name: '汐潮',
|
|
161
161
|
subtitle: 'MOON PULL / COAST',
|
|
162
162
|
group: 'progress',
|
|
163
163
|
initialProgress: 30,
|
|
@@ -166,109 +166,284 @@ const PROGRESS_PRESETS = [
|
|
|
166
166
|
}
|
|
167
167
|
];
|
|
168
168
|
|
|
169
|
-
[...CAPSULE_PRESETS, ...PROGRESS_PRESETS];
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return Boolean(
|
|
186
|
-
preset &&
|
|
187
|
-
typeof preset.id === 'string' &&
|
|
188
|
-
typeof preset.code === 'string' &&
|
|
189
|
-
typeof preset.name === 'string' &&
|
|
190
|
-
preset.group === 'progress' &&
|
|
191
|
-
Number.isFinite(preset.initialProgress) &&
|
|
192
|
-
['flow', 'tide'].indexOf(preset.edgeStyle || 'flow') !== -1 &&
|
|
193
|
-
Array.isArray(preset.colors) &&
|
|
194
|
-
preset.colors.length === 4
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function getPreset(kind, ref) {
|
|
199
|
-
const list = kind === 'capsule' ? CAPSULE_PRESETS : kind === 'progress' ? PROGRESS_PRESETS : null;
|
|
200
|
-
if (!list) throw new Error(`Unknown preset kind: ${kind} (use "capsule" or "progress")`);
|
|
201
|
-
if (ref && typeof ref === 'object') {
|
|
202
|
-
const valid = kind === 'capsule' ? validatePreset(ref) : validateProgressPreset(ref);
|
|
203
|
-
if (!valid) throw new Error(`Invalid ${kind} preset object`);
|
|
204
|
-
return ref;
|
|
205
|
-
}
|
|
206
|
-
const key = String(ref).trim().toLowerCase();
|
|
207
|
-
const found = list.find(
|
|
208
|
-
(preset) =>
|
|
209
|
-
preset.id.toLowerCase() === key ||
|
|
210
|
-
preset.code.toLowerCase() === key ||
|
|
211
|
-
preset.name.toLowerCase() === key
|
|
212
|
-
);
|
|
213
|
-
if (!found) throw new Error(`Unknown ${kind} preset: ${ref}`);
|
|
214
|
-
return found;
|
|
169
|
+
[...CAPSULE_PRESETS, ...PROGRESS_PRESETS];
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Resolve a preset by its literary name (e.g. '沧溟').
|
|
173
|
+
* Matching is case-insensitive and ignores surrounding whitespace,
|
|
174
|
+
* so '沧溟' and ' 沧溟 ' both work.
|
|
175
|
+
*/
|
|
176
|
+
function getPreset(kind, ref) {
|
|
177
|
+
const list = kind === 'capsule' ? CAPSULE_PRESETS : kind === 'progress' ? PROGRESS_PRESETS : null;
|
|
178
|
+
if (!list) throw new Error(`Unknown preset kind: ${kind} (use "capsule" or "progress")`);
|
|
179
|
+
const key = String(ref).trim().toLowerCase();
|
|
180
|
+
const found = list.find(
|
|
181
|
+
(preset) => preset.name.toLowerCase() === key
|
|
182
|
+
);
|
|
183
|
+
if (!found) throw new Error(`Unknown ${kind} preset: ${ref}`);
|
|
184
|
+
return found;
|
|
215
185
|
}
|
|
216
186
|
|
|
217
|
-
const DEFAULTS = {
|
|
218
|
-
capsule: {
|
|
219
|
-
width: '100%',
|
|
220
|
-
height: 160,
|
|
221
|
-
quality: 'auto',
|
|
222
|
-
renderer: 'auto',
|
|
223
|
-
respectReducedMotion: true,
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
min: 0,
|
|
187
|
+
const DEFAULTS = {
|
|
188
|
+
capsule: {
|
|
189
|
+
width: '100%',
|
|
190
|
+
height: 160,
|
|
191
|
+
quality: 'auto',
|
|
192
|
+
renderer: 'auto',
|
|
193
|
+
respectReducedMotion: true,
|
|
194
|
+
mouseColor: true,
|
|
195
|
+
textRatio: 39
|
|
196
|
+
},
|
|
197
|
+
progress: {
|
|
198
|
+
width: 454,
|
|
199
|
+
height: 104,
|
|
200
|
+
min: 0,
|
|
232
201
|
max: 100,
|
|
233
|
-
draggable: true,
|
|
234
|
-
keyboard: true,
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
+
}
|
|
241
213
|
};
|
|
242
214
|
|
|
243
|
-
/**
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
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: '%',
|
|
251
223
|
progressAria: '{brand} {code} 加载进度',
|
|
252
224
|
capsuleAria: '打开 {name} 沉浸预览'
|
|
253
225
|
};
|
|
254
226
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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})`;
|
|
272
447
|
}
|
|
273
448
|
|
|
274
449
|
const VERTEX_SHADER$1 = `#version 300 es
|
|
@@ -496,12 +671,31 @@ class CosmicRenderer {
|
|
|
496
671
|
this.timeOffset = preset.seed * 0.73;
|
|
497
672
|
}
|
|
498
673
|
|
|
499
|
-
setDprCap(cap) {
|
|
500
|
-
this.options.dprCap = cap;
|
|
501
|
-
this.resize();
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
|
|
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() {
|
|
505
699
|
this.preset.seed = Math.random() * 100;
|
|
506
700
|
this.timeOffset = Math.random() * 40;
|
|
507
701
|
}
|
|
@@ -581,10 +775,12 @@ class FallbackRenderer {
|
|
|
581
775
|
}
|
|
582
776
|
}
|
|
583
777
|
|
|
584
|
-
drawNebula(time) {
|
|
585
|
-
const ctx = this.context;
|
|
586
|
-
const { width, height } = this.canvas;
|
|
587
|
-
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);
|
|
588
784
|
gradient.addColorStop(0, this.preset.colors[0]);
|
|
589
785
|
gradient.addColorStop(0.38, this.preset.colors[1]);
|
|
590
786
|
gradient.addColorStop(0.72, this.preset.colors[2]);
|
|
@@ -592,10 +788,10 @@ class FallbackRenderer {
|
|
|
592
788
|
ctx.fillStyle = gradient;
|
|
593
789
|
ctx.fillRect(0, 0, width, height);
|
|
594
790
|
|
|
595
|
-
ctx.globalCompositeOperation = 'screen';
|
|
596
|
-
for (let index = 0; index < 6; index += 1) {
|
|
597
|
-
const x = (0.5 + 0.45 * Math.sin(
|
|
598
|
-
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;
|
|
599
795
|
const radius = Math.max(width, height) * (0.08 + (index % 3) * 0.04);
|
|
600
796
|
const glow = ctx.createRadialGradient(x, y, 0, x, y, radius);
|
|
601
797
|
glow.addColorStop(0, rgb(this.preset.colors[(index + 1) % 4], 0.24));
|
|
@@ -616,8 +812,11 @@ class FallbackRenderer {
|
|
|
616
812
|
this.preset = preset;
|
|
617
813
|
}
|
|
618
814
|
|
|
619
|
-
randomize() {
|
|
620
|
-
|
|
815
|
+
randomize() {
|
|
816
|
+
if (this.preset) this.preset.seed = Math.random() * 100;
|
|
817
|
+
}
|
|
818
|
+
setMouseColor() {}
|
|
819
|
+
dispose() {}
|
|
621
820
|
}
|
|
622
821
|
|
|
623
822
|
/**
|
|
@@ -717,78 +916,105 @@ function createVisibilityGuard(element) {
|
|
|
717
916
|
};
|
|
718
917
|
}
|
|
719
918
|
|
|
720
|
-
|
|
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() {
|
|
721
936
|
return typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
722
937
|
}
|
|
723
938
|
|
|
724
939
|
/**
|
|
725
|
-
* Mount a cosmic (nebula) capsule into `container`.
|
|
726
|
-
*
|
|
727
|
-
* Options: preset (
|
|
728
|
-
*
|
|
729
|
-
*
|
|
730
|
-
|
|
731
|
-
|
|
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 = {}) {
|
|
732
949
|
if (!container || typeof container.appendChild !== 'function') {
|
|
733
950
|
throw new Error('createCapsule: container element is required');
|
|
734
951
|
}
|
|
735
952
|
|
|
736
|
-
const preset = { ...getPreset('capsule', options.preset ?? '
|
|
737
|
-
const merged = normalizeOptions(DEFAULTS.capsule, preset, options);
|
|
738
|
-
const
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
root
|
|
745
|
-
root.
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
};
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
if (
|
|
762
|
-
if (
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
canvas.
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
root.appendChild(canvas);
|
|
788
|
-
container.appendChild(root);
|
|
789
|
-
|
|
790
|
-
const emitter = createEmitter();
|
|
791
|
-
let paused = merged.respectReducedMotion && prefersReducedMotion$
|
|
953
|
+
const preset = { ...getPreset('capsule', options.preset ?? '初光') };
|
|
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
|
+
};
|
|
792
1018
|
|
|
793
1019
|
let renderer;
|
|
794
1020
|
const useWebgl = merged.renderer !== 'canvas2d';
|
|
@@ -797,19 +1023,25 @@ function createCapsule(container, options = {}) {
|
|
|
797
1023
|
renderer = new CosmicRenderer(canvas, merged, { dprCap: dprCapFor(merged.quality) });
|
|
798
1024
|
} catch (error) {
|
|
799
1025
|
renderer = new FallbackRenderer(canvas, merged);
|
|
800
|
-
|
|
1026
|
+
nextTick(() => {
|
|
1027
|
+
if (disposed) return;
|
|
1028
|
+
emitter.emit('error', { message: String(error && error.message ? error.message : error) });
|
|
1029
|
+
});
|
|
801
1030
|
}
|
|
802
1031
|
} else {
|
|
803
1032
|
renderer = new FallbackRenderer(canvas, merged);
|
|
804
1033
|
}
|
|
805
1034
|
|
|
806
|
-
const applySize = () => {
|
|
807
|
-
root.style.width = parseSize(merged.width);
|
|
808
|
-
root.style.height = parseSize(merged.height);
|
|
809
|
-
const vars = merged.cssVars || {};
|
|
810
|
-
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
811
|
-
|
|
812
|
-
|
|
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
|
+
};
|
|
813
1045
|
applySize();
|
|
814
1046
|
|
|
815
1047
|
const resizeObserver = typeof ResizeObserver !== 'undefined'
|
|
@@ -820,10 +1052,8 @@ function createCapsule(container, options = {}) {
|
|
|
820
1052
|
|
|
821
1053
|
const visibility = createVisibilityGuard(root);
|
|
822
1054
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
root.addEventListener('pointerleave', () => emitter.emit('pointerleave', { preset: { ...preset } }));
|
|
826
|
-
}
|
|
1055
|
+
root.addEventListener('pointerenter', () => emitter.emit('pointerenter', { preset: { ...preset } }));
|
|
1056
|
+
root.addEventListener('pointerleave', () => emitter.emit('pointerleave', { preset: { ...preset } }));
|
|
827
1057
|
root.addEventListener('click', (event) => {
|
|
828
1058
|
emitter.emit('click', { event, preset: { ...preset } });
|
|
829
1059
|
});
|
|
@@ -840,45 +1070,97 @@ function createCapsule(container, options = {}) {
|
|
|
840
1070
|
() => paused
|
|
841
1071
|
);
|
|
842
1072
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
root.dataset.theme = preset.theme || 'light';
|
|
848
|
-
renderCopy();
|
|
849
|
-
};
|
|
1073
|
+
nextTick(() => {
|
|
1074
|
+
if (disposed) return;
|
|
1075
|
+
emitter.emit('ready', { preset: { ...preset } });
|
|
1076
|
+
});
|
|
850
1077
|
|
|
851
|
-
|
|
1078
|
+
const syncTheme = () => {
|
|
1079
|
+
root.dataset.mode = 'nebula';
|
|
1080
|
+
root.dataset.theme = preset.theme || 'light';
|
|
1081
|
+
updateAria();
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
return {
|
|
852
1085
|
element: root,
|
|
853
1086
|
canvas,
|
|
854
1087
|
preset,
|
|
855
1088
|
on: emitter.on,
|
|
856
1089
|
off: emitter.off,
|
|
857
|
-
setPreset(ref) {
|
|
858
|
-
const next = getPreset('capsule', ref);
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
preset.colors = colors;
|
|
869
|
-
renderer.setPreset({ ...preset, colors });
|
|
870
|
-
return this;
|
|
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;
|
|
871
1101
|
},
|
|
872
|
-
|
|
1102
|
+
setColors(colors) {
|
|
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 });
|
|
1108
|
+
return this;
|
|
1109
|
+
},
|
|
1110
|
+
setSeed(seed) {
|
|
1111
|
+
const value = Number(seed);
|
|
1112
|
+
if (!Number.isFinite(value)) return this;
|
|
1113
|
+
dirty.seed = true;
|
|
1114
|
+
preset.seed = value;
|
|
1115
|
+
renderer.setPreset({ ...preset });
|
|
1116
|
+
return this;
|
|
1117
|
+
},
|
|
1118
|
+
setSpeed(speed) {
|
|
1119
|
+
const value = Number(speed);
|
|
1120
|
+
if (!Number.isFinite(value)) return this;
|
|
1121
|
+
dirty.speed = true;
|
|
1122
|
+
preset.speed = value;
|
|
1123
|
+
renderer.setPreset({ ...preset });
|
|
1124
|
+
return this;
|
|
1125
|
+
},
|
|
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) {
|
|
873
1155
|
if (width !== undefined) merged.width = width;
|
|
874
1156
|
if (height !== undefined) merged.height = height;
|
|
875
1157
|
applySize();
|
|
876
1158
|
return this;
|
|
877
1159
|
},
|
|
878
|
-
randomize() {
|
|
879
|
-
|
|
880
|
-
return this;
|
|
881
|
-
},
|
|
1160
|
+
randomize() {
|
|
1161
|
+
this.setSeed(Math.random() * 100);
|
|
1162
|
+
return this;
|
|
1163
|
+
},
|
|
882
1164
|
pause() {
|
|
883
1165
|
paused = true;
|
|
884
1166
|
return this;
|
|
@@ -892,15 +1174,19 @@ function createCapsule(container, options = {}) {
|
|
|
892
1174
|
if (typeof renderer.setDprCap === 'function') renderer.setDprCap(dprCapFor(quality));
|
|
893
1175
|
return this;
|
|
894
1176
|
},
|
|
895
|
-
dispose() {
|
|
896
|
-
|
|
1177
|
+
dispose() {
|
|
1178
|
+
disposed = true;
|
|
1179
|
+
unsubscribe();
|
|
897
1180
|
visibility.dispose();
|
|
898
1181
|
if (resizeObserver) resizeObserver.disconnect();
|
|
899
1182
|
else window.removeEventListener('resize', renderer.resize);
|
|
900
|
-
renderer.dispose();
|
|
901
|
-
root.remove();
|
|
902
|
-
}
|
|
903
|
-
|
|
1183
|
+
renderer.dispose();
|
|
1184
|
+
root.remove();
|
|
1185
|
+
},
|
|
1186
|
+
textRatio: merged.textRatio,
|
|
1187
|
+
cssVars: merged.cssVars,
|
|
1188
|
+
dirty
|
|
1189
|
+
};
|
|
904
1190
|
}
|
|
905
1191
|
|
|
906
1192
|
const PROGRESS_MOTION_WIDTH = 240;
|
|
@@ -1525,7 +1811,7 @@ function interpolate(template, values) {
|
|
|
1525
1811
|
return template.replace(/\{(\w+)\}/g, (_, key) => (values[key] !== undefined ? values[key] : ''));
|
|
1526
1812
|
}
|
|
1527
1813
|
|
|
1528
|
-
function prefersReducedMotion() {
|
|
1814
|
+
function prefersReducedMotion$1() {
|
|
1529
1815
|
return typeof matchMedia !== 'undefined' && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
1530
1816
|
}
|
|
1531
1817
|
|
|
@@ -1588,14 +1874,16 @@ const FLOW_PROFILES = {
|
|
|
1588
1874
|
};
|
|
1589
1875
|
|
|
1590
1876
|
class ProgressCapsuleController {
|
|
1591
|
-
constructor({ root, canvas, valueElement, preset, emitter, options, copy }) {
|
|
1877
|
+
constructor({ root, canvas, valueElement, preset, emitter, options, copy, dirty }) {
|
|
1592
1878
|
this.root = root;
|
|
1593
1879
|
this.canvas = canvas;
|
|
1594
1880
|
this.valueElement = valueElement;
|
|
1595
1881
|
this.preset = preset;
|
|
1596
1882
|
this.emitter = emitter;
|
|
1597
|
-
this.options = options;
|
|
1598
|
-
this.copy = copy;
|
|
1883
|
+
this.options = options;
|
|
1884
|
+
this.copy = copy;
|
|
1885
|
+
this.dirty = dirty;
|
|
1886
|
+
this.valueSuffix = options.valueSuffix ?? copy.valueSuffix;
|
|
1599
1887
|
this.profile = preset.edgeStyle === 'tide'
|
|
1600
1888
|
? FLOW_PROFILES.tide
|
|
1601
1889
|
: (FLOW_PROFILES[preset.id] || FLOW_PROFILES['visual-training']);
|
|
@@ -1636,11 +1924,22 @@ class ProgressCapsuleController {
|
|
|
1636
1924
|
this.value = clamp(nextValue, this.min, this.max);
|
|
1637
1925
|
const rounded = Math.round(this.value);
|
|
1638
1926
|
this.root.style.setProperty('--progress', this.value.toFixed(2));
|
|
1639
|
-
this.root.setAttribute('aria-valuenow', String(rounded));
|
|
1640
|
-
this.root.setAttribute('aria-valuetext', `${rounded}${this.
|
|
1641
|
-
this.valueElement.textContent = `${rounded}${this.
|
|
1642
|
-
if (!this.suppressEvents)
|
|
1643
|
-
|
|
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
|
+
}
|
|
1644
1943
|
|
|
1645
1944
|
resizeCanvas() {
|
|
1646
1945
|
const bounds = this.root.getBoundingClientRect();
|
|
@@ -1813,13 +2112,14 @@ class ProgressCapsuleController {
|
|
|
1813
2112
|
if (clamped !== this.value) this.setProgress(clamped, 'prop');
|
|
1814
2113
|
}
|
|
1815
2114
|
|
|
1816
|
-
drawReferenceFlow() {
|
|
2115
|
+
drawReferenceFlow() {
|
|
1817
2116
|
const ctx = this.ctx;
|
|
1818
2117
|
const width = this.width;
|
|
1819
2118
|
const height = this.height;
|
|
1820
2119
|
if (!ctx || width <= 0 || height <= 0) return;
|
|
1821
2120
|
|
|
1822
|
-
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);
|
|
1823
2123
|
const time = this.flowTime;
|
|
1824
2124
|
const [dark, accentA, accentB, glow] = this.preset.colors;
|
|
1825
2125
|
|
|
@@ -1937,32 +2237,49 @@ class ProgressCapsuleController {
|
|
|
1937
2237
|
}
|
|
1938
2238
|
}
|
|
1939
2239
|
|
|
1940
|
-
updateFromPointer(event) {
|
|
1941
|
-
const bounds = this.root.getBoundingClientRect();
|
|
1942
|
-
const ratio = bounds.width > 0 ? (event.clientX - bounds.left) / bounds.width : 0;
|
|
1943
|
-
this.setProgress(ratio *
|
|
1944
|
-
}
|
|
1945
|
-
|
|
1946
|
-
beginDrag(event) {
|
|
1947
|
-
if (event.button !== undefined && event.button !== 0) return;
|
|
1948
|
-
event.preventDefault();
|
|
1949
|
-
this.dragging = true;
|
|
1950
|
-
this.
|
|
1951
|
-
|
|
1952
|
-
this.
|
|
1953
|
-
this.
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
this.
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
if (
|
|
1964
|
-
this.
|
|
1965
|
-
|
|
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');
|
|
1966
2283
|
try {
|
|
1967
2284
|
if (event?.pointerId !== undefined && this.root.hasPointerCapture?.(event.pointerId)) {
|
|
1968
2285
|
this.root.releasePointerCapture(event.pointerId);
|
|
@@ -1984,15 +2301,16 @@ class ProgressCapsuleController {
|
|
|
1984
2301
|
}
|
|
1985
2302
|
|
|
1986
2303
|
if (this.options.keyboard !== false) {
|
|
1987
|
-
this.handlers.keydown = (event) => {
|
|
1988
|
-
const
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
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
|
+
};
|
|
1996
2314
|
if (event.key === 'Home') {
|
|
1997
2315
|
event.preventDefault();
|
|
1998
2316
|
this.setProgress(this.min, 'keyboard');
|
|
@@ -2030,8 +2348,9 @@ class ProgressCapsuleController {
|
|
|
2030
2348
|
this.preset = { ...this.preset, colors };
|
|
2031
2349
|
}
|
|
2032
2350
|
|
|
2033
|
-
dispose() {
|
|
2034
|
-
if (this.
|
|
2351
|
+
dispose() {
|
|
2352
|
+
if (this._dragRaf) cancelAnimationFrame(this._dragRaf);
|
|
2353
|
+
if (this.resizeObserver) this.resizeObserver.disconnect();
|
|
2035
2354
|
else window.removeEventListener('resize', this.resizeCanvas);
|
|
2036
2355
|
for (const name of Object.keys(this.handlers)) {
|
|
2037
2356
|
const handler = this.handlers[name];
|
|
@@ -2042,103 +2361,127 @@ class ProgressCapsuleController {
|
|
|
2042
2361
|
}
|
|
2043
2362
|
|
|
2044
2363
|
/**
|
|
2045
|
-
* Mount a fluid progress capsule into `container`.
|
|
2046
|
-
*
|
|
2047
|
-
* Options: preset (id/code/name or object), width, height, value, min, max,
|
|
2048
|
-
* draggable, keyboard, colors, edgeStyle,
|
|
2049
|
-
*
|
|
2050
|
-
|
|
2051
|
-
|
|
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 = {}) {
|
|
2052
2374
|
if (!container || typeof container.appendChild !== 'function') {
|
|
2053
2375
|
throw new Error('createProgressCapsule: container element is required');
|
|
2054
2376
|
}
|
|
2055
2377
|
|
|
2056
|
-
const preset = { ...getPreset('progress', options.preset ?? '
|
|
2057
|
-
const merged = normalizeOptions(DEFAULTS.progress, preset, options);
|
|
2058
|
-
const
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2378
|
+
const preset = { ...getPreset('progress', options.preset ?? '星火') };
|
|
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
|
+
const 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
|
+
}
|
|
2065
2408
|
root.setAttribute('aria-valuemin', String(merged.min ?? 0));
|
|
2066
2409
|
root.setAttribute('aria-valuemax', String(merged.max ?? 100));
|
|
2067
2410
|
root.setAttribute('aria-valuenow', String(preset.initialProgress));
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
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');
|
|
2073
2419
|
|
|
2074
2420
|
const canvas = document.createElement('canvas');
|
|
2075
2421
|
canvas.className = 'hj-progress-canvas';
|
|
2076
2422
|
canvas.setAttribute('aria-hidden', 'true');
|
|
2077
2423
|
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
const
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
const
|
|
2088
|
-
const
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
const
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
root.style.height = parseSize(merged.height);
|
|
2128
|
-
const vars = merged.cssVars || {};
|
|
2129
|
-
for (const name of Object.keys(vars)) root.style.setProperty(name, vars[name]);
|
|
2130
|
-
};
|
|
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
|
+
};
|
|
2131
2473
|
applySize();
|
|
2132
2474
|
|
|
2133
|
-
const controller = new ProgressCapsuleController({
|
|
2134
|
-
root,
|
|
2135
|
-
canvas,
|
|
2136
|
-
valueElement,
|
|
2137
|
-
preset,
|
|
2138
|
-
emitter,
|
|
2139
|
-
options: merged,
|
|
2140
|
-
copy
|
|
2141
|
-
|
|
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
|
+
});
|
|
2142
2485
|
|
|
2143
2486
|
let overlay = null;
|
|
2144
2487
|
if (merged.renderer !== 'canvas2d') {
|
|
@@ -2149,7 +2492,13 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2149
2492
|
getProgress: () => controller.value
|
|
2150
2493
|
});
|
|
2151
2494
|
}
|
|
2152
|
-
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
|
+
}
|
|
2153
2502
|
|
|
2154
2503
|
const visibility = createVisibilityGuard(root);
|
|
2155
2504
|
let flowTime = 0;
|
|
@@ -2165,15 +2514,14 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2165
2514
|
() => paused
|
|
2166
2515
|
);
|
|
2167
2516
|
|
|
2168
|
-
|
|
2517
|
+
nextTick(() => {
|
|
2518
|
+
if (disposed) return;
|
|
2519
|
+
emitter.emit('ready', { preset: { ...preset } });
|
|
2520
|
+
});
|
|
2169
2521
|
|
|
2170
|
-
const syncDom = () => {
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
interpolate(copy.progressAria, { brand: copy.brandName, code: preset.code, name: preset.name })
|
|
2174
|
-
);
|
|
2175
|
-
renderCopy();
|
|
2176
|
-
};
|
|
2522
|
+
const syncDom = () => {
|
|
2523
|
+
updateAria();
|
|
2524
|
+
};
|
|
2177
2525
|
|
|
2178
2526
|
return {
|
|
2179
2527
|
element: root,
|
|
@@ -2192,31 +2540,84 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2192
2540
|
controller.setRange(min, max);
|
|
2193
2541
|
return this;
|
|
2194
2542
|
},
|
|
2195
|
-
setPreset(ref) {
|
|
2196
|
-
const next = getPreset('progress', ref);
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
controller.
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
return this;
|
|
2543
|
+
setPreset(ref) {
|
|
2544
|
+
const next = getPreset('progress', ref);
|
|
2545
|
+
dirty.preset = true;
|
|
2546
|
+
Object.assign(preset, next);
|
|
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 } });
|
|
2563
|
+
return this;
|
|
2564
|
+
},
|
|
2565
|
+
setEdgeStyle(edgeStyle) {
|
|
2566
|
+
const value = String(edgeStyle || '').toLowerCase();
|
|
2567
|
+
if (value !== 'flow' && value !== 'tide') return this;
|
|
2568
|
+
dirty.edgeStyle = true;
|
|
2569
|
+
preset.edgeStyle = value;
|
|
2570
|
+
controller.profile = value === 'tide'
|
|
2571
|
+
? FLOW_PROFILES.tide
|
|
2572
|
+
: (FLOW_PROFILES[preset.id] || FLOW_PROFILES['visual-training']);
|
|
2573
|
+
if (overlay) {
|
|
2574
|
+
overlay.dispose();
|
|
2575
|
+
overlay = attachProgressFlowOverlay({ root, canvas, preset, getProgress: () => controller.value });
|
|
2576
|
+
}
|
|
2577
|
+
controller.webglActive = Boolean(overlay);
|
|
2578
|
+
controller.resizeCanvas();
|
|
2579
|
+
return this;
|
|
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
|
+
},
|
|
2613
|
+
setColors(colors) {
|
|
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;
|
|
2220
2621
|
},
|
|
2221
2622
|
setSize(width, height) {
|
|
2222
2623
|
if (width !== undefined) merged.width = width;
|
|
@@ -2243,14 +2644,240 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2243
2644
|
controller.resizeCanvas();
|
|
2244
2645
|
return this;
|
|
2245
2646
|
},
|
|
2246
|
-
dispose() {
|
|
2247
|
-
|
|
2647
|
+
dispose() {
|
|
2648
|
+
disposed = true;
|
|
2649
|
+
unsubscribe();
|
|
2248
2650
|
visibility.dispose();
|
|
2249
2651
|
if (overlay) overlay.dispose();
|
|
2250
|
-
controller.dispose();
|
|
2251
|
-
root.remove();
|
|
2252
|
-
}
|
|
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);
|
|
2253
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
|
+
};
|
|
2254
2881
|
}
|
|
2255
2882
|
|
|
2256
2883
|
/**
|
|
@@ -2259,11 +2886,12 @@ function createProgressCapsule(container, options = {}) {
|
|
|
2259
2886
|
* `h` (see vue2.js / vue3.js). Lifecycle options cover both.
|
|
2260
2887
|
*/
|
|
2261
2888
|
|
|
2262
|
-
const CAPSULE_EVENTS = [
|
|
2263
|
-
'ready', 'error', 'click', 'pointerdown', 'pointerup', 'dblclick',
|
|
2264
|
-
'pointerenter', 'pointerleave', 'presetchange'
|
|
2265
|
-
];
|
|
2266
|
-
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'];
|
|
2267
2895
|
|
|
2268
2896
|
function hookEvents(controller, events, emit) {
|
|
2269
2897
|
for (const event of events) {
|
|
@@ -2271,42 +2899,92 @@ function hookEvents(controller, events, emit) {
|
|
|
2271
2899
|
}
|
|
2272
2900
|
}
|
|
2273
2901
|
|
|
2274
|
-
function makeWrapper({ name, props, events, create, render, methods, watch }) {
|
|
2275
|
-
const internalMethods = {
|
|
2276
|
-
mountController() {
|
|
2277
|
-
this.controller = create(this.$el, this.$props);
|
|
2278
|
-
hookEvents(this.controller, events, (event, payload) => this.$emit(event, payload));
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
this.
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
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;
|
|
2288
2918
|
this.$nextTick(() => {
|
|
2289
2919
|
this._recreateQueued = false;
|
|
2290
|
-
this.recreate();
|
|
2291
|
-
});
|
|
2292
|
-
}
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
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,
|
|
2300
2974
|
mounted() {
|
|
2301
2975
|
this.mountController();
|
|
2302
2976
|
},
|
|
2303
2977
|
beforeUnmount() {
|
|
2304
2978
|
if (this.controller) this.controller.dispose();
|
|
2305
2979
|
},
|
|
2306
|
-
beforeDestroy() {
|
|
2307
|
-
if (this.controller) this.controller.dispose();
|
|
2308
|
-
},
|
|
2309
|
-
|
|
2980
|
+
beforeDestroy() {
|
|
2981
|
+
if (this.controller) this.controller.dispose();
|
|
2982
|
+
},
|
|
2983
|
+
updated() {
|
|
2984
|
+
this.moveSlots();
|
|
2985
|
+
this.applyAttrs();
|
|
2986
|
+
},
|
|
2987
|
+
watch: {
|
|
2310
2988
|
preset(value) { if (value && this.controller) this.controller.setPreset(value); },
|
|
2311
2989
|
colors(value) { if (value && this.controller) this.controller.setColors(value); },
|
|
2312
2990
|
width(value) { if (value !== undefined && this.controller) this.controller.setSize(value); },
|
|
@@ -2320,33 +2998,39 @@ function makeWrapper({ name, props, events, create, render, methods, watch }) {
|
|
|
2320
2998
|
|
|
2321
2999
|
function makeCapsuleWrapper(createCapsule, render) {
|
|
2322
3000
|
return makeWrapper({
|
|
2323
|
-
name: 'CosmicCapsule',
|
|
2324
|
-
props: [
|
|
2325
|
-
'preset', 'width', 'height', 'colors', 'seed', 'speed',
|
|
2326
|
-
'mouseColor', 'renderer', 'quality', 'respectReducedMotion', '
|
|
2327
|
-
],
|
|
3001
|
+
name: 'CosmicCapsule',
|
|
3002
|
+
props: [
|
|
3003
|
+
'preset', 'width', 'height', 'colors', 'seed', 'speed',
|
|
3004
|
+
'textRatio', 'label', 'mouseColor', 'renderer', 'quality', 'respectReducedMotion', 'cssVars'
|
|
3005
|
+
],
|
|
2328
3006
|
events: CAPSULE_EVENTS,
|
|
2329
3007
|
create: createCapsule,
|
|
2330
3008
|
render,
|
|
2331
3009
|
watch: {
|
|
2332
|
-
seed(value) {
|
|
2333
|
-
const seed = Number(value);
|
|
2334
|
-
if (this.controller && Number.isFinite(seed)) {
|
|
2335
|
-
this.controller.
|
|
2336
|
-
}
|
|
2337
|
-
},
|
|
2338
|
-
speed(value) {
|
|
2339
|
-
const speed = Number(value);
|
|
2340
|
-
if (this.controller && Number.isFinite(speed)) {
|
|
2341
|
-
this.controller.
|
|
2342
|
-
}
|
|
2343
|
-
},
|
|
2344
|
-
|
|
2345
|
-
this.
|
|
2346
|
-
},
|
|
2347
|
-
|
|
2348
|
-
this.
|
|
2349
|
-
},
|
|
3010
|
+
seed(value) {
|
|
3011
|
+
const seed = Number(value);
|
|
3012
|
+
if (this.controller && Number.isFinite(seed)) {
|
|
3013
|
+
this.controller.setSeed(seed);
|
|
3014
|
+
}
|
|
3015
|
+
},
|
|
3016
|
+
speed(value) {
|
|
3017
|
+
const speed = Number(value);
|
|
3018
|
+
if (this.controller && Number.isFinite(speed)) {
|
|
3019
|
+
this.controller.setSpeed(speed);
|
|
3020
|
+
}
|
|
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
|
+
},
|
|
3031
|
+
mouseColor() {
|
|
3032
|
+
this.scheduleRecreate();
|
|
3033
|
+
},
|
|
2350
3034
|
renderer() {
|
|
2351
3035
|
this.scheduleRecreate();
|
|
2352
3036
|
}
|
|
@@ -2355,23 +3039,28 @@ function makeCapsuleWrapper(createCapsule, render) {
|
|
|
2355
3039
|
randomize() { return this.controller && this.controller.randomize(); },
|
|
2356
3040
|
pause() { return this.controller && this.controller.pause(); },
|
|
2357
3041
|
resume() { return this.controller && this.controller.resume(); },
|
|
2358
|
-
setPreset(preset) { return this.controller && this.controller.setPreset(preset); },
|
|
2359
|
-
setSize(width, height) { return this.controller && this.controller.setSize(width, height); },
|
|
2360
|
-
setColors(colors) { return this.controller && this.controller.setColors(colors); },
|
|
2361
|
-
|
|
2362
|
-
|
|
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; }
|
|
2363
3052
|
}
|
|
2364
3053
|
});
|
|
2365
3054
|
}
|
|
2366
3055
|
|
|
2367
|
-
function makeProgressWrapper(createProgressCapsule, render) {
|
|
3056
|
+
function makeProgressWrapper(createProgressCapsule, render) {
|
|
2368
3057
|
return makeWrapper({
|
|
2369
|
-
name: 'ProgressCapsule',
|
|
2370
|
-
props: [
|
|
2371
|
-
'preset', 'width', 'height', 'value', 'min', 'max',
|
|
2372
|
-
'draggable', 'keyboard', 'edgeStyle', 'colors', '
|
|
2373
|
-
'
|
|
2374
|
-
],
|
|
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
|
+
],
|
|
2375
3064
|
events: PROGRESS_EVENTS,
|
|
2376
3065
|
create: createProgressCapsule,
|
|
2377
3066
|
render,
|
|
@@ -2385,12 +3074,30 @@ function makeProgressWrapper(createProgressCapsule, render) {
|
|
|
2385
3074
|
max(value) {
|
|
2386
3075
|
if (value !== undefined && this.controller) this.controller.setRange(this.controller.min, value);
|
|
2387
3076
|
},
|
|
2388
|
-
edgeStyle(value) {
|
|
2389
|
-
if (value && this.controller) this.controller.
|
|
2390
|
-
},
|
|
2391
|
-
|
|
2392
|
-
this.
|
|
2393
|
-
},
|
|
3077
|
+
edgeStyle(value) {
|
|
3078
|
+
if (value && this.controller) this.controller.setEdgeStyle(value);
|
|
3079
|
+
},
|
|
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
|
+
},
|
|
2394
3101
|
keyboard() {
|
|
2395
3102
|
this.scheduleRecreate();
|
|
2396
3103
|
},
|
|
@@ -2405,24 +3112,96 @@ function makeProgressWrapper(createProgressCapsule, render) {
|
|
|
2405
3112
|
randomize() { return this.controller && this.controller.randomize(); },
|
|
2406
3113
|
pause() { return this.controller && this.controller.pause(); },
|
|
2407
3114
|
resume() { return this.controller && this.controller.resume(); },
|
|
2408
|
-
setPreset(preset) { return this.controller && this.controller.setPreset(preset); },
|
|
2409
|
-
setSize(width, height) { return this.controller && this.controller.setSize(width, height); },
|
|
2410
|
-
setColors(colors) { return this.controller && this.controller.setColors(colors); },
|
|
2411
|
-
|
|
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); },
|
|
2412
3125
|
getController() { return this.controller; }
|
|
2413
3126
|
}
|
|
2414
|
-
});
|
|
3127
|
+
});
|
|
2415
3128
|
}
|
|
2416
3129
|
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
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
|
|
2426
3205
|
};
|
|
2427
3206
|
|
|
2428
|
-
export { CosmicCapsule, ProgressCapsule, vue3 as default };
|
|
3207
|
+
export { ColorBackground, CosmicCapsule, ProgressCapsule, vue3 as default };
|