@applemusic-like-lyrics/core 0.0.14 → 0.1.0

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.
@@ -1,1270 +0,0 @@
1
- import { Container as C } from "@pixi/display";
2
- import { Application as z } from "@pixi/app";
3
- import { BlurFilter as y } from "@pixi/filter-blur";
4
- import { ColorMatrixFilter as P } from "@pixi/filter-color-matrix";
5
- import { Texture as I } from "@pixi/core";
6
- import { Sprite as S } from "@pixi/sprite";
7
- import { BulgePinchFilter as b } from "@pixi/filter-bulge-pinch";
8
- import { create as k } from "jss";
9
- import D from "jss-preset-default";
10
- class A extends C {
11
- time = 0;
12
- }
13
- class B {
14
- constructor(e) {
15
- this.canvas = e;
16
- const t = e.getBoundingClientRect();
17
- this.canvas.width = t.width * this.currerntRenderScale, this.canvas.height = t.height * this.currerntRenderScale, this.observer = new ResizeObserver(() => {
18
- const i = e.getBoundingClientRect();
19
- this.canvas.width = Math.max(1, i.width), this.canvas.height = Math.max(1, i.height), this.app.renderer.resize(
20
- this.canvas.width * this.currerntRenderScale,
21
- this.canvas.height * this.currerntRenderScale
22
- ), this.app.ticker.start(), this.rebuildFilters();
23
- }), this.observer.observe(e), this.app = new z({
24
- view: e,
25
- resizeTo: this.canvas,
26
- powerPreference: "low-power",
27
- backgroundAlpha: 0
28
- }), this.rebuildFilters(), this.app.ticker.maxFPS = 30, this.app.ticker.add(this.onTick), this.app.ticker.start();
29
- }
30
- observer;
31
- app;
32
- curContainer;
33
- staticMode = !1;
34
- lastContainer = /* @__PURE__ */ new Set();
35
- onTick = (e) => {
36
- for (const t of this.lastContainer)
37
- t.alpha = Math.max(0, t.alpha - e / 60), t.alpha <= 0 && (this.app.stage.removeChild(t), this.lastContainer.delete(t), t.destroy(!0));
38
- if (this.curContainer) {
39
- this.curContainer.alpha = Math.min(
40
- 1,
41
- this.curContainer.alpha + e / 60
42
- );
43
- const [t, i, s, r] = this.curContainer.children, n = Math.max(this.app.screen.width, this.app.screen.height);
44
- t.position.set(this.app.screen.width / 2, this.app.screen.height / 2), i.position.set(
45
- this.app.screen.width / 2.5,
46
- this.app.screen.height / 2.5
47
- ), s.position.set(this.app.screen.width / 2, this.app.screen.height / 2), r.position.set(this.app.screen.width / 2, this.app.screen.height / 2), t.width = n * Math.sqrt(2), t.height = t.width, i.width = n * 0.8, i.height = i.width, s.width = n * 0.5, s.height = s.width, r.width = n * 0.25, r.height = r.width, this.curContainer.time += e * this.flowSpeed, t.rotation += e / 1e3 * this.flowSpeed, i.rotation -= e / 500 * this.flowSpeed, s.rotation += e / 1e3 * this.flowSpeed, r.rotation -= e / 750 * this.flowSpeed, s.x = this.app.screen.width / 2 + this.app.screen.width / 4 * Math.cos(this.curContainer.time / 1e3 * 0.75), s.y = this.app.screen.height / 2 + this.app.screen.width / 4 * Math.cos(this.curContainer.time / 1e3 * 0.75), r.x = this.app.screen.width / 2 + this.app.screen.width / 4 * 0.1 + Math.cos(this.curContainer.time * 6e-3 * 0.75), r.y = this.app.screen.height / 2 + this.app.screen.width / 4 * 0.1 + Math.cos(this.curContainer.time * 6e-3 * 0.75), this.curContainer.alpha >= 1 && this.lastContainer.size === 0 && this.staticMode && this.app.ticker.stop();
48
- }
49
- };
50
- flowSpeed = 2;
51
- currerntRenderScale = 0.75;
52
- /**
53
- * 修改背景的流动速度,数字越大越快,默认为 2
54
- * @param speed 背景的流动速度,默认为 2
55
- */
56
- setFlowSpeed(e) {
57
- this.flowSpeed = e;
58
- }
59
- /**
60
- * 修改背景的渲染比例,默认是 0.5
61
- *
62
- * 一般情况下这个程度既没有明显瑕疵也不会特别吃性能
63
- * @param scale 背景的渲染比例
64
- */
65
- setRenderScale(e) {
66
- this.currerntRenderScale = e;
67
- const t = this.canvas.getBoundingClientRect();
68
- this.canvas.width = Math.max(1, t.width), this.canvas.height = Math.max(1, t.height), this.app.renderer.resize(
69
- this.canvas.width * this.currerntRenderScale,
70
- this.canvas.height * this.currerntRenderScale
71
- ), this.rebuildFilters();
72
- }
73
- rebuildFilters() {
74
- const e = Math.min(this.canvas.width, this.canvas.height), t = Math.max(this.canvas.width, this.canvas.height), i = new P();
75
- i.saturate(1.2, !1);
76
- const s = new P();
77
- s.brightness(0.6, !1);
78
- const r = new P();
79
- r.contrast(0.3, !0), this.app.stage.filters?.forEach((n) => {
80
- n.destroy();
81
- }), this.app.stage.filters = [], this.app.stage.filters.push(new y(5, 1)), this.app.stage.filters.push(new y(10, 1)), this.app.stage.filters.push(new y(20, 2)), this.app.stage.filters.push(new y(40, 2)), this.app.stage.filters.push(new y(80, 2)), e > 768 && this.app.stage.filters.push(new y(160, 4)), e > 768 * 2 && this.app.stage.filters.push(new y(320, 4)), this.app.stage.filters.push(i, s, r), this.app.stage.filters.push(new y(5, 1)), Math.random() > 0.5 ? (this.app.stage.filters.push(
82
- new b({
83
- radius: (t + e) / 2,
84
- strength: 1,
85
- center: [0.25, 1]
86
- })
87
- ), this.app.stage.filters.push(
88
- new b({
89
- radius: (t + e) / 2,
90
- strength: 1,
91
- center: [0.75, 0]
92
- })
93
- )) : (this.app.stage.filters.push(
94
- new b({
95
- radius: (t + e) / 2,
96
- strength: 1,
97
- center: [0.75, 1]
98
- })
99
- ), this.app.stage.filters.push(
100
- new b({
101
- radius: (t + e) / 2,
102
- strength: 1,
103
- center: [0.25, 0]
104
- })
105
- ));
106
- }
107
- /**
108
- * 是否启用静态模式,即图片在更换后就会保持静止状态并禁用更新,以节省性能
109
- * @param enable 是否启用静态模式
110
- */
111
- setStaticMode(e = !1) {
112
- this.staticMode = e, this.app.ticker.start();
113
- }
114
- /**
115
- * 修改背景动画帧率,默认是 30 FPS
116
- *
117
- * 如果设置成 0 则会停止动画
118
- * @param fps 目标帧率,默认 30 FPS
119
- */
120
- setFPS(e) {
121
- this.app.ticker.maxFPS = e;
122
- }
123
- /**
124
- * 暂停背景动画,画面即便是更新了图片也不会发生变化
125
- */
126
- pause() {
127
- this.app.ticker.stop(), this.app.render();
128
- }
129
- /**
130
- * 恢复播放背景动画
131
- */
132
- resume() {
133
- this.app.ticker.start();
134
- }
135
- /**
136
- * 设置背景专辑图片,图片材质加载并设置完成后会返回
137
- * @param albumUrl 图片的目标链接
138
- */
139
- async setAlbumImage(e) {
140
- const t = new Image();
141
- t.src = e, t.crossOrigin = "anonymous";
142
- let i = 5, s;
143
- for (; !s?.baseTexture?.resource?.valid && i > 0; )
144
- try {
145
- await t.decode(), s = I.from(t, {
146
- resourceOptions: {
147
- autoLoad: !1
148
- }
149
- }), await s.baseTexture.resource.load();
150
- } catch (c) {
151
- console.warn(
152
- `failed on loading album image, retrying (${i})`,
153
- e,
154
- c
155
- ), s = void 0, i--;
156
- }
157
- if (!s)
158
- return;
159
- const r = new A(), n = new S(s), a = new S(s), h = new S(s), l = new S(s);
160
- n.anchor.set(0.5, 0.5), a.anchor.set(0.5, 0.5), h.anchor.set(0.5, 0.5), l.anchor.set(0.5, 0.5), n.rotation = Math.random() * Math.PI * 2, a.rotation = Math.random() * Math.PI * 2, h.rotation = Math.random() * Math.PI * 2, l.rotation = Math.random() * Math.PI * 2, r.addChild(n, a, h, l), this.curContainer && this.lastContainer.add(this.curContainer), this.curContainer = r, this.app.stage.addChild(this.curContainer), this.curContainer.alpha = 0, this.app.ticker.start();
161
- }
162
- dispose() {
163
- this.observer.disconnect(), this.app.ticker.remove(this.onTick), this.app.destroy(!0);
164
- }
165
- }
166
- class se extends B {
167
- element;
168
- constructor() {
169
- const e = document.createElement("canvas");
170
- super(e), this.element = e, e.style.pointerEvents = "none", e.style.zIndex = "-1", e.style.contain = "strict";
171
- }
172
- getElement() {
173
- return this.element;
174
- }
175
- dispose() {
176
- super.dispose(), this.element.remove();
177
- }
178
- }
179
- const $ = (o, e) => o.size === e.size && [...o].every((t) => e.has(t));
180
- class L {
181
- currentPosition = 0;
182
- targetPosition = 0;
183
- currentTime = 0;
184
- params = {};
185
- currentSolver;
186
- getV;
187
- getV2;
188
- queueParams;
189
- queuePosition;
190
- constructor(e = 0) {
191
- this.targetPosition = e, this.currentPosition = this.targetPosition, this.currentSolver = () => this.targetPosition, this.getV = () => 0, this.getV2 = () => 0;
192
- }
193
- resetSolver() {
194
- const e = this.getV(this.currentTime);
195
- this.currentTime = 0, this.currentSolver = F(
196
- this.currentPosition,
197
- e,
198
- this.targetPosition,
199
- 0,
200
- this.params
201
- ), this.getV = x(this.currentSolver), this.getV2 = x(this.getV);
202
- }
203
- arrived() {
204
- return Math.abs(this.targetPosition - this.currentPosition) < 0.01 && this.getV(this.currentTime) < 0.01 && this.getV2(this.currentTime) < 0.01 && this.queueParams === void 0 && this.queuePosition === void 0;
205
- }
206
- setPosition(e) {
207
- this.targetPosition = e, this.currentPosition = e, this.currentSolver = () => this.targetPosition, this.getV = () => 0, this.getV2 = () => 0;
208
- }
209
- update(e = 0) {
210
- this.currentTime += e, this.currentPosition = this.currentSolver(this.currentTime), this.queueParams && (this.queueParams.time -= e, this.queueParams.time <= 0 && this.updateParams({
211
- ...this.queueParams
212
- })), this.queuePosition && (this.queuePosition.time -= e, this.queuePosition.time <= 0 && this.setTargetPosition(this.queuePosition.position)), this.arrived() && this.setPosition(this.targetPosition);
213
- }
214
- updateParams(e, t = 0) {
215
- t > 0 ? this.queueParams = {
216
- ...this.queuePosition ?? {},
217
- ...e,
218
- time: t
219
- } : (this.queuePosition = void 0, this.params = {
220
- ...this.params,
221
- ...e
222
- }, this.resetSolver());
223
- }
224
- setTargetPosition(e, t = 0) {
225
- t > 0 ? this.queuePosition = {
226
- ...this.queuePosition ?? {},
227
- position: e,
228
- time: t
229
- } : (this.queuePosition = void 0, this.targetPosition = e, this.resetSolver());
230
- }
231
- getCurrentPosition() {
232
- return this.currentPosition;
233
- }
234
- }
235
- function F(o, e, t, i = 0, s) {
236
- const r = s?.soft ?? !1, n = s?.stiffness ?? 100, a = s?.damping ?? 10, h = s?.mass ?? 1, l = t - o;
237
- if (r || 1 <= a / (2 * Math.sqrt(n * h))) {
238
- const c = -Math.sqrt(n / h), p = -c * l - e;
239
- return (u) => (u -= i, u < 0 ? o : t - (l + u * p) * Math.E ** (u * c));
240
- } else {
241
- const c = Math.sqrt(
242
- 4 * h * n - a ** 2
243
- ), p = (a * l - 2 * h * e) / c, u = 0.5 * c / h, f = -(0.5 * a) / h;
244
- return (d) => (d -= i, d < 0 ? o : t - (Math.cos(d * u) * l + Math.sin(d * u) * p) * Math.E ** (d * f));
245
- }
246
- }
247
- function _(o) {
248
- return (t) => (o(t + 1e-3) - o(t - 1e-3)) / (2 * 1e-3);
249
- }
250
- function x(o) {
251
- return _(o);
252
- }
253
- class O {
254
- constructor(e) {
255
- this.lyricPlayer = e, this.element.setAttribute(
256
- "class",
257
- this.lyricPlayer.style.classes.lyricLine
258
- ), this.rebuildStyle();
259
- }
260
- element = document.createElement("div");
261
- left = 0;
262
- top = 0;
263
- delay = 0;
264
- // 由 LyricPlayer 来设置
265
- lineSize = [0, 0];
266
- lineTransforms = {
267
- posX: new L(0),
268
- posY: new L(0)
269
- };
270
- measureSize() {
271
- return [
272
- this.element.clientWidth,
273
- this.element.clientHeight
274
- ];
275
- }
276
- lastStyle = "";
277
- show() {
278
- this.rebuildStyle();
279
- }
280
- hide() {
281
- this.rebuildStyle();
282
- }
283
- rebuildStyle() {
284
- let e = `transform:translate(${this.lineTransforms.posX.getCurrentPosition().toFixed(2)}px,${this.lineTransforms.posY.getCurrentPosition().toFixed(2)}px);`;
285
- !this.lyricPlayer.getEnableSpring() && this.isInSight && (e += `transition-delay:${this.delay}ms;`), e !== this.lastStyle && (this.lastStyle = e, this.element.setAttribute("style", e));
286
- }
287
- getElement() {
288
- return this.element;
289
- }
290
- setTransform(e = this.left, t = this.top, i = !1, s = 0) {
291
- this.left = e, this.top = t, this.delay = s * 1e3 | 0, i || !this.lyricPlayer.getEnableSpring() ? (i && this.element.classList.add(
292
- this.lyricPlayer.style.classes.tmpDisableTransition
293
- ), this.lineTransforms.posX.setPosition(e), this.lineTransforms.posY.setPosition(t), this.lyricPlayer.getEnableSpring() ? this.rebuildStyle() : this.show(), i && requestAnimationFrame(() => {
294
- this.element.classList.remove(
295
- this.lyricPlayer.style.classes.tmpDisableTransition
296
- );
297
- })) : (this.lineTransforms.posX.setTargetPosition(e, s), this.lineTransforms.posY.setTargetPosition(t, s));
298
- }
299
- update(e = 0) {
300
- this.lyricPlayer.getEnableSpring() && (this.lineTransforms.posX.update(e), this.lineTransforms.posY.update(e), this.isInSight ? this.show() : this.hide());
301
- }
302
- get isInSight() {
303
- const e = this.lineTransforms.posX.getCurrentPosition(), t = this.lineTransforms.posY.getCurrentPosition(), i = e + this.lineSize[0], s = t + this.lineSize[1], r = this.lyricPlayer.size[0], n = this.lyricPlayer.size[1];
304
- return !(e > r || t > n || i < 0 || s < 0);
305
- }
306
- dispose() {
307
- this.element.remove();
308
- }
309
- }
310
- function W(o) {
311
- const t = 2.5949095;
312
- return o < 0.5 ? Math.pow(2 * o, 2) * ((t + 1) * 2 * o - t) / 2 : (Math.pow(2 * o - 2, 2) * ((t + 1) * (o * 2 - 2) + t) + 2) / 2;
313
- }
314
- const g = (o, e, t) => Math.max(o, Math.min(e, t));
315
- class q {
316
- constructor(e) {
317
- this.lyricPlayer = e, this.element.className = this.lyricPlayer.style.classes.interludeDots, this.element.appendChild(this.dot0), this.element.appendChild(this.dot1), this.element.appendChild(this.dot2);
318
- }
319
- element = document.createElement("div");
320
- dot0 = document.createElement("span");
321
- dot1 = document.createElement("span");
322
- dot2 = document.createElement("span");
323
- left = 0;
324
- top = 0;
325
- scale = 1;
326
- lastStyle = "";
327
- currentInterlude;
328
- currentTime = 0;
329
- targetBreatheDuration = 1500;
330
- getElement() {
331
- return this.element;
332
- }
333
- setTransform(e = this.left, t = this.top) {
334
- this.left = e, this.top = t, this.update();
335
- }
336
- setInterlude(e) {
337
- this.currentInterlude = e, this.currentTime = e?.[0] ?? 0;
338
- }
339
- update(e = 0) {
340
- this.currentTime += e;
341
- let t = "";
342
- if (t += `transform:translate(${this.left.toFixed(
343
- 2
344
- )}px, ${this.top.toFixed(2)}px)`, this.currentInterlude) {
345
- const i = this.currentInterlude[1] - this.currentInterlude[0], s = this.currentTime - this.currentInterlude[0];
346
- if (s <= i) {
347
- const r = i / Math.ceil(i / this.targetBreatheDuration);
348
- let n = 1, a = 1;
349
- n *= Math.sin(1.5 * Math.PI - s / r * 2) / 10 + 1, s < 1e3 && (n *= 1 - Math.pow((1e3 - s) / 1e3, 2)), s < 500 ? a = 0 : s < 1e3 && (a *= (s - 500) / 500), i - s < 750 && (n *= 1 - W(
350
- (750 - (i - s)) / 750 / 2
351
- )), i - s < 375 && (a *= g(
352
- 0,
353
- (i - s) / 375,
354
- 1
355
- )), n = Math.max(0, n), t += ` scale(${n})`;
356
- const h = g(
357
- 0.25,
358
- s * 3 / i * 0.75,
359
- 1
360
- ), l = g(
361
- 0.25,
362
- (s - i / 3) * 3 / i * 0.75,
363
- 1
364
- ), c = g(
365
- 0.25,
366
- (s - i / 3 * 2) * 3 / i * 0.75,
367
- 1
368
- );
369
- this.dot0.style.opacity = `${g(
370
- 0,
371
- Math.max(0, a * h),
372
- 1
373
- )}`, this.dot1.style.opacity = `${g(
374
- 0,
375
- Math.max(0, a * l),
376
- 1
377
- )}`, this.dot2.style.opacity = `${g(
378
- 0,
379
- Math.max(0, a * c),
380
- 1
381
- )}`;
382
- } else
383
- t += " scale(0)", this.dot0.style.opacity = "0", this.dot1.style.opacity = "0", this.dot2.style.opacity = "0";
384
- } else
385
- t += " scale(0)", this.dot0.style.opacity = "0", this.dot1.style.opacity = "0", this.dot2.style.opacity = "0";
386
- t += ";", this.lastStyle !== t && (this.element.setAttribute("style", t), this.lastStyle = t);
387
- }
388
- dispose() {
389
- this.element.remove();
390
- }
391
- }
392
- const R = /^[\p{Unified_Ideograph}\u0800-\u9FFC]+$/u;
393
- function Y(o, e = "rgba(0,0,0,1)", t = "rgba(0,0,0,0.5)") {
394
- const i = 2 + o, s = o / i, r = (1 - s) / 2;
395
- return [
396
- `linear-gradient(to right,${e} ${r * 100}%,${t} ${(r + s) * 100}%)`,
397
- s,
398
- i
399
- ];
400
- }
401
- function X(o, e) {
402
- let t = [], i = [];
403
- const s = [];
404
- for (const r of o) {
405
- const n = e(r);
406
- t.push(n), i.push(r), n.length > 0 && n.trim().length === 0 ? (t.pop(), i.pop(), i.length === 1 ? s.push(i[0]) : i.length > 1 && s.push(i), s.push(r), t = [], i = []) : (!/^\s*[^\s]*\s*$/.test(t.join("")) || R.test(n)) && (t.pop(), i.pop(), i.length === 1 ? s.push(i[0]) : i.length > 1 && s.push(i), t = [n], i = [r]);
407
- }
408
- return i.length === 1 ? s.push(i[0]) : s.push(i), s;
409
- }
410
- function v(o) {
411
- return o.endTime - o.startTime >= 1e3 && o.word.length <= 7;
412
- }
413
- class G extends MouseEvent {
414
- constructor(e, t) {
415
- super(t.type, t), this.line = e;
416
- }
417
- }
418
- class V extends EventTarget {
419
- // rome-ignore lint/correctness/noUnreachableSuper: <explanation>
420
- constructor(e, t = {
421
- words: [],
422
- translatedLyric: "",
423
- romanLyric: "",
424
- startTime: 0,
425
- endTime: 0,
426
- isBG: !1,
427
- isDuet: !1
428
- }) {
429
- super(), this.lyricPlayer = e, this.lyricLine = t, this._prevParentEl = e.getElement(), this.element.setAttribute(
430
- "class",
431
- this.lyricPlayer.style.classes.lyricLine
432
- ), this.lyricLine.isBG && this.element.classList.add(this.lyricPlayer.style.classes.lyricBgLine), this.lyricLine.isDuet && this.element.classList.add(this.lyricPlayer.style.classes.lyricDuetLine), this.element.appendChild(document.createElement("div")), this.element.appendChild(document.createElement("div")), this.element.appendChild(document.createElement("div"));
433
- const i = this.element.children[0], s = this.element.children[1], r = this.element.children[2];
434
- i.setAttribute("class", this.lyricPlayer.style.classes.lyricMainLine), s.setAttribute("class", this.lyricPlayer.style.classes.lyricSubLine), r.setAttribute("class", this.lyricPlayer.style.classes.lyricSubLine), this.rebuildElement(), this.rebuildStyle();
435
- }
436
- element = document.createElement("div");
437
- left = 0;
438
- top = 0;
439
- scale = 1;
440
- blur = 0;
441
- delay = 0;
442
- splittedWords = [];
443
- // 由 LyricPlayer 来设置
444
- lineSize = [0, 0];
445
- lineTransforms = {
446
- posX: new L(0),
447
- posY: new L(0),
448
- scale: new L(1)
449
- };
450
- listenersMap = /* @__PURE__ */ new Map();
451
- onMouseEvent = (e) => {
452
- if (!this.dispatchEvent(new G(this, e)))
453
- return e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(), !1;
454
- };
455
- addEventListener(e, t, i) {
456
- if (super.addEventListener(e, t, i), t) {
457
- const s = this.listenersMap.get(e) ?? /* @__PURE__ */ new Set();
458
- s.size === 0 && this.element.addEventListener(e, this.onMouseEvent), s.add(t), this.listenersMap.set(e, s);
459
- }
460
- }
461
- removeEventListener(e, t, i) {
462
- if (super.removeEventListener(e, t, i), t) {
463
- const s = this.listenersMap.get(e);
464
- s && (s.delete(t), s.size === 0 && this.element.removeEventListener(e, this.onMouseEvent));
465
- }
466
- }
467
- isEnabled = !1;
468
- enable() {
469
- this.isEnabled = !0, this.element.classList.add("active");
470
- const e = this.element.children[0];
471
- this.splittedWords.forEach((t) => {
472
- t.elementAnimations.forEach((i) => {
473
- i.currentTime = 0, i.playbackRate = 1, i.play();
474
- });
475
- }), e.classList.add("active");
476
- }
477
- measureSize() {
478
- this._hide && (this._prevParentEl && this._prevParentEl.appendChild(this.element), this.element.style.display = "", this.element.style.visibility = "hidden");
479
- const e = [
480
- this.element.clientWidth,
481
- this.element.clientHeight
482
- ];
483
- return this._hide && (this._prevParentEl && this.element.remove(), this.element.style.display = "none", this.element.style.visibility = ""), e;
484
- }
485
- disable() {
486
- this.isEnabled = !1, this.element.classList.remove("active");
487
- const e = this.element.children[0];
488
- this.splittedWords.forEach((t) => {
489
- t.elementAnimations.forEach((i) => {
490
- i.id === "float-word" && (i.playbackRate = -1, i.play());
491
- });
492
- }), e.classList.remove("active");
493
- }
494
- setLine(e) {
495
- this.lyricLine = e, this.lyricLine.isBG ? this.element.classList.add(this.lyricPlayer.style.classes.lyricBgLine) : this.element.classList.remove(this.lyricPlayer.style.classes.lyricBgLine), this.lyricLine.isDuet ? this.element.classList.add(this.lyricPlayer.style.classes.lyricDuetLine) : this.element.classList.remove(
496
- this.lyricPlayer.style.classes.lyricDuetLine
497
- ), this.rebuildElement(), this.rebuildStyle();
498
- }
499
- getLine() {
500
- return this.lyricLine;
501
- }
502
- _hide = !0;
503
- _prevParentEl = null;
504
- lastStyle = "";
505
- show() {
506
- this._hide = !1, this._prevParentEl && (this._prevParentEl.appendChild(this.element), this._prevParentEl = null), this.rebuildStyle();
507
- }
508
- hide() {
509
- this._hide = !0, this.element.parentElement && (this._prevParentEl = this.element.parentElement, this.element.remove()), this.rebuildStyle();
510
- }
511
- rebuildStyle() {
512
- if (this._hide) {
513
- this.lastStyle !== "display:none;transform:translate(0,-10000px);" && (this.lastStyle = "display:none;transform:translate(0,-10000px);", this.element.setAttribute(
514
- "style",
515
- "display:none;transform:translate(0,-10000px);"
516
- ));
517
- return;
518
- }
519
- let e = `transform:translate(${this.lineTransforms.posX.getCurrentPosition().toFixed(2)}px,${this.lineTransforms.posY.getCurrentPosition().toFixed(2)}px) scale(${this.lineTransforms.scale.getCurrentPosition().toFixed(4)});`;
520
- !this.lyricPlayer.getEnableSpring() && this.isInSight && (e += `transition-delay:${this.delay}ms;`), e += `filter:blur(${Math.min(32, this.blur)}px);`, e !== this.lastStyle && (this.lastStyle = e, this.element.setAttribute("style", e));
521
- }
522
- rebuildElement() {
523
- const e = this.element.children[0], t = this.element.children[1], i = this.element.children[2];
524
- if (this.lyricPlayer._getIsNonDynamic()) {
525
- e.innerText = this.lyricLine.words.map((r) => r.word).join(""), t.innerText = this.lyricLine.translatedLyric, i.innerText = this.lyricLine.romanLyric;
526
- return;
527
- }
528
- const s = X(this.lyricLine.words, (r) => r.word);
529
- e.innerHTML = "", this.splittedWords = [];
530
- for (const r of s)
531
- if (r instanceof Array) {
532
- const n = r.map((l) => v(l)).reduce((l, c) => l || c, !1), a = r.reduce(
533
- (l, c) => (l.endTime = Math.max(l.endTime, c.endTime), l.startTime = Math.min(l.startTime, c.startTime), l.word += c.word, l),
534
- { word: "", startTime: 1 / 0, endTime: -1 / 0 }
535
- ), h = document.createElement("span");
536
- for (const l of r) {
537
- const c = document.createElement("span"), p = this.initFloatAnimation(
538
- a,
539
- c
540
- );
541
- if (n) {
542
- c.classList.add("emphasize");
543
- const u = [];
544
- for (const d of l.word.trim().split("")) {
545
- const m = document.createElement("span");
546
- m.innerText = d, u.push(m), c.appendChild(m);
547
- }
548
- const f = {
549
- ...l,
550
- mainElement: c,
551
- subElements: u,
552
- elementAnimations: [p],
553
- width: 0,
554
- height: 0,
555
- shouldEmphasize: n
556
- };
557
- f.elementAnimations.push(
558
- ...this.initEmphasizeAnimation(f)
559
- ), this.splittedWords.push(f);
560
- } else
561
- c.innerText = l.word, this.splittedWords.push({
562
- ...l,
563
- mainElement: c,
564
- subElements: [],
565
- elementAnimations: [p],
566
- width: 0,
567
- height: 0,
568
- shouldEmphasize: n
569
- });
570
- h.appendChild(c);
571
- }
572
- a.word.trimStart() !== a.word && e.appendChild(document.createTextNode(" ")), e.appendChild(h), a.word.trimEnd() !== a.word && e.appendChild(document.createTextNode(" "));
573
- } else if (r.word.trim().length === 0)
574
- e.appendChild(document.createTextNode(" "));
575
- else {
576
- const n = v(r), a = document.createElement("span"), h = {
577
- ...r,
578
- mainElement: a,
579
- subElements: [],
580
- elementAnimations: [this.initFloatAnimation(r, a)],
581
- width: 0,
582
- height: 0,
583
- shouldEmphasize: n
584
- };
585
- if (n) {
586
- a.classList.add("emphasize");
587
- const l = [];
588
- for (const c of r.word.trim().split("")) {
589
- const p = document.createElement("span");
590
- p.innerText = c, l.push(p), a.appendChild(p);
591
- }
592
- h.subElements = l, h.elementAnimations.push(
593
- ...this.initEmphasizeAnimation(h)
594
- );
595
- } else
596
- a.innerText = r.word.trim();
597
- r.word.trimStart() !== r.word && e.appendChild(document.createTextNode(" ")), e.appendChild(a), r.word.trimEnd() !== r.word && e.appendChild(document.createTextNode(" ")), this.splittedWords.push(h);
598
- }
599
- t.innerText = this.lyricLine.translatedLyric, i.innerText = this.lyricLine.romanLyric;
600
- }
601
- initFloatAnimation(e, t) {
602
- const i = e.startTime - this.lyricLine.startTime, s = Math.max(1e3, e.endTime - e.startTime), r = t.animate(
603
- [
604
- {
605
- transform: "translateY(0px)"
606
- },
607
- {
608
- transform: "translateY(-3%)"
609
- }
610
- ],
611
- {
612
- duration: isFinite(s) ? s : 0,
613
- delay: isFinite(i) ? i : 0,
614
- id: "float-word",
615
- composite: "add",
616
- fill: "both"
617
- }
618
- );
619
- return r.pause(), r;
620
- }
621
- initEmphasizeAnimation(e) {
622
- const t = e.startTime - this.lyricLine.startTime, i = e.endTime - e.startTime;
623
- return e.subElements.map((s, r, n) => {
624
- const a = Math.max(1e3, e.endTime - e.startTime), h = t + i / n.length * r, l = s.animate(
625
- [
626
- {
627
- offset: 0,
628
- transform: "translate3d(0, 0, 0px)",
629
- filter: "drop-shadow(0 0 0 var(--amll-lyric-view-color,white))"
630
- },
631
- {
632
- offset: 0.5,
633
- transform: "translate3d(0, -0.02em, 20px)",
634
- filter: "drop-shadow(0 0 0.05em var(--amll-lyric-view-color,white))"
635
- },
636
- {
637
- offset: 1,
638
- transform: "translate3d(0, 0, 0)",
639
- filter: "drop-shadow(0 0 0 var(--amll-lyric-view-color,white))"
640
- }
641
- ],
642
- {
643
- duration: isFinite(a) ? a : 0,
644
- delay: isFinite(h) ? h : 0,
645
- id: "glow-word",
646
- iterations: 1,
647
- composite: "replace",
648
- easing: "ease-in-out",
649
- fill: "both"
650
- }
651
- );
652
- return l.pause(), l;
653
- });
654
- }
655
- updateMaskImage() {
656
- this._hide && (this._prevParentEl && this._prevParentEl.appendChild(this.element), this.element.style.display = "", this.element.style.visibility = "hidden"), this.splittedWords.forEach((e) => {
657
- const t = e.mainElement;
658
- if (t) {
659
- e.width = t.clientWidth, e.height = t.clientHeight;
660
- const i = e.height / 2, [s, r, n] = Y(
661
- i / e.width,
662
- "rgba(0,0,0,1)",
663
- "rgba(0,0,0,0.25)"
664
- ), a = `${n * 100}% 100%`;
665
- this.lyricPlayer.supportMaskImage ? (t.style.maskImage = s, t.style.maskOrigin = "left", t.style.maskSize = a) : (t.style.webkitMaskImage = s, t.style.webkitMaskOrigin = "left", t.style.webkitMaskSize = a);
666
- const h = e.width + i, l = `clamp(${-h}px,calc(${-h}px + (var(--amll-player-time) - ${e.startTime})*${h / Math.abs(e.endTime - e.startTime)}px),0px) 0px, left top`;
667
- t.style.maskPosition = l, t.style.webkitMaskPosition = l;
668
- }
669
- }), this._hide && (this._prevParentEl && this.element.remove(), this.element.style.display = "none", this.element.style.visibility = "");
670
- }
671
- getElement() {
672
- return this.element;
673
- }
674
- setTransform(e = this.left, t = this.top, i = this.scale, s = 1, r = 0, n = !1, a = 0) {
675
- const h = this.isInSight, l = this.lyricPlayer.getEnableSpring();
676
- this.left = e, this.top = t, this.scale = i, this.delay = a * 1e3 | 0;
677
- const c = this.element.children[0], p = this.element.children[1], u = this.element.children[2];
678
- if (c.style.opacity = `${s}`, p.style.opacity = `${s / 2}`, u.style.opacity = `${s / 2}`, n || !l) {
679
- if (this.blur = Math.min(32, r), n && this.element.classList.add(
680
- this.lyricPlayer.style.classes.tmpDisableTransition
681
- ), this.lineTransforms.posX.setPosition(e), this.lineTransforms.posY.setPosition(t), this.lineTransforms.scale.setPosition(i), l)
682
- this.rebuildStyle();
683
- else {
684
- const f = this.isInSight;
685
- h || f ? this.show() : this.hide();
686
- }
687
- n && requestAnimationFrame(() => {
688
- this.element.classList.remove(
689
- this.lyricPlayer.style.classes.tmpDisableTransition
690
- );
691
- });
692
- } else
693
- this.lineTransforms.posX.setTargetPosition(e, a), this.lineTransforms.posY.setTargetPosition(t, a), this.lineTransforms.scale.setTargetPosition(i), this.blur !== Math.min(32, r) && (this.blur = Math.min(32, r), this.element.style.filter = `blur(${Math.min(32, r)}px)`);
694
- }
695
- update(e = 0) {
696
- this.lyricPlayer.getEnableSpring() && (this.lineTransforms.posX.update(e), this.lineTransforms.posY.update(e), this.lineTransforms.scale.update(e), this.isInSight ? this.show() : this.hide());
697
- }
698
- _getDebugTargetPos() {
699
- return `[位移: ${this.left}, ${this.top}; 缩放: ${this.scale}; 延时: ${this.delay}]`;
700
- }
701
- get isInSight() {
702
- const e = this.lineTransforms.posX.getCurrentPosition(), t = this.lineTransforms.posY.getCurrentPosition(), i = e + this.lineSize[0], s = t + this.lineSize[1], r = this.lyricPlayer.size[0], n = this.lyricPlayer.size[1];
703
- return !(e > r || i < 0 || t > n || s < 0);
704
- }
705
- dispose() {
706
- this.element.remove();
707
- }
708
- }
709
- const N = k(D());
710
- class H extends MouseEvent {
711
- constructor(e, t, i) {
712
- super(`line-${i.type}`, i), this.lineIndex = e, this.line = t;
713
- }
714
- }
715
- class ne extends EventTarget {
716
- element = document.createElement("div");
717
- currentTime = 0;
718
- lyricLines = [];
719
- processedLines = [];
720
- lyricLinesEl = [];
721
- lyricLinesSize = /* @__PURE__ */ new WeakMap();
722
- lyricLinesIndexes = /* @__PURE__ */ new WeakMap();
723
- hotLines = /* @__PURE__ */ new Set();
724
- bufferedLines = /* @__PURE__ */ new Set();
725
- scrollToIndex = 0;
726
- allowScroll = !0;
727
- scrolledHandler = 0;
728
- isScrolled = !1;
729
- invokedByScrollEvent = !1;
730
- scrollOffset = 0;
731
- hidePassedLines = !1;
732
- resizeObserver = new ResizeObserver((e) => {
733
- const t = e[0].contentRect;
734
- this.size[0] = t.width, this.size[1] = t.height;
735
- const i = getComputedStyle(e[0].target), s = this.element.clientWidth - parseFloat(i.paddingLeft) - parseFloat(i.paddingRight), r = this.element.clientHeight - parseFloat(i.paddingTop) - parseFloat(i.paddingBottom);
736
- this.innerSize[0] = s, this.innerSize[1] = r, this.rebuildStyle(), this.calcLayout(!0, !0), this.lyricLinesEl.forEach((n) => n.updateMaskImage());
737
- });
738
- posXSpringParams = {
739
- mass: 1,
740
- damping: 10,
741
- stiffness: 100
742
- };
743
- posYSpringParams = {
744
- mass: 1,
745
- damping: 15,
746
- stiffness: 100
747
- };
748
- scaleSpringParams = {
749
- mass: 1,
750
- damping: 20,
751
- stiffness: 100
752
- };
753
- emUnit = Math.max(Math.min(innerHeight * 0.05, innerWidth * 0.1), 12);
754
- padding = this.emUnit;
755
- enableBlur = !0;
756
- enableScale = !0;
757
- interludeDots;
758
- interludeDotsSize = [0, 0];
759
- bottomLine;
760
- supportPlusLighter = CSS.supports("mix-blend-mode", "plus-lighter");
761
- supportMaskImage = CSS.supports("mask-image", "none");
762
- disableSpring = !1;
763
- alignAnchor = "center";
764
- alignPosition = 0.5;
765
- isNonDynamic = !1;
766
- size = [0, 0];
767
- innerSize = [0, 0];
768
- onLineClickedHandler = (e) => {
769
- const t = new H(
770
- this.lyricLinesIndexes.get(e.line) ?? -1,
771
- e.line,
772
- e
773
- );
774
- this.dispatchEvent(t) || (e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation());
775
- };
776
- _getIsNonDynamic() {
777
- return this.isNonDynamic;
778
- }
779
- /**
780
- * 设置是否使用物理弹簧算法实现歌词动画效果,默认启用
781
- *
782
- * 如果启用,则会通过弹簧算法实时处理歌词位置,但是需要性能足够强劲的电脑方可流畅运行
783
- *
784
- * 如果不启用,则会回退到基于 `transition` 的过渡效果,对低性能的机器比较友好,但是效果会比较单一
785
- */
786
- setEnableSpring(e = !0) {
787
- this.disableSpring = !e, e ? this.element.classList.remove(this.style.classes.disableSpring) : this.element.classList.add(this.style.classes.disableSpring), this.calcLayout(!0);
788
- }
789
- /**
790
- * 获取当前是否启用了物理弹簧
791
- * @returns 是否启用物理弹簧
792
- */
793
- getEnableSpring() {
794
- return !this.disableSpring;
795
- }
796
- /**
797
- * 是否启用歌词行缩放效果,默认启用
798
- *
799
- * 如果启用,非选中的歌词行会轻微缩小以凸显当前播放歌词行效果
800
- *
801
- * 此效果对性能影响微乎其微,推荐启用
802
- * @param enable 是否启用歌词行缩放效果
803
- */
804
- setEnableScale(e = !0) {
805
- this.enableScale = e, this.calcLayout();
806
- }
807
- /**
808
- * 获取当前是否启用了歌词行缩放效果
809
- * @returns 是否启用歌词行缩放效果
810
- */
811
- getEnableScale() {
812
- return this.enableScale;
813
- }
814
- style = N.createStyleSheet({
815
- lyricPlayer: {
816
- userSelect: "none",
817
- fontSize: "var(--amll-lyric-player-font-size,max(min(5vh, 10vw), 12px))",
818
- padding: "1em",
819
- margin: "-1em",
820
- width: "100%",
821
- height: "100%",
822
- overflow: "hidden",
823
- boxSizing: "content-box",
824
- maxWidth: "100%",
825
- maxHeight: "100%",
826
- zIndex: 1,
827
- color: "var(--amll-lyric-view-color,white)",
828
- mixBlendMode: "plus-lighter",
829
- contain: "strict",
830
- "&:hover": {
831
- "& $lyricLine": {
832
- filter: "unset !important"
833
- }
834
- }
835
- },
836
- lyricLine: {
837
- position: "absolute",
838
- transformOrigin: "left",
839
- width: "var(--amll-lyric-player-width,100%)",
840
- height: "fit-content",
841
- padding: "2vh 1.05em",
842
- margin: "0 -1em",
843
- contain: "content",
844
- willChange: "filter,transform,opacity",
845
- transition: "filter 0.25s, background-color 0.25s, box-shadow 0.25s",
846
- boxSizing: "content-box",
847
- borderRadius: "8px",
848
- "&:hover": {
849
- backgroundColor: "var(--amll-lyric-view-hover-bg-color,#fff1)",
850
- boxShadow: "0 0 0 8px var(--amll-lyric-view-hover-bg-color,#fff1)"
851
- },
852
- "&:active": {
853
- boxShadow: "0 0 0 4px var(--amll-lyric-view-hover-bg-color,#fff1)"
854
- }
855
- },
856
- "@media (max-width: 1024px)": {
857
- lyricLine: {
858
- padding: "1vh 1em"
859
- }
860
- },
861
- lyricDuetLine: {
862
- textAlign: "right",
863
- transformOrigin: "right"
864
- },
865
- lyricBgLine: {
866
- opacity: 0,
867
- fontSize: "max(50%, 10px)",
868
- transition: "opacity 0.25s",
869
- "&.active": {
870
- transition: "opacity 0.25s 0.25s",
871
- opacity: 0.75
872
- }
873
- },
874
- lyricMainLine: {
875
- transition: "opacity 0.3s 0.25s",
876
- willChange: "opacity",
877
- margin: "-1em",
878
- padding: "1em",
879
- "& span": {
880
- display: "inline-block"
881
- },
882
- "& > span": {
883
- whiteSpace: "pre-wrap",
884
- maxLines: "1",
885
- // willChange: "transform,display,mask-image",
886
- "&.emphasize": {
887
- transformStyle: "preserve-3d",
888
- perspective: "50vw",
889
- padding: "1em",
890
- margin: "-1em"
891
- }
892
- }
893
- },
894
- lyricSubLine: {
895
- fontSize: "max(0.5em, 10px)",
896
- transition: "opacity 0.3s 0.25s",
897
- opacity: 0.5
898
- },
899
- disableSpring: {
900
- "& > *": {
901
- transition: "filter 0.25s, transform 0.5s, background-color 0.25s, box-shadow 0.25s"
902
- }
903
- },
904
- interludeDots: {
905
- height: "clamp(0.5em,1vh,3em)",
906
- transformOrigin: "center",
907
- width: "fit-content",
908
- padding: "2.5% 0",
909
- position: "absolute",
910
- display: "flex",
911
- gap: "0.25em",
912
- left: "1em",
913
- "& > *": {
914
- height: "clamp(0.5em,1vh,3em)",
915
- display: "inline-block",
916
- borderRadius: "50%",
917
- aspectRatio: "1 / 1",
918
- backgroundColor: "var(--amll-lyric-view-color,white)",
919
- marginRight: "4px"
920
- },
921
- "&.duet": {
922
- right: "1em",
923
- transformOrigin: "center"
924
- }
925
- },
926
- "@supports (mix-blend-mode: plus-lighter)": {
927
- lyricSubLine: {
928
- opacity: 0.3
929
- }
930
- },
931
- tmpDisableTransition: {
932
- transition: "none !important"
933
- }
934
- });
935
- onPageShow = () => {
936
- this.calcLayout(!0, !0);
937
- };
938
- constructor() {
939
- super(), this.interludeDots = new q(this), this.bottomLine = new O(this), this.element.setAttribute("class", this.style.classes.lyricPlayer), this.disableSpring && this.element.classList.add(this.style.classes.disableSpring), this.rebuildStyle(), this.resizeObserver.observe(this.element), this.element.appendChild(this.interludeDots.getElement()), this.element.appendChild(this.bottomLine.getElement()), this.style.attach(), this.interludeDots.setTransform(0, 200), window.addEventListener("pageshow", this.onPageShow), this.element.addEventListener("wheel", (e) => {
940
- this.allowScroll && (this.isScrolled = !0, clearTimeout(this.scrolledHandler), this.scrolledHandler = setTimeout(() => {
941
- this.isScrolled = !1, this.scrollOffset = 0;
942
- }, 5e3), this.invokedByScrollEvent = !0, e.deltaMode === e.DOM_DELTA_PIXEL ? (this.scrollOffset += e.deltaY, this.calcLayout(!0)) : (this.scrollOffset += e.deltaY * 50, this.calcLayout(!1)), this.invokedByScrollEvent = !1);
943
- });
944
- }
945
- /**
946
- * 获取当前播放时间里是否处于间奏区间
947
- * 如果是则会返回单位为毫秒的始末时间
948
- * 否则返回 undefined
949
- *
950
- * 这个只允许内部调用
951
- * @returns [开始时间,结束时间,大概处于的歌词行ID,下一句是否为对唱歌词] 或 undefined 如果不处于间奏区间
952
- */
953
- getCurrentInterlude() {
954
- if (this.bufferedLines.size > 0)
955
- return;
956
- const e = this.currentTime + 20, t = this.scrollToIndex;
957
- if (t === 0) {
958
- if (this.processedLines[0]?.startTime && this.processedLines[0].startTime > e)
959
- return [
960
- e,
961
- this.processedLines[0].startTime,
962
- -2,
963
- this.processedLines[0].isDuet
964
- ];
965
- } else if (this.processedLines[t]?.endTime && this.processedLines[t + 1]?.startTime && this.processedLines[t + 1].startTime > e && this.processedLines[t].endTime < e)
966
- return [
967
- Math.max(this.processedLines[t].endTime, e),
968
- this.processedLines[t + 1].startTime,
969
- t,
970
- this.processedLines[t + 1].isDuet
971
- ];
972
- }
973
- /**
974
- * 重建样式
975
- *
976
- * 这个只允许内部调用
977
- */
978
- rebuildStyle() {
979
- let e = "";
980
- e += "--amll-lyric-player-width:", e += this.innerSize[0] - this.padding * 2, e += "px;", e += "--amll-lyric-player-height:", e += this.innerSize[1] - this.padding * 2, e += "px;", this.element.setAttribute("style", e);
981
- }
982
- /**
983
- * 设置是否隐藏已经播放过的歌词行,默认不隐藏
984
- * @param hide 是否隐藏已经播放过的歌词行,默认不隐藏
985
- */
986
- setHidePassedLines(e) {
987
- this.hidePassedLines = e, this.calcLayout();
988
- }
989
- /**
990
- * 设置是否启用歌词行的模糊效果
991
- * @param enable 是否启用
992
- */
993
- setEnableBlur(e) {
994
- this.enableBlur !== e && (this.enableBlur = e, this.calcLayout());
995
- }
996
- /**
997
- * 设置当前播放歌词,要注意传入后这个数组内的信息不得修改,否则会发生错误
998
- * @param lines 歌词数组
999
- */
1000
- setLyricLines(e) {
1001
- this.lyricLines = e;
1002
- const t = 750;
1003
- this.processedLines = e.filter(
1004
- (i) => i.words.reduce((s, r) => s + r.word.trim().length, 0) > 0
1005
- ).map((i, s, r) => {
1006
- if (i.isBG)
1007
- return {
1008
- ...i
1009
- };
1010
- if (s === 0)
1011
- return {
1012
- ...i,
1013
- startTime: Math.max(i.startTime - t, 0)
1014
- };
1015
- {
1016
- const n = r[s - 1], a = r[s - 2];
1017
- if (n?.isBG && a) {
1018
- if (a.endTime < i.startTime)
1019
- return {
1020
- ...i,
1021
- startTime: Math.max(a.endTime, i.startTime - t) || i.startTime
1022
- };
1023
- } else if (n?.endTime && n.endTime < i.startTime)
1024
- return {
1025
- ...i,
1026
- startTime: Math.max(n?.endTime, i.startTime - t) || i.startTime
1027
- };
1028
- return {
1029
- ...i
1030
- };
1031
- }
1032
- }), this.isNonDynamic = !0;
1033
- for (const i of this.processedLines)
1034
- if (i.words.length > 1) {
1035
- this.isNonDynamic = !1;
1036
- break;
1037
- }
1038
- this.processedLines.forEach((i, s, r) => {
1039
- const n = r[s + 1], a = i.words[i.words.length - 1];
1040
- a && v(a) && (n ? n.startTime > i.endTime && (i.endTime = Math.min(i.endTime + 1500, n.startTime)) : i.endTime = i.endTime + 1500);
1041
- }), this.processedLines.forEach((i, s, r) => {
1042
- if (i.isBG)
1043
- return;
1044
- const n = r[s + 1];
1045
- n?.isBG && (n.startTime = Math.min(n.startTime, i.startTime));
1046
- }), this.lyricLinesEl.forEach((i) => {
1047
- i.removeEventListener("click", this.onLineClickedHandler), i.removeEventListener("contextmenu", this.onLineClickedHandler), i.dispose();
1048
- }), this.lyricLinesEl = this.processedLines.map((i) => {
1049
- const s = new V(this, i);
1050
- return s.addEventListener("click", this.onLineClickedHandler), s.addEventListener("contextmenu", this.onLineClickedHandler), s;
1051
- }), this.lyricLinesEl.forEach((i, s) => {
1052
- this.element.appendChild(i.getElement()), this.lyricLinesIndexes.set(i, s), i.updateMaskImage();
1053
- }), this.interludeDots.setInterlude(void 0), this.hotLines.clear(), this.bufferedLines.clear(), this.setLinePosXSpringParams({}), this.setLinePosYSpringParams({}), this.setLineScaleSpringParams({}), this.setCurrentTime(0, !0), this.calcLayout(!0, !0);
1054
- }
1055
- /**
1056
- * 重置用户滚动状态
1057
- *
1058
- * 请在用户完成滚动点击跳转歌词时调用本事件再调用 `calcLayout` 以正确滚动到目标位置
1059
- */
1060
- resetScroll() {
1061
- this.isScrolled = !1, this.scrollOffset = 0, this.invokedByScrollEvent = !1, clearTimeout(this.scrolledHandler), this.scrolledHandler = 0;
1062
- }
1063
- /**
1064
- * 重新布局定位歌词行的位置,调用完成后再逐帧调用 `update`
1065
- * 函数即可让歌词通过动画移动到目标位置。
1066
- *
1067
- * 函数有一个 `force` 参数,用于指定是否强制修改布局,也就是不经过动画直接调整元素位置和大小。
1068
- *
1069
- * 此函数还有一个 `reflow` 参数,用于指定是否需要重新计算布局
1070
- *
1071
- * 因为计算布局必定会导致浏览器重排布局,所以会大幅度影响流畅度和性能,故请只在以下情况下将其​设置为 true:
1072
- *
1073
- * 1. 歌词页面大小发生改变时(这个组件会自行处理)
1074
- * 2. 加载了新的歌词时(不论前后歌词是否完全一样)
1075
- * 3. 用户自行跳转了歌曲播放位置(不论距离远近)
1076
- *
1077
- * @param force 是否不经过动画直接修改布局定位
1078
- * @param reflow 是否进行重新布局(重新计算每行歌词大小)
1079
- */
1080
- calcLayout(e = !1, t = !1) {
1081
- t && (this.emUnit = parseFloat(getComputedStyle(this.element).fontSize), this.lyricLinesEl.forEach((d) => {
1082
- const m = d.measureSize();
1083
- this.lyricLinesSize.set(d, m), d.lineSize = m;
1084
- }), this.interludeDotsSize[0] = this.interludeDots.getElement().clientWidth, this.interludeDotsSize[1] = this.interludeDots.getElement().clientHeight, this.bottomLine.lineSize = this.bottomLine.measureSize());
1085
- const i = this.getCurrentInterlude();
1086
- let s = -this.scrollOffset, r = this.scrollToIndex, n = 0;
1087
- i ? (n = i[1] - i[0], n >= 5e3 && this.lyricLinesEl[i[2] + 1] && (r = i[2] + 1)) : this.interludeDots.setInterlude(void 0);
1088
- const a = this.enableScale ? 0.95 : 1, h = this.lyricLinesEl.slice(0, r).reduce(
1089
- (d, m) => d + (m.getLine().isBG ? 0 : this.lyricLinesSize.get(m)?.[1] ?? 0),
1090
- 0
1091
- );
1092
- s -= h, s += this.size[1] * this.alignPosition;
1093
- const l = this.lyricLinesEl[r];
1094
- if (l) {
1095
- const d = this.lyricLinesSize.get(l)?.[1] ?? 0;
1096
- switch (this.alignAnchor) {
1097
- case "bottom":
1098
- s -= d;
1099
- break;
1100
- case "center":
1101
- s -= d / 2;
1102
- break;
1103
- }
1104
- }
1105
- const c = Math.max(...this.bufferedLines);
1106
- let p = 0, u = 0.05, f = !1;
1107
- this.lyricLinesEl.forEach((d, m) => {
1108
- const T = this.bufferedLines.has(m), w = T || m >= this.scrollToIndex && m < c, E = d.getLine();
1109
- E.isDuet && this.size[0] - (this.lyricLinesSize.get(d)?.[0] ?? 0), !f && n >= 5e3 && (m === this.scrollToIndex && i?.[2] === -2 || m === this.scrollToIndex + 1) && (f = !0, this.interludeDots.setTransform(this.padding, s), i && this.interludeDots.setInterlude([i[0], i[1]]), s += this.interludeDotsSize[1]);
1110
- const M = this.hidePassedLines && m < (i ? i[2] + 1 : this.scrollToIndex) ? 0 : T ? 1 : 1 / 3;
1111
- d.setTransform(
1112
- this.padding,
1113
- s,
1114
- w ? 1 : a,
1115
- M,
1116
- !this.invokedByScrollEvent && this.enableBlur ? w ? 0 : 1 + (m < this.scrollToIndex ? Math.abs(this.scrollToIndex - m) : Math.abs(m - Math.max(this.scrollToIndex, c))) : 0,
1117
- e,
1118
- p
1119
- ), E.isBG && w ? s += this.lyricLinesSize.get(d)?.[1] ?? 0 : E.isBG || (s += this.lyricLinesSize.get(d)?.[1] ?? 0), s >= 0 && (p += u, u /= 1.2);
1120
- }), this.bottomLine.setTransform(this.padding, s, e, p);
1121
- }
1122
- /**
1123
- * 获取当前歌词的播放位置
1124
- *
1125
- * 一般和最后调用 `setCurrentTime` 给予的参数一样
1126
- * @returns 当前播放位置
1127
- */
1128
- getCurrentTime() {
1129
- return this.currentTime;
1130
- }
1131
- /**
1132
- * 获取当前歌词数组
1133
- *
1134
- * 一般和最后调用 `setLyricLines` 给予的参数一样
1135
- * @returns 当前歌词数组
1136
- */
1137
- getLyricLines() {
1138
- return this.lyricLines;
1139
- }
1140
- getElement() {
1141
- return this.element;
1142
- }
1143
- /**
1144
- * 获取一个特殊的底栏元素,默认是空白的,可以往内部添加任意元素
1145
- *
1146
- * 这个元素始终在歌词的底部,可以用于显示歌曲创作者等信息
1147
- *
1148
- * 但是请勿删除该元素,只能在内部存放元素
1149
- *
1150
- * @returns 一个元素,可以往内部添加任意元素
1151
- */
1152
- getBottomLineElement() {
1153
- return this.bottomLine.getElement();
1154
- }
1155
- /**
1156
- * 设置目标歌词行的对齐方式,默认为 `center`
1157
- *
1158
- * - 设置成 `top` 的话将会向目标歌词行的顶部对齐
1159
- * - 设置成 `bottom` 的话将会向目标歌词行的底部对齐
1160
- * - 设置成 `center` 的话将会向目标歌词行的垂直中心对齐
1161
- * @param alignAnchor 歌词行对齐方式,详情见函数说明
1162
- */
1163
- setAlignAnchor(e) {
1164
- this.alignAnchor = e;
1165
- }
1166
- /**
1167
- * 设置默认的歌词行对齐位置,相对于整个歌词播放组件的大小位置,默认为 `0.5`
1168
- * @param alignPosition 一个 `[0.0-1.0]` 之间的任意数字,代表组件高度由上到下的比例位置
1169
- */
1170
- setAlignPosition(e) {
1171
- this.alignPosition = e;
1172
- }
1173
- /**
1174
- * 设置当前播放进度,单位为毫秒且**必须是整数**,此时将会更新内部的歌词进度信息
1175
- * 内部会根据调用间隔和播放进度自动决定如何滚动和显示歌词,所以这个的调用频率越快越准确越好
1176
- *
1177
- * 调用完成后,可以每帧调用 `update` 函数来执行歌词动画效果
1178
- * @param time 当前播放进度,单位为毫秒
1179
- */
1180
- setCurrentTime(e, t = !1) {
1181
- if (this.currentTime = e, this._getIsNonDynamic() || this.element.style.setProperty("--amll-player-time", `${e}`), this.isScrolled)
1182
- return;
1183
- const i = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), r = /* @__PURE__ */ new Set();
1184
- this.hotLines.forEach((n) => {
1185
- const a = this.processedLines[n];
1186
- if (a) {
1187
- if (a.isBG)
1188
- return;
1189
- const h = this.processedLines[n + 1];
1190
- if (h?.isBG) {
1191
- const l = Math.min(a.startTime, h?.startTime), c = Math.max(a.endTime, h?.endTime);
1192
- (l > e || c <= e) && (this.hotLines.delete(n), i.add(n), this.hotLines.delete(n + 1), i.add(n + 1), t && (this.lyricLinesEl[n].disable(), this.lyricLinesEl[n + 1].disable()));
1193
- } else
1194
- (a.startTime > e || a.endTime <= e) && (this.hotLines.delete(n), i.add(n), t && this.lyricLinesEl[n].disable());
1195
- } else
1196
- this.hotLines.delete(n), i.add(n), t && this.lyricLinesEl[n].disable();
1197
- }), this.processedLines.forEach((n, a, h) => {
1198
- !n.isBG && n.startTime <= e && n.endTime > e && (this.hotLines.has(a) || (this.hotLines.add(a), r.add(a), t && this.lyricLinesEl[a].enable(), h[a + 1]?.isBG && (this.hotLines.add(a + 1), r.add(a + 1), t && this.lyricLinesEl[a + 1].enable())));
1199
- }), this.bufferedLines.forEach((n) => {
1200
- this.hotLines.has(n) || (s.add(n), t && this.lyricLinesEl[n].disable());
1201
- }), t ? (this.bufferedLines.size > 0 ? this.scrollToIndex = Math.min(...this.bufferedLines) : this.scrollToIndex = this.processedLines.findIndex(
1202
- (n) => n.startTime >= e
1203
- ), this.bufferedLines.clear(), this.hotLines.forEach((n) => this.bufferedLines.add(n)), this.calcLayout(!0)) : (s.size > 0 || r.size > 0) && (s.size === 0 && r.size > 0 ? (r.forEach((n) => {
1204
- this.bufferedLines.add(n), this.lyricLinesEl[n].enable();
1205
- }), this.scrollToIndex = Math.min(...this.bufferedLines), this.calcLayout()) : r.size === 0 && s.size > 0 ? $(s, this.bufferedLines) && (this.bufferedLines.forEach((n) => {
1206
- this.hotLines.has(n) || (this.bufferedLines.delete(n), this.lyricLinesEl[n].disable());
1207
- }), this.calcLayout()) : (r.forEach((n) => {
1208
- this.bufferedLines.add(n), this.lyricLinesEl[n].enable();
1209
- }), s.forEach((n) => {
1210
- this.bufferedLines.delete(n), this.lyricLinesEl[n].disable();
1211
- }), this.bufferedLines.size > 0 && (this.scrollToIndex = Math.min(...this.bufferedLines)), this.calcLayout()));
1212
- }
1213
- /**
1214
- * 更新动画,这个函数应该被逐帧调用或者在以下情况下调用一次:
1215
- *
1216
- * 1. 刚刚调用完设置歌词函数的时候
1217
- * @param delta 距离上一次被调用到现在的时长,单位为毫秒(可为浮点数)
1218
- */
1219
- update(e = 0) {
1220
- const t = e / 1e3;
1221
- this.interludeDots.update(e), this.bottomLine.update(t), this.lyricLinesEl.forEach((i) => i.update(t));
1222
- }
1223
- /**
1224
- * 设置所有歌词行在横坐标上的弹簧属性,包括重量、弹力和阻力。
1225
- *
1226
- * @param params 需要设置的弹簧属性,提供的属性将会覆盖原来的属性,未提供的属性将会保持原样
1227
- */
1228
- setLinePosXSpringParams(e) {
1229
- this.posXSpringParams = {
1230
- ...this.posXSpringParams,
1231
- ...e
1232
- }, this.bottomLine.lineTransforms.posX.updateParams(this.posXSpringParams), this.lyricLinesEl.forEach(
1233
- (t) => t.lineTransforms.posX.updateParams(this.posXSpringParams)
1234
- );
1235
- }
1236
- /**
1237
- * 设置所有歌词行在​纵坐标上的弹簧属性,包括重量、弹力和阻力。
1238
- *
1239
- * @param params 需要设置的弹簧属性,提供的属性将会覆盖原来的属性,未提供的属性将会保持原样
1240
- */
1241
- setLinePosYSpringParams(e) {
1242
- this.posYSpringParams = {
1243
- ...this.posYSpringParams,
1244
- ...e
1245
- }, this.bottomLine.lineTransforms.posY.updateParams(this.posYSpringParams), this.lyricLinesEl.forEach(
1246
- (t) => t.lineTransforms.posY.updateParams(this.posYSpringParams)
1247
- );
1248
- }
1249
- /**
1250
- * 设置所有歌词行在​缩放大小上的弹簧属性,包括重量、弹力和阻力。
1251
- *
1252
- * @param params 需要设置的弹簧属性,提供的属性将会覆盖原来的属性,未提供的属性将会保持原样
1253
- */
1254
- setLineScaleSpringParams(e) {
1255
- this.scaleSpringParams = {
1256
- ...this.scaleSpringParams,
1257
- ...e
1258
- }, this.lyricLinesEl.forEach(
1259
- (t) => t.lineTransforms.scale.updateParams(this.scaleSpringParams)
1260
- );
1261
- }
1262
- dispose() {
1263
- this.element.remove(), this.resizeObserver.disconnect(), this.style.detach(), this.lyricLinesEl.forEach((e) => e.dispose()), window.removeEventListener("pageshow", this.onPageShow), this.bottomLine.dispose(), this.interludeDots.dispose();
1264
- }
1265
- }
1266
- export {
1267
- se as BackgroundRender,
1268
- ne as LyricPlayer
1269
- };
1270
- //# sourceMappingURL=amll-core.mjs.map