@applemusic-like-lyrics/core 0.0.15 → 0.1.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.
@@ -1,1313 +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 D } 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 I } from "jss";
9
- import k from "jss-preset-default";
10
- class B extends C {
11
- time = 0;
12
- }
13
- class A {
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 = D.from(t, {
146
- resourceOptions: {
147
- autoLoad: !1
148
- }
149
- }), await s.baseTexture.resource.load();
150
- } catch (h) {
151
- console.warn(
152
- `failed on loading album image, retrying (${i})`,
153
- e,
154
- h
155
- ), s = void 0, i--;
156
- }
157
- if (!s)
158
- return;
159
- const r = new B(), n = new S(s), a = new S(s), o = new S(s), l = new S(s);
160
- n.anchor.set(0.5, 0.5), a.anchor.set(0.5, 0.5), o.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, o.rotation = Math.random() * Math.PI * 2, l.rotation = Math.random() * Math.PI * 2, r.addChild(n, a, o, 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 A {
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 = (c, e) => c.size === e.size && [...c].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(c, e, t, i = 0, s) {
236
- const r = s?.soft ?? !1, n = s?.stiffness ?? 100, a = s?.damping ?? 10, o = s?.mass ?? 1, l = t - c;
237
- if (r || 1 <= a / (2 * Math.sqrt(n * o))) {
238
- const h = -Math.sqrt(n / o), u = -h * l - e;
239
- return (p) => (p -= i, p < 0 ? c : t - (l + p * u) * Math.E ** (p * h));
240
- } else {
241
- const h = Math.sqrt(
242
- 4 * o * n - a ** 2
243
- ), u = (a * l - 2 * o * e) / h, p = 0.5 * h / o, f = -(0.5 * a) / o;
244
- return (d) => (d -= i, d < 0 ? c : t - (Math.cos(d * p) * l + Math.sin(d * p) * u) * Math.E ** (d * f));
245
- }
246
- }
247
- function $(c) {
248
- return (t) => (c(t + 1e-3) - c(t - 1e-3)) / (2 * 1e-3);
249
- }
250
- function x(c) {
251
- return $(c);
252
- }
253
- class Y {
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 _(c) {
311
- const t = 2.5949095;
312
- return c < 0.5 ? Math.pow(2 * c, 2) * ((t + 1) * 2 * c - t) / 2 : (Math.pow(2 * c - 2, 2) * ((t + 1) * (c * 2 - 2) + t) + 2) / 2;
313
- }
314
- const g = (c, e, t) => Math.max(c, 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 - _(
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 o = 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
- ), h = 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 * o),
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 * h),
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 W(c, e = "rgba(0,0,0,0.85)", t = "rgba(0,0,0,0.5)") {
394
- const i = 2 + c, s = c / 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(c, e) {
402
- let t = [], i = [];
403
- const s = [];
404
- for (const r of c) {
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(c) {
411
- return c.endTime - c.startTime >= 1e3 && c.word.length <= 7;
412
- }
413
- class H extends MouseEvent {
414
- constructor(e, t) {
415
- super(t.type, t), this.line = e;
416
- }
417
- }
418
- class G 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
- const t = new H(this, e);
453
- for (const i of this.listenersMap.get(e.type) ?? [])
454
- i.call(this, t);
455
- if (!this.dispatchEvent(t) || t.defaultPrevented)
456
- return e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation(), !1;
457
- };
458
- addMouseEventListener(e, t, i) {
459
- if (t) {
460
- const s = this.listenersMap.get(e) ?? /* @__PURE__ */ new Set();
461
- s.size === 0 && this.element.addEventListener(e, this.onMouseEvent), s.add(t), this.listenersMap.set(e, s);
462
- }
463
- }
464
- removeMouseEventListener(e, t, i) {
465
- if (t) {
466
- const s = this.listenersMap.get(e);
467
- s && (s.delete(t), s.size === 0 && this.element.removeEventListener(e, this.onMouseEvent));
468
- }
469
- }
470
- isEnabled = !1;
471
- enable() {
472
- this.isEnabled = !0, this.element.classList.add("active");
473
- const e = this.element.children[0];
474
- this.splittedWords.forEach((t) => {
475
- t.elementAnimations.forEach((i) => {
476
- i.currentTime = 0, i.playbackRate = 1, i.play();
477
- });
478
- }), e.classList.add("active");
479
- }
480
- measureSize() {
481
- this._hide && (this._prevParentEl && this._prevParentEl.appendChild(this.element), this.element.style.display = "", this.element.style.visibility = "hidden");
482
- const e = [
483
- this.element.clientWidth,
484
- this.element.clientHeight
485
- ];
486
- return this._hide && (this._prevParentEl && this.element.remove(), this.element.style.display = "none", this.element.style.visibility = ""), e;
487
- }
488
- disable() {
489
- this.isEnabled = !1, this.element.classList.remove("active");
490
- const e = this.element.children[0];
491
- this.splittedWords.forEach((t) => {
492
- t.elementAnimations.forEach((i) => {
493
- i.id === "float-word" && (i.playbackRate = -1, i.play());
494
- });
495
- }), e.classList.remove("active");
496
- }
497
- setLine(e) {
498
- 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(
499
- this.lyricPlayer.style.classes.lyricDuetLine
500
- ), this.rebuildElement(), this.rebuildStyle();
501
- }
502
- getLine() {
503
- return this.lyricLine;
504
- }
505
- _hide = !0;
506
- _prevParentEl = null;
507
- lastStyle = "";
508
- show() {
509
- this._hide = !1, this._prevParentEl && (this._prevParentEl.appendChild(this.element), this._prevParentEl = null), this.rebuildStyle();
510
- }
511
- hide() {
512
- this._hide = !0, this.element.parentElement && (this._prevParentEl = this.element.parentElement, this.element.remove()), this.rebuildStyle();
513
- }
514
- rebuildStyle() {
515
- if (this._hide) {
516
- this.lastStyle !== "display:none;transform:translate(0,-10000px);" && (this.lastStyle = "display:none;transform:translate(0,-10000px);", this.element.setAttribute(
517
- "style",
518
- "display:none;transform:translate(0,-10000px);"
519
- ));
520
- return;
521
- }
522
- 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)});`;
523
- !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));
524
- }
525
- rebuildElement() {
526
- const e = this.element.children[0], t = this.element.children[1], i = this.element.children[2];
527
- if (this.lyricPlayer._getIsNonDynamic()) {
528
- e.innerText = this.lyricLine.words.map((r) => r.word).join(""), t.innerText = this.lyricLine.translatedLyric, i.innerText = this.lyricLine.romanLyric;
529
- return;
530
- }
531
- const s = X(this.lyricLine.words, (r) => r.word);
532
- e.innerHTML = "", this.splittedWords = [];
533
- for (const r of s)
534
- if (r instanceof Array) {
535
- const n = r.map((l) => v(l)).reduce((l, h) => l || h, !1), a = r.reduce(
536
- (l, h) => (l.endTime = Math.max(l.endTime, h.endTime), l.startTime = Math.min(l.startTime, h.startTime), l.word += h.word, l),
537
- { word: "", startTime: 1 / 0, endTime: -1 / 0 }
538
- ), o = document.createElement("span");
539
- for (const l of r) {
540
- const h = document.createElement("span"), u = this.initFloatAnimation(
541
- a,
542
- h
543
- );
544
- if (n) {
545
- h.classList.add("emphasize");
546
- const p = [];
547
- for (const d of l.word.trim().split("")) {
548
- const m = document.createElement("span");
549
- m.innerText = d, p.push(m), h.appendChild(m);
550
- }
551
- const f = {
552
- ...l,
553
- mainElement: h,
554
- subElements: p,
555
- elementAnimations: [u],
556
- width: 0,
557
- height: 0,
558
- shouldEmphasize: n
559
- };
560
- f.elementAnimations.push(
561
- ...this.initEmphasizeAnimation(f)
562
- ), this.splittedWords.push(f);
563
- } else
564
- h.innerText = l.word, this.splittedWords.push({
565
- ...l,
566
- mainElement: h,
567
- subElements: [],
568
- elementAnimations: [u],
569
- width: 0,
570
- height: 0,
571
- shouldEmphasize: n
572
- });
573
- o.appendChild(h);
574
- }
575
- a.word.trimStart() !== a.word && e.appendChild(document.createTextNode(" ")), e.appendChild(o), a.word.trimEnd() !== a.word && e.appendChild(document.createTextNode(" "));
576
- } else if (r.word.trim().length === 0)
577
- e.appendChild(document.createTextNode(" "));
578
- else {
579
- const n = v(r), a = document.createElement("span"), o = {
580
- ...r,
581
- mainElement: a,
582
- subElements: [],
583
- elementAnimations: [this.initFloatAnimation(r, a)],
584
- width: 0,
585
- height: 0,
586
- shouldEmphasize: n
587
- };
588
- if (n) {
589
- a.classList.add("emphasize");
590
- const l = [];
591
- for (const h of r.word.trim().split("")) {
592
- const u = document.createElement("span");
593
- u.innerText = h, l.push(u), a.appendChild(u);
594
- }
595
- o.subElements = l, o.elementAnimations.push(
596
- ...this.initEmphasizeAnimation(o)
597
- );
598
- } else
599
- a.innerText = r.word.trim();
600
- r.word.trimStart() !== r.word && e.appendChild(document.createTextNode(" ")), e.appendChild(a), r.word.trimEnd() !== r.word && e.appendChild(document.createTextNode(" ")), this.splittedWords.push(o);
601
- }
602
- t.innerText = this.lyricLine.translatedLyric, i.innerText = this.lyricLine.romanLyric;
603
- }
604
- initFloatAnimation(e, t) {
605
- const i = e.startTime - this.lyricLine.startTime, s = Math.max(1e3, e.endTime - e.startTime), r = t.animate(
606
- [
607
- {
608
- transform: "translateY(0px)"
609
- },
610
- {
611
- transform: "translateY(-3%)"
612
- }
613
- ],
614
- {
615
- duration: isFinite(s) ? s : 0,
616
- delay: isFinite(i) ? i : 0,
617
- id: "float-word",
618
- composite: "add",
619
- fill: "both"
620
- }
621
- );
622
- return r.pause(), r;
623
- }
624
- initEmphasizeAnimation(e) {
625
- const t = e.startTime - this.lyricLine.startTime, i = e.endTime - e.startTime;
626
- return e.subElements.map((s, r, n) => {
627
- const a = Math.max(1e3, e.endTime - e.startTime), o = t + i / n.length * r, l = s.animate(
628
- [
629
- {
630
- offset: 0,
631
- transform: "translate3d(0, 0, 0px)",
632
- filter: "drop-shadow(0 0 0 var(--amll-lyric-view-color,white))"
633
- },
634
- {
635
- offset: 0.5,
636
- transform: "translate3d(0, -0.02em, 20px)",
637
- filter: "drop-shadow(0 0 0.05em var(--amll-lyric-view-color,white))"
638
- },
639
- {
640
- offset: 1,
641
- transform: "translate3d(0, 0, 0)",
642
- filter: "drop-shadow(0 0 0 var(--amll-lyric-view-color,white))"
643
- }
644
- ],
645
- {
646
- duration: isFinite(a) ? a : 0,
647
- delay: isFinite(o) ? o : 0,
648
- id: "glow-word",
649
- iterations: 1,
650
- composite: "replace",
651
- easing: "ease-in-out",
652
- fill: "both"
653
- }
654
- );
655
- return l.pause(), l;
656
- });
657
- }
658
- updateMaskImage() {
659
- this._hide && (this._prevParentEl && this._prevParentEl.appendChild(this.element), this.element.style.display = "", this.element.style.visibility = "hidden"), this.splittedWords.forEach((e) => {
660
- const t = e.mainElement;
661
- if (t) {
662
- e.width = t.clientWidth, e.height = t.clientHeight;
663
- const i = e.height / 2, [s, r, n] = W(
664
- i / e.width,
665
- "rgba(0,0,0,0.85)",
666
- "rgba(0,0,0,0.25)"
667
- ), a = `${n * 100}% 100%`;
668
- this.lyricPlayer.supportMaskImage ? (t.style.maskImage = s, t.style.maskRepeat = "no-repeat", t.style.maskOrigin = "left", t.style.maskSize = a) : (t.style.webkitMaskImage = s, t.style.webkitMaskRepeat = "no-repeat", t.style.webkitMaskOrigin = "left", t.style.webkitMaskSize = a);
669
- const o = e.width + i, l = `clamp(${-o}px,calc(${-o}px + (var(--amll-player-time) - ${e.startTime})*${o / Math.abs(e.endTime - e.startTime)}px),0px) 0px, left top`;
670
- t.style.maskPosition = l, t.style.webkitMaskPosition = l;
671
- }
672
- }), this._hide && (this._prevParentEl && this.element.remove(), this.element.style.display = "none", this.element.style.visibility = "");
673
- }
674
- getElement() {
675
- return this.element;
676
- }
677
- setTransform(e = this.left, t = this.top, i = this.scale, s = 1, r = 0, n = !1, a = 0) {
678
- const o = this.isInSight, l = this.lyricPlayer.getEnableSpring();
679
- this.left = e, this.top = t, this.scale = i, this.delay = a * 1e3 | 0;
680
- const h = this.element.children[0], u = this.element.children[1], p = this.element.children[2];
681
- if (h.style.opacity = `${s}`, u.style.opacity = `${s / 2}`, p.style.opacity = `${s / 2}`, n || !l) {
682
- if (this.blur = Math.min(32, r), n && this.element.classList.add(
683
- this.lyricPlayer.style.classes.tmpDisableTransition
684
- ), this.lineTransforms.posX.setPosition(e), this.lineTransforms.posY.setPosition(t), this.lineTransforms.scale.setPosition(i), l)
685
- this.rebuildStyle();
686
- else {
687
- const f = this.isInSight;
688
- o || f ? this.show() : this.hide();
689
- }
690
- n && requestAnimationFrame(() => {
691
- this.element.classList.remove(
692
- this.lyricPlayer.style.classes.tmpDisableTransition
693
- );
694
- });
695
- } else
696
- 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)`);
697
- }
698
- update(e = 0) {
699
- this.lyricPlayer.getEnableSpring() && (this.lineTransforms.posX.update(e), this.lineTransforms.posY.update(e), this.lineTransforms.scale.update(e), this.isInSight ? this.show() : this.hide());
700
- }
701
- _getDebugTargetPos() {
702
- return `[位移: ${this.left}, ${this.top}; 缩放: ${this.scale}; 延时: ${this.delay}]`;
703
- }
704
- get isInSight() {
705
- 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];
706
- return !(e > r || i < 0 || t > n || s < 0);
707
- }
708
- dispose() {
709
- this.element.remove();
710
- }
711
- }
712
- const V = I(k());
713
- class N extends MouseEvent {
714
- constructor(e, t, i) {
715
- super(`line-${i.type}`, i), this.lineIndex = e, this.line = t;
716
- }
717
- }
718
- class ne extends EventTarget {
719
- element = document.createElement("div");
720
- currentTime = 0;
721
- lyricLines = [];
722
- processedLines = [];
723
- lyricLinesEl = [];
724
- lyricLinesSize = /* @__PURE__ */ new WeakMap();
725
- lyricLinesIndexes = /* @__PURE__ */ new WeakMap();
726
- hotLines = /* @__PURE__ */ new Set();
727
- bufferedLines = /* @__PURE__ */ new Set();
728
- scrollToIndex = 0;
729
- allowScroll = !0;
730
- scrolledHandler = 0;
731
- isScrolled = !1;
732
- invokedByScrollEvent = !1;
733
- scrollOffset = 0;
734
- hidePassedLines = !1;
735
- resizeObserver = new ResizeObserver((e) => {
736
- const t = e[0].contentRect;
737
- this.size[0] = t.width, this.size[1] = t.height;
738
- 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);
739
- this.innerSize[0] = s, this.innerSize[1] = r, this.rebuildStyle(), this.calcLayout(!0, !0), this.lyricLinesEl.forEach((n) => n.updateMaskImage());
740
- });
741
- posXSpringParams = {
742
- mass: 1,
743
- damping: 10,
744
- stiffness: 100
745
- };
746
- posYSpringParams = {
747
- mass: 1,
748
- damping: 15,
749
- stiffness: 100
750
- };
751
- scaleSpringParams = {
752
- mass: 1,
753
- damping: 20,
754
- stiffness: 100
755
- };
756
- emUnit = Math.max(Math.min(innerHeight * 0.05, innerWidth * 0.1), 12);
757
- padding = this.emUnit;
758
- enableBlur = !0;
759
- enableScale = !0;
760
- interludeDots;
761
- interludeDotsSize = [0, 0];
762
- bottomLine;
763
- supportPlusLighter = CSS.supports("mix-blend-mode", "plus-lighter");
764
- supportMaskImage = CSS.supports("mask-image", "none");
765
- disableSpring = !1;
766
- alignAnchor = "center";
767
- alignPosition = 0.5;
768
- isNonDynamic = !1;
769
- scrollBoundary = [0, 0];
770
- size = [0, 0];
771
- innerSize = [0, 0];
772
- onLineClickedHandler = (e) => {
773
- const t = new N(
774
- this.lyricLinesIndexes.get(e.line) ?? -1,
775
- e.line,
776
- e
777
- );
778
- this.dispatchEvent(t) || (e.preventDefault(), e.stopPropagation(), e.stopImmediatePropagation());
779
- };
780
- _getIsNonDynamic() {
781
- return this.isNonDynamic;
782
- }
783
- /**
784
- * 设置是否使用物理弹簧算法实现歌词动画效果,默认启用
785
- *
786
- * 如果启用,则会通过弹簧算法实时处理歌词位置,但是需要性能足够强劲的电脑方可流畅运行
787
- *
788
- * 如果不启用,则会回退到基于 `transition` 的过渡效果,对低性能的机器比较友好,但是效果会比较单一
789
- */
790
- setEnableSpring(e = !0) {
791
- this.disableSpring = !e, e ? this.element.classList.remove(this.style.classes.disableSpring) : this.element.classList.add(this.style.classes.disableSpring), this.calcLayout(!0);
792
- }
793
- /**
794
- * 获取当前是否启用了物理弹簧
795
- * @returns 是否启用物理弹簧
796
- */
797
- getEnableSpring() {
798
- return !this.disableSpring;
799
- }
800
- /**
801
- * 是否启用歌词行缩放效果,默认启用
802
- *
803
- * 如果启用,非选中的歌词行会轻微缩小以凸显当前播放歌词行效果
804
- *
805
- * 此效果对性能影响微乎其微,推荐启用
806
- * @param enable 是否启用歌词行缩放效果
807
- */
808
- setEnableScale(e = !0) {
809
- this.enableScale = e, this.calcLayout();
810
- }
811
- /**
812
- * 获取当前是否启用了歌词行缩放效果
813
- * @returns 是否启用歌词行缩放效果
814
- */
815
- getEnableScale() {
816
- return this.enableScale;
817
- }
818
- style = V.createStyleSheet({
819
- lyricPlayer: {
820
- userSelect: "none",
821
- fontSize: "var(--amll-lyric-player-font-size,max(min(5vh, 10vw), 12px))",
822
- padding: "1em",
823
- margin: "-1em",
824
- width: "100%",
825
- height: "100%",
826
- overflow: "hidden",
827
- boxSizing: "content-box",
828
- maxWidth: "100%",
829
- maxHeight: "100%",
830
- zIndex: 1,
831
- color: "var(--amll-lyric-view-color,white)",
832
- mixBlendMode: "plus-lighter",
833
- contain: "strict",
834
- "&:hover": {
835
- "& $lyricLine": {
836
- filter: "unset !important"
837
- }
838
- }
839
- },
840
- lyricLine: {
841
- position: "absolute",
842
- transformOrigin: "left",
843
- width: "var(--amll-lyric-player-width,100%)",
844
- height: "fit-content",
845
- padding: "2vh 1.05em",
846
- margin: "0 -1em",
847
- contain: "content",
848
- willChange: "filter,transform,opacity",
849
- transition: "filter 0.25s, background-color 0.25s, box-shadow 0.25s",
850
- boxSizing: "content-box",
851
- borderRadius: "8px",
852
- "&:hover": {
853
- backgroundColor: "var(--amll-lyric-view-hover-bg-color,#fff1)",
854
- boxShadow: "0 0 0 8px var(--amll-lyric-view-hover-bg-color,#fff1)"
855
- },
856
- "&:active": {
857
- boxShadow: "0 0 0 4px var(--amll-lyric-view-hover-bg-color,#fff1)"
858
- }
859
- },
860
- "@media (max-width: 1024px)": {
861
- lyricLine: {
862
- padding: "1vh 1em"
863
- }
864
- },
865
- lyricDuetLine: {
866
- textAlign: "right",
867
- transformOrigin: "right"
868
- },
869
- lyricBgLine: {
870
- opacity: 0,
871
- fontSize: "max(50%, 10px)",
872
- transition: "opacity 0.25s",
873
- "&.active": {
874
- transition: "opacity 0.25s 0.25s",
875
- opacity: 0.75
876
- }
877
- },
878
- lyricMainLine: {
879
- transition: "opacity 0.3s 0.25s",
880
- willChange: "opacity",
881
- margin: "-1em",
882
- padding: "1em",
883
- "& span": {
884
- display: "inline-block"
885
- },
886
- "& > span": {
887
- whiteSpace: "pre-wrap",
888
- maxLines: "1",
889
- // willChange: "transform,display,mask-image",
890
- "&.emphasize": {
891
- transformStyle: "preserve-3d",
892
- perspective: "50vw",
893
- padding: "1em",
894
- margin: "-1em"
895
- }
896
- }
897
- },
898
- lyricSubLine: {
899
- fontSize: "max(0.5em, 10px)",
900
- transition: "opacity 0.3s 0.25s",
901
- opacity: 0.5
902
- },
903
- disableSpring: {
904
- "& > *": {
905
- transition: "filter 0.25s, transform 0.5s, background-color 0.25s, box-shadow 0.25s"
906
- }
907
- },
908
- interludeDots: {
909
- height: "clamp(0.5em,1vh,3em)",
910
- transformOrigin: "center",
911
- width: "fit-content",
912
- padding: "2.5% 0",
913
- position: "absolute",
914
- display: "flex",
915
- gap: "0.25em",
916
- left: "1em",
917
- "& > *": {
918
- height: "clamp(0.5em,1vh,3em)",
919
- display: "inline-block",
920
- borderRadius: "50%",
921
- aspectRatio: "1 / 1",
922
- backgroundColor: "var(--amll-lyric-view-color,white)",
923
- marginRight: "4px"
924
- },
925
- "&.duet": {
926
- right: "1em",
927
- transformOrigin: "center"
928
- }
929
- },
930
- "@supports (mix-blend-mode: plus-lighter)": {
931
- lyricSubLine: {
932
- opacity: 0.3
933
- }
934
- },
935
- tmpDisableTransition: {
936
- transition: "none !important"
937
- }
938
- });
939
- onPageShow = () => {
940
- this.calcLayout(!0, !0);
941
- };
942
- constructor() {
943
- super(), this.interludeDots = new q(this), this.bottomLine = new Y(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);
944
- let e = 0, t = "none", i = 0, s = 0, r = 0, n = Symbol("amll-scroll"), a = 0, o = 0;
945
- this.element.addEventListener("touchstart", (l) => {
946
- this.beginScrollHandler() && (l.preventDefault(), e = this.scrollOffset, i = l.touches[0].screenY, a = i, s = Date.now(), r = 0);
947
- }), this.element.addEventListener("touchmove", (l) => {
948
- if (this.beginScrollHandler()) {
949
- l.preventDefault();
950
- const h = l.touches[0].screenY, u = h - i, p = h - a, f = p > 0 ? "down" : p < 0 ? "up" : "none";
951
- t !== f ? (t = f, e = this.scrollOffset, i = h, s = Date.now()) : this.scrollOffset = e - u, a = h, o = Date.now(), this.limitScrollOffset(), this.calcLayout(!0);
952
- }
953
- }), this.element.addEventListener("touchend", (l) => {
954
- if (this.beginScrollHandler()) {
955
- l.preventDefault(), i = 0;
956
- const h = Date.now();
957
- if (h - o > 100)
958
- return this.endScrollHandler();
959
- const u = h - s;
960
- r = (this.scrollOffset - e) / u * 1e3;
961
- let p = 0;
962
- const f = Symbol("amll-scroll");
963
- n = f;
964
- const d = (m) => {
965
- p ||= m, n === f && this.beginScrollHandler() && (this.scrollOffset += r * (m - p) / 1e3, r *= 0.99, this.limitScrollOffset(), this.calcLayout(!0), Math.abs(r) > 1 && !this.scrollBoundary.includes(this.scrollOffset) && requestAnimationFrame(d), this.endScrollHandler(), p = m);
966
- };
967
- requestAnimationFrame(d), this.endScrollHandler();
968
- }
969
- }), this.element.addEventListener("wheel", (l) => {
970
- this.beginScrollHandler() && (l.deltaMode === l.DOM_DELTA_PIXEL ? (this.scrollOffset += l.deltaY, this.limitScrollOffset(), this.calcLayout(!0)) : (this.scrollOffset += l.deltaY * 50, this.limitScrollOffset(), this.calcLayout(!1)), this.endScrollHandler());
971
- });
972
- }
973
- beginScrollHandler() {
974
- const e = this.allowScroll;
975
- return e && (this.isScrolled = !0, this.invokedByScrollEvent = !0, clearTimeout(this.scrolledHandler), this.scrolledHandler = setTimeout(() => {
976
- this.isScrolled = !1, this.scrollOffset = 0;
977
- }, 5e3)), e;
978
- }
979
- endScrollHandler() {
980
- this.invokedByScrollEvent = !1;
981
- }
982
- limitScrollOffset() {
983
- this.scrollOffset = Math.max(
984
- Math.min(this.scrollBoundary[1], this.scrollOffset),
985
- this.scrollBoundary[0]
986
- );
987
- }
988
- /**
989
- * 获取当前播放时间里是否处于间奏区间
990
- * 如果是则会返回单位为毫秒的始末时间
991
- * 否则返回 undefined
992
- *
993
- * 这个只允许内部调用
994
- * @returns [开始时间,结束时间,大概处于的歌词行ID,下一句是否为对唱歌词] 或 undefined 如果不处于间奏区间
995
- */
996
- getCurrentInterlude() {
997
- if (this.bufferedLines.size > 0)
998
- return;
999
- const e = this.currentTime + 20, t = this.scrollToIndex;
1000
- if (t === 0) {
1001
- if (this.processedLines[0]?.startTime && this.processedLines[0].startTime > e)
1002
- return [
1003
- e,
1004
- this.processedLines[0].startTime,
1005
- -2,
1006
- this.processedLines[0].isDuet
1007
- ];
1008
- } else if (this.processedLines[t]?.endTime && this.processedLines[t + 1]?.startTime && this.processedLines[t + 1].startTime > e && this.processedLines[t].endTime < e)
1009
- return [
1010
- Math.max(this.processedLines[t].endTime, e),
1011
- this.processedLines[t + 1].startTime,
1012
- t,
1013
- this.processedLines[t + 1].isDuet
1014
- ];
1015
- }
1016
- /**
1017
- * 重建样式
1018
- *
1019
- * 这个只允许内部调用
1020
- */
1021
- rebuildStyle() {
1022
- let e = "";
1023
- 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);
1024
- }
1025
- /**
1026
- * 设置是否隐藏已经播放过的歌词行,默认不隐藏
1027
- * @param hide 是否隐藏已经播放过的歌词行,默认不隐藏
1028
- */
1029
- setHidePassedLines(e) {
1030
- this.hidePassedLines = e, this.calcLayout();
1031
- }
1032
- /**
1033
- * 设置是否启用歌词行的模糊效果
1034
- * @param enable 是否启用
1035
- */
1036
- setEnableBlur(e) {
1037
- this.enableBlur !== e && (this.enableBlur = e, this.calcLayout());
1038
- }
1039
- /**
1040
- * 设置当前播放歌词,要注意传入后这个数组内的信息不得修改,否则会发生错误
1041
- * @param lines 歌词数组
1042
- */
1043
- setLyricLines(e) {
1044
- this.lyricLines = e;
1045
- const t = 750;
1046
- this.processedLines = e.filter(
1047
- (i) => i.words.reduce((s, r) => s + r.word.trim().length, 0) > 0
1048
- ).map((i, s, r) => {
1049
- if (i.isBG)
1050
- return {
1051
- ...i
1052
- };
1053
- if (s === 0)
1054
- return {
1055
- ...i,
1056
- startTime: Math.max(i.startTime - t, 0)
1057
- };
1058
- {
1059
- const n = r[s - 1], a = r[s - 2];
1060
- if (n?.isBG && a) {
1061
- if (a.endTime < i.startTime)
1062
- return {
1063
- ...i,
1064
- startTime: Math.max(a.endTime, i.startTime - t) || i.startTime
1065
- };
1066
- } else if (n?.endTime && n.endTime < i.startTime)
1067
- return {
1068
- ...i,
1069
- startTime: Math.max(n?.endTime, i.startTime - t) || i.startTime
1070
- };
1071
- return {
1072
- ...i
1073
- };
1074
- }
1075
- }), this.isNonDynamic = !0;
1076
- for (const i of this.processedLines)
1077
- if (i.words.length > 1) {
1078
- this.isNonDynamic = !1;
1079
- break;
1080
- }
1081
- this.processedLines.forEach((i, s, r) => {
1082
- const n = r[s + 1], a = i.words[i.words.length - 1];
1083
- a && v(a) && (n ? n.startTime > i.endTime && (i.endTime = Math.min(i.endTime + 1500, n.startTime)) : i.endTime = i.endTime + 1500);
1084
- }), this.processedLines.forEach((i, s, r) => {
1085
- if (i.isBG)
1086
- return;
1087
- const n = r[s + 1];
1088
- n?.isBG && (n.startTime = Math.min(n.startTime, i.startTime));
1089
- }), this.lyricLinesEl.forEach((i) => {
1090
- i.removeMouseEventListener("click", this.onLineClickedHandler), i.removeMouseEventListener("contextmenu", this.onLineClickedHandler), i.dispose();
1091
- }), this.lyricLinesEl = this.processedLines.map((i) => {
1092
- const s = new G(this, i);
1093
- return s.addMouseEventListener("click", this.onLineClickedHandler), s.addMouseEventListener("contextmenu", this.onLineClickedHandler), s;
1094
- }), this.lyricLinesEl.forEach((i, s) => {
1095
- this.element.appendChild(i.getElement()), this.lyricLinesIndexes.set(i, s), i.updateMaskImage();
1096
- }), 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);
1097
- }
1098
- /**
1099
- * 重置用户滚动状态
1100
- *
1101
- * 请在用户完成滚动点击跳转歌词时调用本事件再调用 `calcLayout` 以正确滚动到目标位置
1102
- */
1103
- resetScroll() {
1104
- this.isScrolled = !1, this.scrollOffset = 0, this.invokedByScrollEvent = !1, clearTimeout(this.scrolledHandler), this.scrolledHandler = 0;
1105
- }
1106
- /**
1107
- * 重新布局定位歌词行的位置,调用完成后再逐帧调用 `update`
1108
- * 函数即可让歌词通过动画移动到目标位置。
1109
- *
1110
- * 函数有一个 `force` 参数,用于指定是否强制修改布局,也就是不经过动画直接调整元素位置和大小。
1111
- *
1112
- * 此函数还有一个 `reflow` 参数,用于指定是否需要重新计算布局
1113
- *
1114
- * 因为计算布局必定会导致浏览器重排布局,所以会大幅度影响流畅度和性能,故请只在以下情况下将其​设置为 true:
1115
- *
1116
- * 1. 歌词页面大小发生改变时(这个组件会自行处理)
1117
- * 2. 加载了新的歌词时(不论前后歌词是否完全一样)
1118
- * 3. 用户自行跳转了歌曲播放位置(不论距离远近)
1119
- *
1120
- * @param force 是否不经过动画直接修改布局定位
1121
- * @param reflow 是否进行重新布局(重新计算每行歌词大小)
1122
- */
1123
- calcLayout(e = !1, t = !1) {
1124
- t && (this.emUnit = parseFloat(getComputedStyle(this.element).fontSize), this.lyricLinesEl.forEach((d) => {
1125
- const m = d.measureSize();
1126
- this.lyricLinesSize.set(d, m), d.lineSize = m;
1127
- }), this.interludeDotsSize[0] = this.interludeDots.getElement().clientWidth, this.interludeDotsSize[1] = this.interludeDots.getElement().clientHeight, this.bottomLine.lineSize = this.bottomLine.measureSize());
1128
- const i = this.getCurrentInterlude();
1129
- let s = -this.scrollOffset, r = this.scrollToIndex, n = 0;
1130
- i ? (n = i[1] - i[0], n >= 5e3 && this.lyricLinesEl[i[2] + 1] && (r = i[2] + 1)) : this.interludeDots.setInterlude(void 0);
1131
- const a = this.enableScale ? 0.95 : 1, o = this.lyricLinesEl.slice(0, r).reduce(
1132
- (d, m) => d + (m.getLine().isBG ? 0 : this.lyricLinesSize.get(m)?.[1] ?? 0),
1133
- 0
1134
- );
1135
- this.scrollBoundary[0] = -o, s -= o, s += this.size[1] * this.alignPosition;
1136
- const l = this.lyricLinesEl[r];
1137
- if (l) {
1138
- const d = this.lyricLinesSize.get(l)?.[1] ?? 0;
1139
- switch (this.alignAnchor) {
1140
- case "bottom":
1141
- s -= d;
1142
- break;
1143
- case "center":
1144
- s -= d / 2;
1145
- break;
1146
- }
1147
- }
1148
- const h = Math.max(...this.bufferedLines);
1149
- let u = 0, p = 0.05, f = !1;
1150
- this.lyricLinesEl.forEach((d, m) => {
1151
- const T = this.bufferedLines.has(m), w = T || m >= this.scrollToIndex && m < h, E = d.getLine();
1152
- 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]);
1153
- const M = this.hidePassedLines && m < (i ? i[2] + 1 : this.scrollToIndex) ? 0 : T ? 1 : 1 / 3;
1154
- d.setTransform(
1155
- this.padding,
1156
- s,
1157
- w ? 1 : a,
1158
- M,
1159
- !this.invokedByScrollEvent && this.enableBlur ? w ? 0 : 1 + (m < this.scrollToIndex ? Math.abs(this.scrollToIndex - m) : Math.abs(m - Math.max(this.scrollToIndex, h))) : 0,
1160
- e,
1161
- u
1162
- ), E.isBG && w ? s += this.lyricLinesSize.get(d)?.[1] ?? 0 : E.isBG || (s += this.lyricLinesSize.get(d)?.[1] ?? 0), s >= 0 && (u += p, p /= 1.2);
1163
- }), this.scrollBoundary[1] = s + this.scrollOffset - this.size[1] / 2, this.bottomLine.setTransform(this.padding, s, e, u);
1164
- }
1165
- /**
1166
- * 获取当前歌词的播放位置
1167
- *
1168
- * 一般和最后调用 `setCurrentTime` 给予的参数一样
1169
- * @returns 当前播放位置
1170
- */
1171
- getCurrentTime() {
1172
- return this.currentTime;
1173
- }
1174
- /**
1175
- * 获取当前歌词数组
1176
- *
1177
- * 一般和最后调用 `setLyricLines` 给予的参数一样
1178
- * @returns 当前歌词数组
1179
- */
1180
- getLyricLines() {
1181
- return this.lyricLines;
1182
- }
1183
- getElement() {
1184
- return this.element;
1185
- }
1186
- /**
1187
- * 获取一个特殊的底栏元素,默认是空白的,可以往内部添加任意元素
1188
- *
1189
- * 这个元素始终在歌词的底部,可以用于显示歌曲创作者等信息
1190
- *
1191
- * 但是请勿删除该元素,只能在内部存放元素
1192
- *
1193
- * @returns 一个元素,可以往内部添加任意元素
1194
- */
1195
- getBottomLineElement() {
1196
- return this.bottomLine.getElement();
1197
- }
1198
- /**
1199
- * 设置目标歌词行的对齐方式,默认为 `center`
1200
- *
1201
- * - 设置成 `top` 的话将会向目标歌词行的顶部对齐
1202
- * - 设置成 `bottom` 的话将会向目标歌词行的底部对齐
1203
- * - 设置成 `center` 的话将会向目标歌词行的垂直中心对齐
1204
- * @param alignAnchor 歌词行对齐方式,详情见函数说明
1205
- */
1206
- setAlignAnchor(e) {
1207
- this.alignAnchor = e;
1208
- }
1209
- /**
1210
- * 设置默认的歌词行对齐位置,相对于整个歌词播放组件的大小位置,默认为 `0.5`
1211
- * @param alignPosition 一个 `[0.0-1.0]` 之间的任意数字,代表组件高度由上到下的比例位置
1212
- */
1213
- setAlignPosition(e) {
1214
- this.alignPosition = e;
1215
- }
1216
- /**
1217
- * 设置当前播放进度,单位为毫秒且**必须是整数**,此时将会更新内部的歌词进度信息
1218
- * 内部会根据调用间隔和播放进度自动决定如何滚动和显示歌词,所以这个的调用频率越快越准确越好
1219
- *
1220
- * 调用完成后,可以每帧调用 `update` 函数来执行歌词动画效果
1221
- * @param time 当前播放进度,单位为毫秒
1222
- */
1223
- setCurrentTime(e, t = !1) {
1224
- if (this.currentTime = e, this._getIsNonDynamic() || this.element.style.setProperty("--amll-player-time", `${e}`), this.isScrolled)
1225
- return;
1226
- const i = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), r = /* @__PURE__ */ new Set();
1227
- this.hotLines.forEach((n) => {
1228
- const a = this.processedLines[n];
1229
- if (a) {
1230
- if (a.isBG)
1231
- return;
1232
- const o = this.processedLines[n + 1];
1233
- if (o?.isBG) {
1234
- const l = Math.min(a.startTime, o?.startTime), h = Math.max(a.endTime, o?.endTime);
1235
- (l > e || h <= 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()));
1236
- } else
1237
- (a.startTime > e || a.endTime <= e) && (this.hotLines.delete(n), i.add(n), t && this.lyricLinesEl[n].disable());
1238
- } else
1239
- this.hotLines.delete(n), i.add(n), t && this.lyricLinesEl[n].disable();
1240
- }), this.processedLines.forEach((n, a, o) => {
1241
- !n.isBG && n.startTime <= e && n.endTime > e && (this.hotLines.has(a) || (this.hotLines.add(a), r.add(a), t && this.lyricLinesEl[a].enable(), o[a + 1]?.isBG && (this.hotLines.add(a + 1), r.add(a + 1), t && this.lyricLinesEl[a + 1].enable())));
1242
- }), this.bufferedLines.forEach((n) => {
1243
- this.hotLines.has(n) || (s.add(n), t && this.lyricLinesEl[n].disable());
1244
- }), t ? (this.bufferedLines.size > 0 ? this.scrollToIndex = Math.min(...this.bufferedLines) : this.scrollToIndex = this.processedLines.findIndex(
1245
- (n) => n.startTime >= e
1246
- ), 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) => {
1247
- this.bufferedLines.add(n), this.lyricLinesEl[n].enable();
1248
- }), this.scrollToIndex = Math.min(...this.bufferedLines), this.calcLayout()) : r.size === 0 && s.size > 0 ? O(s, this.bufferedLines) && (this.bufferedLines.forEach((n) => {
1249
- this.hotLines.has(n) || (this.bufferedLines.delete(n), this.lyricLinesEl[n].disable());
1250
- }), this.calcLayout()) : (r.forEach((n) => {
1251
- this.bufferedLines.add(n), this.lyricLinesEl[n].enable();
1252
- }), s.forEach((n) => {
1253
- this.bufferedLines.delete(n), this.lyricLinesEl[n].disable();
1254
- }), this.bufferedLines.size > 0 && (this.scrollToIndex = Math.min(...this.bufferedLines)), this.calcLayout()));
1255
- }
1256
- /**
1257
- * 更新动画,这个函数应该被逐帧调用或者在以下情况下调用一次:
1258
- *
1259
- * 1. 刚刚调用完设置歌词函数的时候
1260
- * @param delta 距离上一次被调用到现在的时长,单位为毫秒(可为浮点数)
1261
- */
1262
- update(e = 0) {
1263
- const t = e / 1e3;
1264
- this.interludeDots.update(e), this.bottomLine.update(t), this.lyricLinesEl.forEach((i) => i.update(t));
1265
- }
1266
- /**
1267
- * 设置所有歌词行在横坐标上的弹簧属性,包括重量、弹力和阻力。
1268
- *
1269
- * @param params 需要设置的弹簧属性,提供的属性将会覆盖原来的属性,未提供的属性将会保持原样
1270
- */
1271
- setLinePosXSpringParams(e) {
1272
- this.posXSpringParams = {
1273
- ...this.posXSpringParams,
1274
- ...e
1275
- }, this.bottomLine.lineTransforms.posX.updateParams(this.posXSpringParams), this.lyricLinesEl.forEach(
1276
- (t) => t.lineTransforms.posX.updateParams(this.posXSpringParams)
1277
- );
1278
- }
1279
- /**
1280
- * 设置所有歌词行在​纵坐标上的弹簧属性,包括重量、弹力和阻力。
1281
- *
1282
- * @param params 需要设置的弹簧属性,提供的属性将会覆盖原来的属性,未提供的属性将会保持原样
1283
- */
1284
- setLinePosYSpringParams(e) {
1285
- this.posYSpringParams = {
1286
- ...this.posYSpringParams,
1287
- ...e
1288
- }, this.bottomLine.lineTransforms.posY.updateParams(this.posYSpringParams), this.lyricLinesEl.forEach(
1289
- (t) => t.lineTransforms.posY.updateParams(this.posYSpringParams)
1290
- );
1291
- }
1292
- /**
1293
- * 设置所有歌词行在​缩放大小上的弹簧属性,包括重量、弹力和阻力。
1294
- *
1295
- * @param params 需要设置的弹簧属性,提供的属性将会覆盖原来的属性,未提供的属性将会保持原样
1296
- */
1297
- setLineScaleSpringParams(e) {
1298
- this.scaleSpringParams = {
1299
- ...this.scaleSpringParams,
1300
- ...e
1301
- }, this.lyricLinesEl.forEach(
1302
- (t) => t.lineTransforms.scale.updateParams(this.scaleSpringParams)
1303
- );
1304
- }
1305
- dispose() {
1306
- 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();
1307
- }
1308
- }
1309
- export {
1310
- se as BackgroundRender,
1311
- ne as LyricPlayer
1312
- };
1313
- //# sourceMappingURL=amll-core.mjs.map