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