@uimaxbai/am-lyrics 1.5.4 → 1.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/AmLyrics.d.ts +31 -1
- package/dist/src/AmLyrics.d.ts.map +1 -1
- package/dist/src/am-lyrics.js +876 -342
- package/dist/src/am-lyrics.js.map +1 -1
- package/dist/src/react.js +876 -342
- package/dist/src/react.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/AmLyrics.ts +1155 -384
package/dist/src/react.js
CHANGED
|
@@ -322,7 +322,7 @@ class GoogleService {
|
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
-
const VERSION = '1.5.
|
|
325
|
+
const VERSION = '1.5.5';
|
|
326
326
|
const INSTRUMENTAL_THRESHOLD_MS = 7000; // Show dots for gaps >= 7s
|
|
327
327
|
const FETCH_TIMEOUT_MS = 8000; // Timeout for all lyrics fetch requests
|
|
328
328
|
const SEEK_THRESHOLD_MS = 500;
|
|
@@ -331,6 +331,15 @@ const GAP_PULSE_DURATION_MS = 4000;
|
|
|
331
331
|
const GAP_PULSE_CYCLE_MS = GAP_PULSE_DURATION_MS * 2;
|
|
332
332
|
const GAP_EXIT_LEAD_MS = 600;
|
|
333
333
|
const GAP_MIN_SCALE = 0.85;
|
|
334
|
+
const NEXT_WORD_PRE_WIPE_MAX_GAP_MS = 180;
|
|
335
|
+
const NEXT_WORD_PRE_WIPE_MIN_DURATION_MS = 80;
|
|
336
|
+
const NEXT_WORD_PRE_WIPE_MAX_DURATION_MS = 240;
|
|
337
|
+
const BASE_WIPE_GRADIENT_EM = 0.75;
|
|
338
|
+
const LONG_WORD_WIPE_EXTRA_EM = 0.45;
|
|
339
|
+
const LONG_WORD_WIPE_EXTRA_RATIO = 0.35;
|
|
340
|
+
const SHORT_WORD_DRAG_MIN_DURATION_MS = 760;
|
|
341
|
+
const SHORT_WORD_GLOW_MIN_DURATION_MS = 1320;
|
|
342
|
+
const WORD_PRE_WIPE_HANDOFF_LEAD_MS = 120;
|
|
334
343
|
/**
|
|
335
344
|
* Fetch with an automatic timeout via AbortSignal.
|
|
336
345
|
* Rejects if the request takes longer than `timeoutMs`.
|
|
@@ -392,6 +401,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
392
401
|
this.activeLineIds = new Set();
|
|
393
402
|
this.currentPrimaryActiveLine = null;
|
|
394
403
|
this.lastPrimaryActiveLine = null;
|
|
404
|
+
this.backgroundExpandedLine = null;
|
|
395
405
|
// Scroll animation state
|
|
396
406
|
this.scrollAnimationState = null;
|
|
397
407
|
this.currentScrollOffset = 0;
|
|
@@ -489,6 +499,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
489
499
|
this.preActiveLineElements = [];
|
|
490
500
|
this.positionedLineElements = [];
|
|
491
501
|
this.activeGapLineElements = [];
|
|
502
|
+
this.clearBackgroundExpandedLine();
|
|
492
503
|
// Stop all running animations and clear highlights immediately
|
|
493
504
|
if (this.lyricsContainer) {
|
|
494
505
|
const activeLines = this.lyricsContainer.querySelectorAll('.lyrics-line.active, .lyrics-line.pre-active, .lyrics-line.bg-expanded');
|
|
@@ -521,7 +532,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
521
532
|
switchBtn.disabled = this.isFetchingAlternatives;
|
|
522
533
|
}
|
|
523
534
|
if (svgEl) {
|
|
524
|
-
svgEl.
|
|
535
|
+
svgEl.classList.toggle('is-loading', this.isFetchingAlternatives);
|
|
525
536
|
}
|
|
526
537
|
if (labelEl) {
|
|
527
538
|
labelEl.textContent = this.isFetchingAlternatives
|
|
@@ -694,6 +705,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
694
705
|
this.preActiveLineElements = [];
|
|
695
706
|
this.positionedLineElements = [];
|
|
696
707
|
this.activeGapLineElements = [];
|
|
708
|
+
this.clearBackgroundExpandedLine();
|
|
697
709
|
if (this.lyricsContainer) {
|
|
698
710
|
this.isProgrammaticScroll = true;
|
|
699
711
|
this.lyricsContainer.scrollTop = 0;
|
|
@@ -761,14 +773,23 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
761
773
|
return 21;
|
|
762
774
|
return 30;
|
|
763
775
|
}
|
|
776
|
+
static getDisplaySourceLabel(sourceLabel) {
|
|
777
|
+
return sourceLabel.toLowerCase().includes('lyricsplus')
|
|
778
|
+
? 'QQ'
|
|
779
|
+
: sourceLabel;
|
|
780
|
+
}
|
|
781
|
+
static getSourceKey(sourceLabel) {
|
|
782
|
+
const lower = (sourceLabel || '').trim().toLowerCase();
|
|
783
|
+
if (!lower)
|
|
784
|
+
return '';
|
|
785
|
+
if (lower.includes('lyricsplus') || lower === 'qq')
|
|
786
|
+
return 'qq';
|
|
787
|
+
return lower.replace(/\s+/g, ' ');
|
|
788
|
+
}
|
|
764
789
|
static mergeAndSortSources(collectedSources) {
|
|
765
790
|
const uniqueSourcesMap = new Map();
|
|
766
791
|
for (const source of collectedSources) {
|
|
767
|
-
const normalizedSource = source.source
|
|
768
|
-
.toLowerCase()
|
|
769
|
-
.includes('lyricsplus')
|
|
770
|
-
? 'QQ'
|
|
771
|
-
: source.source;
|
|
792
|
+
const normalizedSource = AmLyrics.getDisplaySourceLabel(source.source);
|
|
772
793
|
if (!uniqueSourcesMap.has(normalizedSource)) {
|
|
773
794
|
uniqueSourcesMap.set(normalizedSource, {
|
|
774
795
|
...source,
|
|
@@ -779,9 +800,43 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
779
800
|
return Array.from(uniqueSourcesMap.values()).sort((a, b) => AmLyrics.getRankForCollected(a.source, a.lines) -
|
|
780
801
|
AmLyrics.getRankForCollected(b.source, b.lines));
|
|
781
802
|
}
|
|
803
|
+
findCurrentSourceIndex(sources = this.availableSources, sourceLabel = this.lyricsSource, lines = this.lyrics) {
|
|
804
|
+
const identityIndex = sources.findIndex(source => source.lines === lines);
|
|
805
|
+
if (identityIndex !== -1)
|
|
806
|
+
return identityIndex;
|
|
807
|
+
const sourceKey = AmLyrics.getSourceKey(sourceLabel);
|
|
808
|
+
if (!sourceKey)
|
|
809
|
+
return -1;
|
|
810
|
+
return sources.findIndex(source => AmLyrics.getSourceKey(source.source) === sourceKey);
|
|
811
|
+
}
|
|
812
|
+
static getNextSourceIndex(sources, currentIndex, currentSourceLabel, currentLines) {
|
|
813
|
+
if (sources.length <= 1)
|
|
814
|
+
return -1;
|
|
815
|
+
if (currentIndex !== -1) {
|
|
816
|
+
return (currentIndex + 1) % sources.length;
|
|
817
|
+
}
|
|
818
|
+
const currentKey = AmLyrics.getSourceKey(currentSourceLabel);
|
|
819
|
+
const fallbackIndex = sources.findIndex(source => source.lines !== currentLines &&
|
|
820
|
+
AmLyrics.getSourceKey(source.source) !== currentKey);
|
|
821
|
+
return fallbackIndex === -1 ? 0 : fallbackIndex;
|
|
822
|
+
}
|
|
823
|
+
async applySourceAtIndex(index) {
|
|
824
|
+
const sourceResult = this.availableSources[index];
|
|
825
|
+
if (!sourceResult)
|
|
826
|
+
return;
|
|
827
|
+
this.currentSourceIndex = index;
|
|
828
|
+
this.lyrics = sourceResult.lines;
|
|
829
|
+
this.lyricsSource = sourceResult.source;
|
|
830
|
+
if (sourceResult.songwriters) {
|
|
831
|
+
this.songwriters = sourceResult.songwriters;
|
|
832
|
+
}
|
|
833
|
+
await this.onLyricsLoaded();
|
|
834
|
+
}
|
|
782
835
|
async switchSource() {
|
|
783
836
|
if (this.isFetchingAlternatives)
|
|
784
837
|
return;
|
|
838
|
+
const currentSourceLabel = this.lyricsSource;
|
|
839
|
+
const currentLines = this.lyrics;
|
|
785
840
|
if (!this.hasFetchedAllProviders) {
|
|
786
841
|
this.isFetchingAlternatives = true;
|
|
787
842
|
this._updateFooter();
|
|
@@ -824,10 +879,9 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
824
879
|
...this.availableSources,
|
|
825
880
|
...newSources,
|
|
826
881
|
]);
|
|
827
|
-
// Re-sync current index since sorting
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
this.currentSourceIndex = 0;
|
|
882
|
+
// Re-sync current index since sorting or label normalization can
|
|
883
|
+
// shift the currently displayed source underneath the old index.
|
|
884
|
+
this.currentSourceIndex = this.findCurrentSourceIndex(this.availableSources, currentSourceLabel, currentLines);
|
|
831
885
|
}
|
|
832
886
|
}
|
|
833
887
|
}
|
|
@@ -838,15 +892,11 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
838
892
|
}
|
|
839
893
|
}
|
|
840
894
|
if (this.availableSources.length > 1) {
|
|
841
|
-
this.
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
this.lyricsSource = sourceResult.source;
|
|
846
|
-
if (sourceResult.songwriters) {
|
|
847
|
-
this.songwriters = sourceResult.songwriters;
|
|
895
|
+
const currentIndex = this.findCurrentSourceIndex(this.availableSources, currentSourceLabel, currentLines);
|
|
896
|
+
const nextIndex = AmLyrics.getNextSourceIndex(this.availableSources, currentIndex, currentSourceLabel, currentLines);
|
|
897
|
+
if (nextIndex !== -1) {
|
|
898
|
+
await this.applySourceAtIndex(nextIndex);
|
|
848
899
|
}
|
|
849
|
-
await this.onLyricsLoaded();
|
|
850
900
|
}
|
|
851
901
|
}
|
|
852
902
|
async resolveSongMetadata() {
|
|
@@ -1898,7 +1948,9 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
1898
1948
|
if (!newActiveLines.includes(lineIndex)) {
|
|
1899
1949
|
const lineElement = this._getLineElement(lineIndex);
|
|
1900
1950
|
if (lineElement) {
|
|
1901
|
-
if (isSeek ||
|
|
1951
|
+
if (isSeek ||
|
|
1952
|
+
this.isUserScrolling ||
|
|
1953
|
+
AmLyrics.isLineSyncedLine(this.lyrics?.[lineIndex])) {
|
|
1902
1954
|
AmLyrics.unfinishSyllables(lineElement);
|
|
1903
1955
|
}
|
|
1904
1956
|
else {
|
|
@@ -1914,12 +1966,13 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
1914
1966
|
}
|
|
1915
1967
|
}
|
|
1916
1968
|
}
|
|
1917
|
-
// Add 'active'
|
|
1969
|
+
// Add 'active' to newly active lines. Background expansion is driven
|
|
1970
|
+
// separately by the current scroll target.
|
|
1918
1971
|
for (const lineIndex of newActiveLines) {
|
|
1919
1972
|
if (!oldActiveLines.includes(lineIndex)) {
|
|
1920
1973
|
const lineElement = this._getLineElement(lineIndex);
|
|
1921
1974
|
if (lineElement) {
|
|
1922
|
-
lineElement.classList.add('active'
|
|
1975
|
+
lineElement.classList.add('active');
|
|
1923
1976
|
lineElement.classList.remove('pre-active');
|
|
1924
1977
|
const preIdx = this.preActiveLineElements.indexOf(lineElement);
|
|
1925
1978
|
if (preIdx !== -1)
|
|
@@ -2139,8 +2192,12 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2139
2192
|
for (const lineIndex of activeLines) {
|
|
2140
2193
|
const lineEl = this._getLineElement(lineIndex);
|
|
2141
2194
|
if (lineEl)
|
|
2142
|
-
lineEl.classList.add('active'
|
|
2195
|
+
lineEl.classList.add('active');
|
|
2143
2196
|
}
|
|
2197
|
+
const primaryActiveLine = this.getPrimaryActiveLineIndex(activeLines);
|
|
2198
|
+
this.setBackgroundExpandedLine(primaryActiveLine !== null
|
|
2199
|
+
? this._getLineElement(primaryActiveLine)
|
|
2200
|
+
: null);
|
|
2144
2201
|
// Trigger a faux time-change so that updateSyllablesForLine fires
|
|
2145
2202
|
// to setup inline syllable CSS wipe animations for whatever the current time is
|
|
2146
2203
|
this._onTimeChanged(0, this.currentTime);
|
|
@@ -2179,6 +2236,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2179
2236
|
this.preActiveLineElements = [];
|
|
2180
2237
|
this.positionedLineElements = [];
|
|
2181
2238
|
this.activeGapLineElements = [];
|
|
2239
|
+
this.clearBackgroundExpandedLine();
|
|
2182
2240
|
this.setUserScrolling(false);
|
|
2183
2241
|
// Cancel any running animations
|
|
2184
2242
|
if (this.animationFrameId) {
|
|
@@ -2233,6 +2291,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2233
2291
|
// Don't override it with a scroll back to the last lyric.
|
|
2234
2292
|
const footer = this.lyricsContainer.querySelector('.lyrics-footer');
|
|
2235
2293
|
if (footer?.classList.contains('active')) {
|
|
2294
|
+
this.setBackgroundExpandedLine(null);
|
|
2236
2295
|
return;
|
|
2237
2296
|
}
|
|
2238
2297
|
// 1. Compute scroll lookahead based on gap to next line (YouLyPlus style)
|
|
@@ -2273,17 +2332,21 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2273
2332
|
targetElement = this._getLineElement(targetLineIdx);
|
|
2274
2333
|
}
|
|
2275
2334
|
}
|
|
2276
|
-
if (!targetElement)
|
|
2335
|
+
if (!targetElement) {
|
|
2336
|
+
this.setBackgroundExpandedLine(null);
|
|
2277
2337
|
return;
|
|
2278
|
-
|
|
2279
|
-
|
|
2338
|
+
}
|
|
2339
|
+
const scrollDuration = scrollLookAheadMs;
|
|
2340
|
+
targetElement.style.setProperty('--scroll-duration', `${scrollDuration}ms`);
|
|
2341
|
+
this.setBackgroundExpandedLine(targetElement);
|
|
2342
|
+
// Unblur the upcoming target line early while the separate bg-expanded
|
|
2343
|
+
// class starts background vocal height/opacity in sync with scroll.
|
|
2280
2344
|
if (!targetElement.classList.contains('active')) {
|
|
2281
2345
|
targetElement.classList.add('pre-active');
|
|
2282
2346
|
if (!this.preActiveLineElements.includes(targetElement)) {
|
|
2283
2347
|
this.preActiveLineElements.push(targetElement);
|
|
2284
2348
|
}
|
|
2285
2349
|
}
|
|
2286
|
-
const scrollDuration = scrollLookAheadMs;
|
|
2287
2350
|
this.focusLine(targetElement, forceScroll, scrollDuration);
|
|
2288
2351
|
}
|
|
2289
2352
|
_getTextWidth(text, font) {
|
|
@@ -2356,6 +2419,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2356
2419
|
this.preActiveLineElements = [];
|
|
2357
2420
|
this.positionedLineElements = [];
|
|
2358
2421
|
this.activeGapLineElements = [];
|
|
2422
|
+
this.clearBackgroundExpandedLine();
|
|
2359
2423
|
this.visibilityObserver?.disconnect();
|
|
2360
2424
|
this.visibilityObserver = undefined;
|
|
2361
2425
|
}
|
|
@@ -2391,6 +2455,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2391
2455
|
const groupGrowable = new Array(wordGroups.length).fill(false);
|
|
2392
2456
|
const groupGlowing = new Array(wordGroups.length).fill(false);
|
|
2393
2457
|
const groupCharRise = new Array(wordGroups.length).fill(false);
|
|
2458
|
+
const groupCharDrag = new Array(wordGroups.length).fill(false);
|
|
2394
2459
|
const vwFullText = new Array(wordGroups.length).fill('');
|
|
2395
2460
|
const vwFullDuration = new Array(wordGroups.length).fill(0);
|
|
2396
2461
|
const vwCharOffset = new Array(wordGroups.length).fill(0);
|
|
@@ -2426,28 +2491,42 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2426
2491
|
const isLineSynced = line.isWordSynced === false || line.text.some(s => s.lineSynced);
|
|
2427
2492
|
let isGrowableVW = canAnimateByChar && wordLen > 0 && wordLen <= 7;
|
|
2428
2493
|
if (isGrowableVW) {
|
|
2429
|
-
if (wordLen
|
|
2494
|
+
if (wordLen <= 1) {
|
|
2430
2495
|
isGrowableVW =
|
|
2431
2496
|
combinedDuration >= 1050 && combinedDuration >= wordLen * 525;
|
|
2432
2497
|
}
|
|
2498
|
+
else if (wordLen <= 3) {
|
|
2499
|
+
isGrowableVW =
|
|
2500
|
+
combinedDuration >=
|
|
2501
|
+
SHORT_WORD_GLOW_MIN_DURATION_MS + (wordLen - 2) * 140;
|
|
2502
|
+
}
|
|
2433
2503
|
else {
|
|
2434
2504
|
isGrowableVW =
|
|
2435
2505
|
combinedDuration >= 850 && combinedDuration >= wordLen * 190;
|
|
2436
2506
|
}
|
|
2437
2507
|
}
|
|
2438
2508
|
const hasCharRiseDuration = combinedDuration >= Math.max(700, wordLen * 85);
|
|
2509
|
+
const hasTinyWordDragDuration = wordLen >= 2 &&
|
|
2510
|
+
wordLen <= 3 &&
|
|
2511
|
+
combinedDuration >=
|
|
2512
|
+
Math.max(SHORT_WORD_DRAG_MIN_DURATION_MS, wordLen * 150);
|
|
2439
2513
|
const hasLongShortWordDuration = wordLen >= 4 && combinedDuration >= Math.max(1300, wordLen * 260);
|
|
2440
2514
|
const isCharRiseVW = canAnimateByChar &&
|
|
2441
2515
|
!isLineSynced &&
|
|
2442
2516
|
!isGrowableVW &&
|
|
2443
2517
|
((wordLen >= 8 && hasCharRiseDuration) ||
|
|
2444
2518
|
(wordLen < 8 && hasLongShortWordDuration));
|
|
2519
|
+
const isCharDragVW = canAnimateByChar &&
|
|
2520
|
+
!isLineSynced &&
|
|
2521
|
+
!isGrowableVW &&
|
|
2522
|
+
hasTinyWordDragDuration;
|
|
2445
2523
|
const isGlowingVW = isGrowableVW && !isLineSynced;
|
|
2446
2524
|
let charOff = 0;
|
|
2447
2525
|
for (let gi = vwStart; gi <= vwEnd; gi += 1) {
|
|
2448
2526
|
groupGrowable[gi] = isGrowableVW;
|
|
2449
2527
|
groupGlowing[gi] = isGlowingVW;
|
|
2450
2528
|
groupCharRise[gi] = isCharRiseVW;
|
|
2529
|
+
groupCharDrag[gi] = isCharDragVW;
|
|
2451
2530
|
vwFullText[gi] = combinedText;
|
|
2452
2531
|
vwFullDuration[gi] = combinedDuration;
|
|
2453
2532
|
vwCharOffset[gi] = charOff;
|
|
@@ -2463,6 +2542,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2463
2542
|
groupGrowable,
|
|
2464
2543
|
groupGlowing,
|
|
2465
2544
|
groupCharRise,
|
|
2545
|
+
groupCharDrag,
|
|
2466
2546
|
vwFullText,
|
|
2467
2547
|
vwFullDuration,
|
|
2468
2548
|
vwCharOffset,
|
|
@@ -2482,48 +2562,97 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2482
2562
|
return;
|
|
2483
2563
|
const computedStyle = getComputedStyle(referenceSyllable);
|
|
2484
2564
|
const { font } = computedStyle; // Full font string
|
|
2485
|
-
const
|
|
2486
|
-
|
|
2487
|
-
if (!charTimedWords)
|
|
2565
|
+
const charTimedWords = Array.from(this.shadowRoot.querySelectorAll('.lyrics-word.growable, .lyrics-word.char-rise, .lyrics-word.char-drag'));
|
|
2566
|
+
if (charTimedWords.length === 0)
|
|
2488
2567
|
return;
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2568
|
+
const wordsByVirtualId = new Map();
|
|
2569
|
+
charTimedWords.forEach((wordSpan, index) => {
|
|
2570
|
+
const virtualWordId = wordSpan.dataset.virtualWordId || `word-${index}`;
|
|
2571
|
+
const words = wordsByVirtualId.get(virtualWordId);
|
|
2572
|
+
if (words) {
|
|
2573
|
+
words.push(wordSpan);
|
|
2574
|
+
}
|
|
2575
|
+
else {
|
|
2576
|
+
wordsByVirtualId.set(virtualWordId, [wordSpan]);
|
|
2577
|
+
}
|
|
2578
|
+
});
|
|
2579
|
+
wordsByVirtualId.forEach(wordSpans => {
|
|
2492
2580
|
const syllables = [];
|
|
2493
|
-
|
|
2494
|
-
const
|
|
2495
|
-
|
|
2496
|
-
|
|
2581
|
+
wordSpans.forEach(wordSpan => {
|
|
2582
|
+
const syllableWraps = wordSpan.querySelectorAll('.lyrics-syllable-wrap');
|
|
2583
|
+
syllableWraps.forEach(wrap => {
|
|
2584
|
+
const syl = wrap.querySelector('.lyrics-syllable');
|
|
2585
|
+
if (syl)
|
|
2586
|
+
syllables.push(syl);
|
|
2587
|
+
});
|
|
2497
2588
|
});
|
|
2498
|
-
syllables.
|
|
2499
|
-
const
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
const
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
const
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2589
|
+
const charSpans = syllables.flatMap(syl => {
|
|
2590
|
+
const spans = Array.from(syl.querySelectorAll('.char'));
|
|
2591
|
+
const target = syl;
|
|
2592
|
+
target._cachedCharSpans = spans;
|
|
2593
|
+
return spans;
|
|
2594
|
+
});
|
|
2595
|
+
if (charSpans.length === 0)
|
|
2596
|
+
return;
|
|
2597
|
+
wordSpans.forEach(wordSpan => {
|
|
2598
|
+
const target = wordSpan;
|
|
2599
|
+
target._cachedVirtualWordElements = wordSpans;
|
|
2600
|
+
target._cachedVirtualWordCharSpans = charSpans;
|
|
2601
|
+
});
|
|
2602
|
+
const syllableEntries = syllables.map(syl => {
|
|
2603
|
+
const spans = syl._cachedCharSpans;
|
|
2604
|
+
const charWidths = spans.map(span => this._getTextWidth(span.textContent || '', font));
|
|
2605
|
+
const totalWidth = charWidths.reduce((a, b) => a + b, 0);
|
|
2606
|
+
return {
|
|
2607
|
+
syl,
|
|
2608
|
+
spans,
|
|
2609
|
+
charWidths,
|
|
2610
|
+
totalWidth,
|
|
2611
|
+
start: parseFloat(syl.getAttribute('data-start-time') || ''),
|
|
2612
|
+
end: parseFloat(syl.getAttribute('data-end-time') || ''),
|
|
2613
|
+
};
|
|
2614
|
+
});
|
|
2615
|
+
const totalWordWidth = syllableEntries.reduce((total, entry) => total + entry.totalWidth, 0);
|
|
2616
|
+
if (totalWordWidth <= 0)
|
|
2617
|
+
return;
|
|
2618
|
+
const virtualWordStart = Math.min(...syllableEntries
|
|
2619
|
+
.map(entry => entry.start)
|
|
2620
|
+
.filter(start => Number.isFinite(start)));
|
|
2621
|
+
const virtualWordEnd = Math.max(...syllableEntries
|
|
2622
|
+
.map(entry => entry.end)
|
|
2623
|
+
.filter(end => Number.isFinite(end)));
|
|
2624
|
+
const virtualWordDuration = virtualWordEnd - virtualWordStart;
|
|
2625
|
+
const hasTimedSyllables = Number.isFinite(virtualWordStart) &&
|
|
2626
|
+
Number.isFinite(virtualWordEnd) &&
|
|
2627
|
+
virtualWordDuration > 0;
|
|
2628
|
+
let cumulativeCharWidth = 0;
|
|
2629
|
+
syllableEntries.forEach(entry => {
|
|
2630
|
+
let cumulativeSyllableWidth = 0;
|
|
2631
|
+
const syllableDuration = entry.end - entry.start;
|
|
2632
|
+
const useSyllableTiming = hasTimedSyllables &&
|
|
2633
|
+
Number.isFinite(entry.start) &&
|
|
2634
|
+
Number.isFinite(entry.end) &&
|
|
2635
|
+
syllableDuration > 0 &&
|
|
2636
|
+
entry.totalWidth > 0;
|
|
2637
|
+
entry.spans.forEach((span, index) => {
|
|
2638
|
+
const charWidth = entry.charWidths[index];
|
|
2639
|
+
let startPercent = cumulativeCharWidth / totalWordWidth;
|
|
2640
|
+
let durationPercent = charWidth / totalWordWidth;
|
|
2641
|
+
if (useSyllableTiming) {
|
|
2642
|
+
const charStartMs = entry.start -
|
|
2643
|
+
virtualWordStart +
|
|
2644
|
+
(cumulativeSyllableWidth / entry.totalWidth) * syllableDuration;
|
|
2645
|
+
const charDurationMs = (charWidth / entry.totalWidth) * syllableDuration;
|
|
2646
|
+
startPercent = AmLyrics.clamp(charStartMs / virtualWordDuration, 0, 1);
|
|
2647
|
+
durationPercent = AmLyrics.clamp(charDurationMs / virtualWordDuration, 0, 1);
|
|
2525
2648
|
}
|
|
2649
|
+
const target = span;
|
|
2650
|
+
target.dataset.wipeStart = startPercent.toFixed(4);
|
|
2651
|
+
target.dataset.wipeDuration = durationPercent.toFixed(4);
|
|
2652
|
+
target.style.setProperty('--word-wipe-width', `${totalWordWidth}px`);
|
|
2653
|
+
target.style.setProperty('--char-wipe-position', `${-cumulativeCharWidth}px`);
|
|
2526
2654
|
cumulativeCharWidth += charWidth;
|
|
2655
|
+
cumulativeSyllableWidth += charWidth;
|
|
2527
2656
|
});
|
|
2528
2657
|
});
|
|
2529
2658
|
});
|
|
@@ -2531,6 +2660,33 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2531
2660
|
static arraysEqual(a, b) {
|
|
2532
2661
|
return a.length === b.length && a.every((val, i) => val === b[i]);
|
|
2533
2662
|
}
|
|
2663
|
+
static isLineSyncedLine(line) {
|
|
2664
|
+
if (!line)
|
|
2665
|
+
return false;
|
|
2666
|
+
return line.isWordSynced === false || line.text.some(s => s.lineSynced);
|
|
2667
|
+
}
|
|
2668
|
+
getLineHighlightEndTime(index) {
|
|
2669
|
+
if (!this.lyrics)
|
|
2670
|
+
return 0;
|
|
2671
|
+
const line = this.lyrics[index];
|
|
2672
|
+
if (!line)
|
|
2673
|
+
return 0;
|
|
2674
|
+
const rawEnd = Math.max(line.endtime, line.timestamp);
|
|
2675
|
+
const nextLine = this.lyrics[index + 1];
|
|
2676
|
+
if (!nextLine || nextLine.timestamp <= line.timestamp) {
|
|
2677
|
+
return rawEnd > line.timestamp ? rawEnd + 200 : rawEnd;
|
|
2678
|
+
}
|
|
2679
|
+
if (rawEnd > line.timestamp) {
|
|
2680
|
+
if (nextLine.timestamp < rawEnd) {
|
|
2681
|
+
return rawEnd;
|
|
2682
|
+
}
|
|
2683
|
+
const gapToNext = nextLine.timestamp - rawEnd;
|
|
2684
|
+
if (gapToNext >= INSTRUMENTAL_THRESHOLD_MS) {
|
|
2685
|
+
return rawEnd;
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
return nextLine.timestamp;
|
|
2689
|
+
}
|
|
2534
2690
|
static getLineIndexFromElement(lineElement) {
|
|
2535
2691
|
if (!lineElement)
|
|
2536
2692
|
return null;
|
|
@@ -2561,6 +2717,26 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2561
2717
|
}
|
|
2562
2718
|
this.preActiveLineElements = keptLines;
|
|
2563
2719
|
}
|
|
2720
|
+
setBackgroundExpandedLine(lineElement) {
|
|
2721
|
+
const target = lineElement &&
|
|
2722
|
+
!lineElement.classList.contains('lyrics-gap') &&
|
|
2723
|
+
lineElement.querySelector('.background-vocal-container')
|
|
2724
|
+
? lineElement
|
|
2725
|
+
: null;
|
|
2726
|
+
if (this.backgroundExpandedLine === target) {
|
|
2727
|
+
if (target && !target.classList.contains('bg-expanded')) {
|
|
2728
|
+
target.classList.add('bg-expanded');
|
|
2729
|
+
}
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
this.backgroundExpandedLine?.classList.remove('bg-expanded');
|
|
2733
|
+
this.backgroundExpandedLine = target;
|
|
2734
|
+
target?.classList.add('bg-expanded');
|
|
2735
|
+
}
|
|
2736
|
+
clearBackgroundExpandedLine() {
|
|
2737
|
+
this.backgroundExpandedLine?.classList.remove('bg-expanded');
|
|
2738
|
+
this.backgroundExpandedLine = null;
|
|
2739
|
+
}
|
|
2564
2740
|
getPrimaryActiveLineIndex(activeIndices) {
|
|
2565
2741
|
if (activeIndices.length === 0)
|
|
2566
2742
|
return null;
|
|
@@ -2750,9 +2926,10 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2750
2926
|
const activeLines = [];
|
|
2751
2927
|
for (let i = 0; i < this.lyrics.length; i += 1) {
|
|
2752
2928
|
const line = this.lyrics[i];
|
|
2929
|
+
const highlightEndTime = this.getLineHighlightEndTime(i);
|
|
2753
2930
|
if (line.timestamp > time)
|
|
2754
2931
|
break;
|
|
2755
|
-
if (time >= line.timestamp && time <
|
|
2932
|
+
if (time >= line.timestamp && time < highlightEndTime) {
|
|
2756
2933
|
activeLines.push(i);
|
|
2757
2934
|
}
|
|
2758
2935
|
}
|
|
@@ -2977,6 +3154,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2977
3154
|
this.lastPrimaryActiveLine = null;
|
|
2978
3155
|
this.activeLineIds.clear();
|
|
2979
3156
|
this.animatingLines = [];
|
|
3157
|
+
this.setBackgroundExpandedLine(null);
|
|
2980
3158
|
// Find the clicked line element and scroll to it with forceScroll (like YouLyPlus)
|
|
2981
3159
|
// Timestamps are already in milliseconds — match the data-start-time attribute directly
|
|
2982
3160
|
const clickedLineElement = this.lyricsContainer?.querySelector(`.lyrics-line[data-start-time="${line.text[0]?.timestamp || 0}"]`);
|
|
@@ -2993,6 +3171,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2993
3171
|
this.isClickSeeking = false;
|
|
2994
3172
|
}, 800);
|
|
2995
3173
|
this.scrollToActiveLineYouLy(clickedLineElement, true);
|
|
3174
|
+
this.setBackgroundExpandedLine(clickedLineElement);
|
|
2996
3175
|
}
|
|
2997
3176
|
const event = new CustomEvent('line-click', {
|
|
2998
3177
|
detail: {
|
|
@@ -3338,30 +3517,256 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3338
3517
|
}
|
|
3339
3518
|
/**
|
|
3340
3519
|
* Update syllable highlight animation - apply CSS wipe animation
|
|
3341
|
-
* (Exact copy from YouLyPlus _updateSyllableAnimation)
|
|
3342
3520
|
*/
|
|
3521
|
+
static clamp(value, min, max) {
|
|
3522
|
+
return Math.min(max, Math.max(min, value));
|
|
3523
|
+
}
|
|
3524
|
+
static getVisibleCharacterCount(element) {
|
|
3525
|
+
const attrLength = parseFloat(element.getAttribute('data-word-length') || '');
|
|
3526
|
+
if (Number.isFinite(attrLength) && attrLength > 0)
|
|
3527
|
+
return attrLength;
|
|
3528
|
+
return (element.textContent || '').replace(/\s/g, '').length;
|
|
3529
|
+
}
|
|
3530
|
+
static getLongWordWipeScale(charCount) {
|
|
3531
|
+
if (charCount <= 6)
|
|
3532
|
+
return 1;
|
|
3533
|
+
return (1 +
|
|
3534
|
+
AmLyrics.clamp((charCount - 6) / 10, 0, 1) * LONG_WORD_WIPE_EXTRA_RATIO);
|
|
3535
|
+
}
|
|
3536
|
+
static applyWipeShape(element, charCount) {
|
|
3537
|
+
const extra = AmLyrics.clamp((charCount - 6) / 10, 0, 1) * LONG_WORD_WIPE_EXTRA_EM;
|
|
3538
|
+
const width = BASE_WIPE_GRADIENT_EM + extra;
|
|
3539
|
+
element.style.setProperty('--wipe-gradient-width', `${width.toFixed(3)}em`);
|
|
3540
|
+
element.style.setProperty('--wipe-gradient-half', `${(width / 2).toFixed(3)}em`);
|
|
3541
|
+
}
|
|
3542
|
+
static ensureWordWipeGeometry(charSpans, charCount) {
|
|
3543
|
+
if (charSpans.length === 0)
|
|
3544
|
+
return;
|
|
3545
|
+
const approxWidthCh = Math.max(1, charCount || charSpans.length);
|
|
3546
|
+
charSpans.forEach((span, index) => {
|
|
3547
|
+
if (!span.style.getPropertyValue('--word-wipe-width')) {
|
|
3548
|
+
span.style.setProperty('--word-wipe-width', `${approxWidthCh}ch`);
|
|
3549
|
+
}
|
|
3550
|
+
if (!span.style.getPropertyValue('--char-wipe-position')) {
|
|
3551
|
+
const startPct = Number.parseFloat(span.dataset.wipeStart || `${index / Math.max(1, charSpans.length)}`);
|
|
3552
|
+
span.style.setProperty('--char-wipe-position', `${-(AmLyrics.clamp(startPct, 0, 1) * approxWidthCh)}ch`);
|
|
3553
|
+
}
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
static clearPreHighlight(syllable) {
|
|
3557
|
+
const target = syllable;
|
|
3558
|
+
target.classList.remove('pre-highlight');
|
|
3559
|
+
target.style.removeProperty('--pre-wipe-duration');
|
|
3560
|
+
target.style.removeProperty('--pre-wipe-delay');
|
|
3561
|
+
target.style.animation = '';
|
|
3562
|
+
target
|
|
3563
|
+
.querySelectorAll('.pre-wipe-lead')
|
|
3564
|
+
.forEach(element => AmLyrics.clearPreWipeLead(element));
|
|
3565
|
+
}
|
|
3566
|
+
static clearPreWipeLead(element) {
|
|
3567
|
+
element.classList.remove('pre-wipe-lead');
|
|
3568
|
+
element.style.removeProperty('--pre-wipe-duration');
|
|
3569
|
+
element.style.removeProperty('--pre-wipe-delay');
|
|
3570
|
+
}
|
|
3571
|
+
static hasTextBoundaryAfter(syllable) {
|
|
3572
|
+
return /\s$/.test(syllable.textContent || '');
|
|
3573
|
+
}
|
|
3574
|
+
static getSyllableWordIndex(syllable) {
|
|
3575
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3576
|
+
const virtualWordId = wordElement?.dataset.virtualWordId;
|
|
3577
|
+
if (virtualWordId) {
|
|
3578
|
+
return `virtual:${virtualWordId}`;
|
|
3579
|
+
}
|
|
3580
|
+
const virtualWordStart = wordElement?.dataset.virtualWordStart;
|
|
3581
|
+
const virtualWordEnd = wordElement?.dataset.virtualWordEnd;
|
|
3582
|
+
if (virtualWordStart || virtualWordEnd) {
|
|
3583
|
+
return `virtual:${virtualWordStart || ''}:${virtualWordEnd || ''}`;
|
|
3584
|
+
}
|
|
3585
|
+
return (syllable.getAttribute('data-word-index') ||
|
|
3586
|
+
syllable.getAttribute('data-syllable-index') ||
|
|
3587
|
+
'');
|
|
3588
|
+
}
|
|
3589
|
+
static getNextWordSyllable(syllables, index) {
|
|
3590
|
+
const current = syllables[index];
|
|
3591
|
+
const currentWordIndex = AmLyrics.getSyllableWordIndex(current);
|
|
3592
|
+
const previousSyllable = current;
|
|
3593
|
+
for (let i = index + 1; i < syllables.length; i += 1) {
|
|
3594
|
+
const candidate = syllables[i];
|
|
3595
|
+
if (candidate.classList.contains('transliteration')) {
|
|
3596
|
+
// eslint-disable-next-line no-continue
|
|
3597
|
+
continue;
|
|
3598
|
+
}
|
|
3599
|
+
const candidateWordIndex = AmLyrics.getSyllableWordIndex(candidate);
|
|
3600
|
+
if (candidateWordIndex === currentWordIndex ||
|
|
3601
|
+
!AmLyrics.hasTextBoundaryAfter(previousSyllable)) {
|
|
3602
|
+
return null;
|
|
3603
|
+
}
|
|
3604
|
+
return candidate;
|
|
3605
|
+
}
|
|
3606
|
+
return null;
|
|
3607
|
+
}
|
|
3608
|
+
static getPreviousNonTransliterationSyllable(syllables, index) {
|
|
3609
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
3610
|
+
const candidate = syllables[i];
|
|
3611
|
+
if (!candidate.classList.contains('transliteration')) {
|
|
3612
|
+
return candidate;
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
return null;
|
|
3616
|
+
}
|
|
3617
|
+
static getRenderedWordSyllables(syllable) {
|
|
3618
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3619
|
+
const wordElements = AmLyrics.getCachedVirtualWordElements(wordElement);
|
|
3620
|
+
const wordSyllables = wordElements.flatMap(element => Array.from(element.querySelectorAll('.lyrics-syllable')));
|
|
3621
|
+
return wordSyllables.filter(wordSyllable => !wordSyllable.classList.contains('transliteration'));
|
|
3622
|
+
}
|
|
3623
|
+
static getWordElementForSyllable(syllable) {
|
|
3624
|
+
return syllable.parentElement?.parentElement;
|
|
3625
|
+
}
|
|
3626
|
+
static getWordPreWipeKey(syllable) {
|
|
3627
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3628
|
+
return (wordElement?.dataset.virtualWordId ||
|
|
3629
|
+
`${syllable.getAttribute('data-start-time') || ''}:${AmLyrics.getSyllableWordIndex(syllable)}`);
|
|
3630
|
+
}
|
|
3631
|
+
static isPreWipeArmed(syllable) {
|
|
3632
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3633
|
+
const target = wordElement;
|
|
3634
|
+
return Boolean(target?._wordPreWipeKey === AmLyrics.getWordPreWipeKey(syllable));
|
|
3635
|
+
}
|
|
3636
|
+
static applyWordPreWipe(nextSyllable, wordSyllables, currentTimeMs, preWipeStartMs, preWipeDurationMs) {
|
|
3637
|
+
if (AmLyrics.isPreWipeArmed(nextSyllable))
|
|
3638
|
+
return;
|
|
3639
|
+
const wordElement = AmLyrics.getWordElementForSyllable(nextSyllable);
|
|
3640
|
+
const wordElements = AmLyrics.getCachedVirtualWordElements(wordElement);
|
|
3641
|
+
const charSpans = AmLyrics.getCachedVirtualWordCharSpans(wordElement, []);
|
|
3642
|
+
const elapsedPreWipe = currentTimeMs - preWipeStartMs;
|
|
3643
|
+
const charCount = charSpans.length ||
|
|
3644
|
+
wordSyllables.reduce((total, wordSyllable) => total + AmLyrics.getVisibleCharacterCount(wordSyllable), 0) ||
|
|
3645
|
+
AmLyrics.getVisibleCharacterCount(nextSyllable);
|
|
3646
|
+
AmLyrics.ensureWordWipeGeometry(charSpans, charCount);
|
|
3647
|
+
const leadChar = charSpans[0];
|
|
3648
|
+
const leadCharSyllable = leadChar?.closest('.lyrics-syllable');
|
|
3649
|
+
const preWipeSyllable = leadCharSyllable || wordSyllables[0] || nextSyllable;
|
|
3650
|
+
AmLyrics.applyWipeShape(preWipeSyllable, charCount);
|
|
3651
|
+
preWipeSyllable.style.setProperty('--pre-wipe-duration', `${preWipeDurationMs}ms`);
|
|
3652
|
+
preWipeSyllable.style.setProperty('--pre-wipe-delay', `${-elapsedPreWipe}ms`);
|
|
3653
|
+
preWipeSyllable.classList.add('pre-highlight');
|
|
3654
|
+
let leadSpans = charSpans;
|
|
3655
|
+
if (leadSpans.length === 0 && leadChar) {
|
|
3656
|
+
leadSpans = [leadChar];
|
|
3657
|
+
}
|
|
3658
|
+
leadSpans.forEach(span => {
|
|
3659
|
+
AmLyrics.applyWipeShape(span, charCount);
|
|
3660
|
+
span.style.setProperty('--pre-wipe-duration', `${preWipeDurationMs}ms`);
|
|
3661
|
+
span.style.setProperty('--pre-wipe-delay', `${-elapsedPreWipe}ms`);
|
|
3662
|
+
span.classList.add('pre-wipe-lead');
|
|
3663
|
+
});
|
|
3664
|
+
wordElements.forEach(element => {
|
|
3665
|
+
const target = element;
|
|
3666
|
+
target._wordPreWipeKey = AmLyrics.getWordPreWipeKey(nextSyllable);
|
|
3667
|
+
});
|
|
3668
|
+
}
|
|
3669
|
+
static maybePreWipeNextWord(syllables, index, currentTimeMs, currentEndTimeMs) {
|
|
3670
|
+
const syllable = syllables[index];
|
|
3671
|
+
if (syllable.classList.contains('line-synced') ||
|
|
3672
|
+
syllable.classList.contains('transliteration') ||
|
|
3673
|
+
syllable.closest('.lyrics-gap')) {
|
|
3674
|
+
return;
|
|
3675
|
+
}
|
|
3676
|
+
const currentWordReady = syllable.classList.contains('finished') ||
|
|
3677
|
+
currentTimeMs >= currentEndTimeMs - WORD_PRE_WIPE_HANDOFF_LEAD_MS;
|
|
3678
|
+
if (!currentWordReady) {
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
const nextSyllable = AmLyrics.getNextWordSyllable(syllables, index);
|
|
3682
|
+
if (!nextSyllable ||
|
|
3683
|
+
nextSyllable.classList.contains('line-synced') ||
|
|
3684
|
+
nextSyllable.classList.contains('transliteration') ||
|
|
3685
|
+
nextSyllable.closest('.lyrics-gap') ||
|
|
3686
|
+
nextSyllable.classList.contains('highlight') ||
|
|
3687
|
+
nextSyllable.classList.contains('finished')) {
|
|
3688
|
+
return;
|
|
3689
|
+
}
|
|
3690
|
+
const nextStartTimeMs = nextSyllable._cachedStartTime;
|
|
3691
|
+
if (!Number.isFinite(nextStartTimeMs))
|
|
3692
|
+
return;
|
|
3693
|
+
const gapMs = nextStartTimeMs - currentEndTimeMs;
|
|
3694
|
+
if (gapMs > NEXT_WORD_PRE_WIPE_MAX_GAP_MS || gapMs < -50) {
|
|
3695
|
+
return;
|
|
3696
|
+
}
|
|
3697
|
+
const nextWordSyllables = AmLyrics.getRenderedWordSyllables(nextSyllable);
|
|
3698
|
+
const preWipeSyllables = nextWordSyllables.length > 0 ? nextWordSyllables : [nextSyllable];
|
|
3699
|
+
const wordElement = AmLyrics.getWordElementForSyllable(nextSyllable);
|
|
3700
|
+
const wordCharSpans = AmLyrics.getCachedVirtualWordCharSpans(wordElement, []);
|
|
3701
|
+
const charCount = wordCharSpans.length ||
|
|
3702
|
+
preWipeSyllables.reduce((total, wordSyllable) => total + AmLyrics.getVisibleCharacterCount(wordSyllable), 0);
|
|
3703
|
+
if (charCount <= 0)
|
|
3704
|
+
return;
|
|
3705
|
+
const preWipeDuration = AmLyrics.clamp(64 + charCount * 9, NEXT_WORD_PRE_WIPE_MIN_DURATION_MS, NEXT_WORD_PRE_WIPE_MAX_DURATION_MS);
|
|
3706
|
+
const preWipeStart = Math.max(nextStartTimeMs - preWipeDuration, currentEndTimeMs - WORD_PRE_WIPE_HANDOFF_LEAD_MS);
|
|
3707
|
+
if (currentTimeMs < preWipeStart || currentTimeMs >= nextStartTimeMs) {
|
|
3708
|
+
return;
|
|
3709
|
+
}
|
|
3710
|
+
AmLyrics.applyWordPreWipe(nextSyllable, preWipeSyllables, currentTimeMs, preWipeStart, preWipeDuration);
|
|
3711
|
+
}
|
|
3712
|
+
static getCachedCharSpans(element) {
|
|
3713
|
+
const cacheTarget = element;
|
|
3714
|
+
if (!cacheTarget._cachedCharSpans) {
|
|
3715
|
+
cacheTarget._cachedCharSpans = Array.from(element.querySelectorAll('span.char'));
|
|
3716
|
+
}
|
|
3717
|
+
return cacheTarget._cachedCharSpans;
|
|
3718
|
+
}
|
|
3719
|
+
static getCachedVirtualWordElements(wordElement) {
|
|
3720
|
+
if (!wordElement)
|
|
3721
|
+
return [];
|
|
3722
|
+
const cacheTarget = wordElement;
|
|
3723
|
+
if (cacheTarget._cachedVirtualWordElements) {
|
|
3724
|
+
return cacheTarget._cachedVirtualWordElements;
|
|
3725
|
+
}
|
|
3726
|
+
const { virtualWordId } = wordElement.dataset;
|
|
3727
|
+
let wordElements = [wordElement];
|
|
3728
|
+
if (virtualWordId && wordElement.parentElement) {
|
|
3729
|
+
wordElements = Array.from(wordElement.parentElement.querySelectorAll('.lyrics-word')).filter(el => el.dataset.virtualWordId === virtualWordId);
|
|
3730
|
+
}
|
|
3731
|
+
wordElements.forEach(element => {
|
|
3732
|
+
const target = element;
|
|
3733
|
+
target._cachedVirtualWordElements = wordElements;
|
|
3734
|
+
});
|
|
3735
|
+
return wordElements;
|
|
3736
|
+
}
|
|
3737
|
+
static getCachedVirtualWordCharSpans(wordElement, fallbackCharSpans) {
|
|
3738
|
+
if (!wordElement)
|
|
3739
|
+
return fallbackCharSpans;
|
|
3740
|
+
const cacheTarget = wordElement;
|
|
3741
|
+
if (cacheTarget._cachedVirtualWordCharSpans) {
|
|
3742
|
+
return cacheTarget._cachedVirtualWordCharSpans;
|
|
3743
|
+
}
|
|
3744
|
+
const wordElements = AmLyrics.getCachedVirtualWordElements(wordElement);
|
|
3745
|
+
const charSpans = wordElements.flatMap(word => Array.from(word.querySelectorAll('span.char')));
|
|
3746
|
+
const result = charSpans.length > 0 ? charSpans : fallbackCharSpans;
|
|
3747
|
+
wordElements.forEach(element => {
|
|
3748
|
+
const target = element;
|
|
3749
|
+
target._cachedVirtualWordCharSpans = result;
|
|
3750
|
+
});
|
|
3751
|
+
return result;
|
|
3752
|
+
}
|
|
3343
3753
|
static updateSyllableAnimation(syllable, elapsedTimeMs = 0) {
|
|
3344
3754
|
if (syllable.classList.contains('highlight'))
|
|
3345
3755
|
return;
|
|
3346
3756
|
const { classList } = syllable;
|
|
3757
|
+
const hadPreHighlight = classList.contains('pre-highlight');
|
|
3347
3758
|
const isRTL = classList.contains('rtl-text');
|
|
3348
|
-
const charSpans =
|
|
3759
|
+
const charSpans = AmLyrics.getCachedCharSpans(syllable);
|
|
3349
3760
|
const wordElement = syllable.parentElement?.parentElement; // syllable-wrap -> word
|
|
3350
|
-
const
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
else if (wordElement) {
|
|
3357
|
-
wordElements = [wordElement];
|
|
3358
|
-
}
|
|
3359
|
-
const allWordCharSpans = wordElements.flatMap(word => Array.from(word.querySelectorAll('span.char')));
|
|
3360
|
-
const isGrowable = wordElement?.classList.contains('growable');
|
|
3361
|
-
const isCharRise = wordElement?.classList.contains('char-rise');
|
|
3761
|
+
const typedWordElement = wordElement;
|
|
3762
|
+
const allWordElements = AmLyrics.getCachedVirtualWordElements(typedWordElement);
|
|
3763
|
+
const allWordCharSpans = AmLyrics.getCachedVirtualWordCharSpans(typedWordElement, charSpans);
|
|
3764
|
+
const isGrowable = typedWordElement?.classList.contains('growable');
|
|
3765
|
+
const isCharRise = typedWordElement?.classList.contains('char-rise');
|
|
3766
|
+
const isCharDrag = typedWordElement?.classList.contains('char-drag');
|
|
3362
3767
|
const isFirstSyllable = syllable.getAttribute('data-syllable-index') === '0';
|
|
3363
3768
|
const syllableStartMs = parseFloat(syllable.getAttribute('data-start-time') || '0');
|
|
3364
|
-
const virtualWordStartMs = parseFloat(
|
|
3769
|
+
const virtualWordStartMs = parseFloat(typedWordElement?.dataset.virtualWordStart || '');
|
|
3365
3770
|
const isFirstInVirtualWord = isFirstSyllable &&
|
|
3366
3771
|
(!Number.isFinite(virtualWordStartMs) ||
|
|
3367
3772
|
Math.abs(syllableStartMs - virtualWordStartMs) < 0.5);
|
|
@@ -3372,7 +3777,10 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3372
3777
|
const wordDurationMs = parseFloat(syllable.getAttribute('data-word-duration') ||
|
|
3373
3778
|
syllable.getAttribute('data-duration') ||
|
|
3374
3779
|
'0') || syllableDurationMs;
|
|
3375
|
-
|
|
3780
|
+
const wordElapsedTimeMs = Number.isFinite(virtualWordStartMs)
|
|
3781
|
+
? elapsedTimeMs + (syllableStartMs - virtualWordStartMs)
|
|
3782
|
+
: elapsedTimeMs;
|
|
3783
|
+
const charWipeDurationMs = Math.max(wordDurationMs, syllableDurationMs);
|
|
3376
3784
|
const charAnimationsMap = new Map();
|
|
3377
3785
|
const styleUpdates = [];
|
|
3378
3786
|
// Step 1: Grow Pass
|
|
@@ -3420,16 +3828,60 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3420
3828
|
charAnimationsMap.set(span, `rise-char ${riseDurationMs}ms ease-in-out ${riseDelay}ms forwards`);
|
|
3421
3829
|
});
|
|
3422
3830
|
}
|
|
3831
|
+
if (isCharDrag && isFirstInVirtualWord && allWordCharSpans.length > 0) {
|
|
3832
|
+
const finalDuration = Math.max(wordDurationMs, syllableDurationMs);
|
|
3833
|
+
const baseDelayPerChar = AmLyrics.clamp(finalDuration * 0.15, 64, 118);
|
|
3834
|
+
const dragDurationMs = AmLyrics.clamp(finalDuration * 0.82, 560, 900);
|
|
3835
|
+
allWordCharSpans.forEach(span => {
|
|
3836
|
+
const charIndex = parseFloat(span.dataset.syllableCharIndex || '0');
|
|
3837
|
+
const dragDelay = baseDelayPerChar * charIndex;
|
|
3838
|
+
charAnimationsMap.set(span, `drag-char ${dragDurationMs}ms ease ${dragDelay}ms forwards`);
|
|
3839
|
+
});
|
|
3840
|
+
}
|
|
3423
3841
|
// Step 2: Wipe Pass
|
|
3424
3842
|
if (charSpans.length > 0) {
|
|
3425
|
-
|
|
3843
|
+
const wipeCharCount = allWordCharSpans.length ||
|
|
3844
|
+
charSpans.length ||
|
|
3845
|
+
AmLyrics.getVisibleCharacterCount(syllable);
|
|
3846
|
+
const wipeScale = AmLyrics.getLongWordWipeScale(wipeCharCount);
|
|
3847
|
+
AmLyrics.applyWipeShape(syllable, wipeCharCount);
|
|
3848
|
+
AmLyrics.ensureWordWipeGeometry(allWordCharSpans, wipeCharCount);
|
|
3849
|
+
allWordCharSpans.forEach(span => AmLyrics.applyWipeShape(span, wipeCharCount));
|
|
3850
|
+
const hasWordLevelWipe = !isFirstInVirtualWord &&
|
|
3851
|
+
(Boolean(typedWordElement?._wordWipeStarted) ||
|
|
3852
|
+
allWordCharSpans.some(span => span.style.animation.includes('wipe')));
|
|
3853
|
+
let charSpansToAnimate = charSpans;
|
|
3854
|
+
if (isFirstInVirtualWord) {
|
|
3855
|
+
charSpansToAnimate = allWordCharSpans;
|
|
3856
|
+
}
|
|
3857
|
+
else if (hasWordLevelWipe) {
|
|
3858
|
+
charSpansToAnimate = [];
|
|
3859
|
+
}
|
|
3860
|
+
if (charSpansToAnimate.length > 0 && allWordElements.length > 0) {
|
|
3861
|
+
allWordElements.forEach(element => {
|
|
3862
|
+
const target = element;
|
|
3863
|
+
target._wordWipeStarted = true;
|
|
3864
|
+
target._wordPreWipeKey = undefined;
|
|
3865
|
+
});
|
|
3866
|
+
}
|
|
3867
|
+
charSpansToAnimate.forEach((span, charIndex) => {
|
|
3426
3868
|
const startPct = parseFloat(span.dataset.wipeStart || '0');
|
|
3427
3869
|
const durationPct = parseFloat(span.dataset.wipeDuration || '0');
|
|
3428
|
-
const
|
|
3429
|
-
const
|
|
3430
|
-
|
|
3870
|
+
const globalCharIndex = parseFloat(span.dataset.syllableCharIndex || `${charIndex}`);
|
|
3871
|
+
const hadCharPreWipe = span.classList.contains('pre-wipe-lead') ||
|
|
3872
|
+
(hadPreHighlight && globalCharIndex === 0);
|
|
3873
|
+
const charStartMs = charWipeDurationMs * startPct;
|
|
3874
|
+
const remainingWordWipeMs = Math.max(0, charWipeDurationMs - charStartMs);
|
|
3875
|
+
let wipeDelay = charStartMs - wordElapsedTimeMs;
|
|
3876
|
+
let wipeDuration = Math.min(charWipeDurationMs * durationPct * wipeScale, remainingWordWipeMs);
|
|
3877
|
+
const useStartAnimation = isFirstInContainer && globalCharIndex === 0 && !hadCharPreWipe;
|
|
3431
3878
|
let charWipeAnimation = 'wipe';
|
|
3432
|
-
if (
|
|
3879
|
+
if (hadCharPreWipe) {
|
|
3880
|
+
charWipeAnimation = 'wipe-word-from-pre';
|
|
3881
|
+
wipeDelay = -wordElapsedTimeMs;
|
|
3882
|
+
wipeDuration = charWipeDurationMs;
|
|
3883
|
+
}
|
|
3884
|
+
else if (useStartAnimation) {
|
|
3433
3885
|
charWipeAnimation = isRTL ? 'start-wipe-rtl' : 'start-wipe';
|
|
3434
3886
|
}
|
|
3435
3887
|
else {
|
|
@@ -3439,22 +3891,13 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3439
3891
|
const animationParts = [];
|
|
3440
3892
|
if (existingAnimation &&
|
|
3441
3893
|
(existingAnimation.includes('grow-dynamic') ||
|
|
3442
|
-
existingAnimation.includes('rise-char')
|
|
3894
|
+
existingAnimation.includes('rise-char') ||
|
|
3895
|
+
existingAnimation.includes('drag-char'))) {
|
|
3443
3896
|
animationParts.push(existingAnimation.split(',')[0].trim());
|
|
3444
3897
|
}
|
|
3445
|
-
if (charIndex > 0 && wipeDelay > 0 && wipeDuration > 0) {
|
|
3446
|
-
const arrivalTime = (span.dataset.preWipeArrival
|
|
3447
|
-
? parseFloat(span.dataset.preWipeArrival)
|
|
3448
|
-
: syllableDurationMs * startPct) - elapsedTimeMs;
|
|
3449
|
-
const measuredPreWipeDuration = parseFloat(span.dataset.preWipeDuration || '100');
|
|
3450
|
-
const preWipeDuration = Math.min(measuredPreWipeDuration, wipeDuration * 0.9, syllableDurationMs * 0.08, arrivalTime);
|
|
3451
|
-
const animDelay = arrivalTime - preWipeDuration;
|
|
3452
|
-
if (preWipeDuration >= 16) {
|
|
3453
|
-
animationParts.push(`pre-wipe-char ${preWipeDuration}ms linear ${animDelay}ms none`);
|
|
3454
|
-
}
|
|
3455
|
-
}
|
|
3456
3898
|
if (wipeDuration > 0) {
|
|
3457
|
-
|
|
3899
|
+
const wipeFillMode = hadCharPreWipe ? 'both' : 'forwards';
|
|
3900
|
+
animationParts.push(`${charWipeAnimation} ${wipeDuration}ms linear ${wipeDelay}ms ${wipeFillMode}`);
|
|
3458
3901
|
}
|
|
3459
3902
|
if (animationParts.length > 0) {
|
|
3460
3903
|
charAnimationsMap.set(span, animationParts.join(', '));
|
|
@@ -3464,9 +3907,15 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3464
3907
|
else {
|
|
3465
3908
|
// Syllable-level wipe for regular (non-growable) words without chars
|
|
3466
3909
|
const wipeRatio = parseFloat(syllable.getAttribute('data-wipe-ratio') || '1');
|
|
3467
|
-
const
|
|
3910
|
+
const wipeCharCount = AmLyrics.getVisibleCharacterCount(syllable);
|
|
3911
|
+
const wipeScale = AmLyrics.getLongWordWipeScale(wipeCharCount);
|
|
3912
|
+
const visualDuration = syllableDurationMs * wipeRatio * wipeScale;
|
|
3913
|
+
AmLyrics.applyWipeShape(syllable, wipeCharCount);
|
|
3468
3914
|
let wipeAnimation = 'wipe';
|
|
3469
|
-
if (
|
|
3915
|
+
if (hadPreHighlight) {
|
|
3916
|
+
wipeAnimation = isRTL ? 'wipe-from-pre-rtl' : 'wipe-from-pre';
|
|
3917
|
+
}
|
|
3918
|
+
else if (isFirstInContainer) {
|
|
3470
3919
|
wipeAnimation = isRTL ? 'start-wipe-rtl' : 'start-wipe';
|
|
3471
3920
|
}
|
|
3472
3921
|
else {
|
|
@@ -3479,16 +3928,25 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3479
3928
|
syllable.style.animation = `${currentWipeAnimation} ${visualDuration}ms ${isGap ? 'ease-out' : 'linear'} ${-elapsedTimeMs}ms forwards`;
|
|
3480
3929
|
}
|
|
3481
3930
|
// --- WRITE PHASE ---
|
|
3931
|
+
if (allWordElements.length > 0) {
|
|
3932
|
+
allWordElements.forEach(element => {
|
|
3933
|
+
const target = element;
|
|
3934
|
+
target._wordPreWipeKey = undefined;
|
|
3935
|
+
});
|
|
3936
|
+
}
|
|
3482
3937
|
classList.remove('pre-highlight');
|
|
3483
3938
|
classList.add('highlight');
|
|
3939
|
+
allWordCharSpans.forEach(span => AmLyrics.clearPreWipeLead(span));
|
|
3940
|
+
// Apply keyframe variables before assigning animation strings so the
|
|
3941
|
+
// first painted frame never uses fallback transform values.
|
|
3942
|
+
for (const update of styleUpdates) {
|
|
3943
|
+
update.element.style.setProperty(update.property, update.value);
|
|
3944
|
+
}
|
|
3484
3945
|
for (const [span, animationString] of charAnimationsMap.entries()) {
|
|
3485
3946
|
span.style.willChange = 'transform';
|
|
3947
|
+
span.style.removeProperty('background-color');
|
|
3486
3948
|
span.style.animation = animationString;
|
|
3487
3949
|
}
|
|
3488
|
-
// Apply style updates
|
|
3489
|
-
for (const update of styleUpdates) {
|
|
3490
|
-
update.element.style.setProperty(update.property, update.value);
|
|
3491
|
-
}
|
|
3492
3950
|
}
|
|
3493
3951
|
/**
|
|
3494
3952
|
* Reset syllable animation state
|
|
@@ -3512,10 +3970,19 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3512
3970
|
el.style.animation = '';
|
|
3513
3971
|
el.style.transition = 'none';
|
|
3514
3972
|
el.style.backgroundColor = 'var(--lyplus-text-secondary)';
|
|
3973
|
+
AmLyrics.clearPreWipeLead(el);
|
|
3515
3974
|
}
|
|
3516
3975
|
// Immediately remove all state classes
|
|
3517
3976
|
syllable.classList.remove('highlight', 'finished', 'pre-highlight', 'cleanup');
|
|
3518
3977
|
}
|
|
3978
|
+
static resetWordAnimationState(line) {
|
|
3979
|
+
const wordElements = line.querySelectorAll('.lyrics-word');
|
|
3980
|
+
wordElements.forEach(wordElement => {
|
|
3981
|
+
const target = wordElement;
|
|
3982
|
+
target._wordPreWipeKey = undefined;
|
|
3983
|
+
target._wordWipeStarted = false;
|
|
3984
|
+
});
|
|
3985
|
+
}
|
|
3519
3986
|
/**
|
|
3520
3987
|
* Reset all syllables in a line — batches deferred cleanup into a single rAF
|
|
3521
3988
|
*/
|
|
@@ -3523,6 +3990,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3523
3990
|
if (!line)
|
|
3524
3991
|
return;
|
|
3525
3992
|
line.classList.remove('persist-highlight');
|
|
3993
|
+
AmLyrics.resetWordAnimationState(line);
|
|
3526
3994
|
// eslint-disable-next-line no-param-reassign
|
|
3527
3995
|
line._cachedSyllableElements = null;
|
|
3528
3996
|
const syllables = line.getElementsByClassName('lyrics-syllable');
|
|
@@ -3554,6 +4022,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3554
4022
|
if (!line)
|
|
3555
4023
|
return;
|
|
3556
4024
|
line.classList.remove('persist-highlight');
|
|
4025
|
+
AmLyrics.resetWordAnimationState(line);
|
|
3557
4026
|
const syllables = line.getElementsByClassName('lyrics-syllable');
|
|
3558
4027
|
for (let i = 0; i < syllables.length; i += 1) {
|
|
3559
4028
|
const s = syllables[i];
|
|
@@ -3571,6 +4040,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3571
4040
|
el.style.removeProperty('background-color');
|
|
3572
4041
|
el.style.removeProperty('transition');
|
|
3573
4042
|
el.style.removeProperty('filter');
|
|
4043
|
+
AmLyrics.clearPreWipeLead(el);
|
|
3574
4044
|
}
|
|
3575
4045
|
}
|
|
3576
4046
|
}
|
|
@@ -3607,19 +4077,26 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3607
4077
|
syllable.style.animation = '';
|
|
3608
4078
|
syllable.style.removeProperty('--pre-wipe-duration');
|
|
3609
4079
|
syllable.style.removeProperty('--pre-wipe-delay');
|
|
4080
|
+
syllable.style.removeProperty('background-color');
|
|
4081
|
+
AmLyrics.applyWipeShape(syllable, AmLyrics.getVisibleCharacterCount(syllable));
|
|
3610
4082
|
const chars = syllable.querySelectorAll('span.char');
|
|
3611
4083
|
for (let ci = 0; ci < chars.length; ci += 1) {
|
|
3612
4084
|
const charEl = chars[ci];
|
|
3613
4085
|
const currentAnim = charEl.style.animation || '';
|
|
3614
4086
|
if (currentAnim.includes('grow-dynamic') ||
|
|
3615
|
-
currentAnim.includes('rise-char')
|
|
4087
|
+
currentAnim.includes('rise-char') ||
|
|
4088
|
+
currentAnim.includes('drag-char')) {
|
|
3616
4089
|
const parts = currentAnim.split(',').map(p => p.trim());
|
|
3617
|
-
const transformAnim = parts.find(p => p.includes('grow-dynamic') ||
|
|
4090
|
+
const transformAnim = parts.find(p => p.includes('grow-dynamic') ||
|
|
4091
|
+
p.includes('rise-char') ||
|
|
4092
|
+
p.includes('drag-char'));
|
|
3618
4093
|
charEl.style.animation = transformAnim || '';
|
|
3619
4094
|
}
|
|
3620
4095
|
else {
|
|
3621
4096
|
charEl.style.animation = '';
|
|
3622
4097
|
}
|
|
4098
|
+
charEl.style.backgroundColor = 'var(--lyplus-text-primary)';
|
|
4099
|
+
AmLyrics.clearPreWipeLead(charEl);
|
|
3623
4100
|
}
|
|
3624
4101
|
}
|
|
3625
4102
|
}
|
|
@@ -3660,14 +4137,16 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3660
4137
|
// Early exit check
|
|
3661
4138
|
if (!(currentTimeMs < startTime - 1000 && !hasActiveState)) {
|
|
3662
4139
|
let preHighlightReset = false;
|
|
3663
|
-
//
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
4140
|
+
// Before the syllable starts, pre-highlight only belongs beside a
|
|
4141
|
+
// previous active word. Once the syllable starts, updateSyllableAnimation
|
|
4142
|
+
// consumes the class so the actual wipe can continue from the pre-wipe
|
|
4143
|
+
// pose instead of restarting from the beginning.
|
|
4144
|
+
if (hasPreHighlight && currentTimeMs < startTime) {
|
|
4145
|
+
const prevSyllable = AmLyrics.getPreviousNonTransliterationSyllable(syllables, i);
|
|
4146
|
+
const previousCarriesHighlight = prevSyllable?.classList.contains('highlight') ||
|
|
4147
|
+
prevSyllable?.classList.contains('finished');
|
|
4148
|
+
if (!previousCarriesHighlight) {
|
|
4149
|
+
AmLyrics.clearPreHighlight(syllable);
|
|
3671
4150
|
preHighlightReset = true;
|
|
3672
4151
|
}
|
|
3673
4152
|
}
|
|
@@ -3695,6 +4174,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3695
4174
|
// Not yet started
|
|
3696
4175
|
AmLyrics.resetSyllable(syllable);
|
|
3697
4176
|
}
|
|
4177
|
+
AmLyrics.maybePreWipeNextWord(syllables, i, currentTimeMs, endTime);
|
|
3698
4178
|
}
|
|
3699
4179
|
}
|
|
3700
4180
|
}
|
|
@@ -4004,6 +4484,9 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4004
4484
|
data-end-time="${endTimeMs}"
|
|
4005
4485
|
data-duration="${durationMs}"
|
|
4006
4486
|
data-syllable-index="${syllableIndex}"
|
|
4487
|
+
data-word-index="${syllableIndex}"
|
|
4488
|
+
data-word-length="${syllable.text.replace(/\s/g, '')
|
|
4489
|
+
.length}"
|
|
4007
4490
|
data-wipe-ratio="1"
|
|
4008
4491
|
>${syllable.text}</span
|
|
4009
4492
|
>${bgRomanizedText}</span
|
|
@@ -4025,13 +4508,13 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4025
4508
|
const groupGrowable = lineData?.groupGrowable ?? [];
|
|
4026
4509
|
const groupGlowing = lineData?.groupGlowing ?? [];
|
|
4027
4510
|
const groupCharRise = lineData?.groupCharRise ?? [];
|
|
4511
|
+
const groupCharDrag = lineData?.groupCharDrag ?? [];
|
|
4028
4512
|
const vwFullText = lineData?.vwFullText ?? [];
|
|
4029
4513
|
const vwFullDuration = lineData?.vwFullDuration ?? [];
|
|
4030
4514
|
const vwCharOffset = lineData?.vwCharOffset ?? [];
|
|
4031
4515
|
const vwStartMs = lineData?.vwStartMs ?? [];
|
|
4032
4516
|
const vwEndMs = lineData?.vwEndMs ?? [];
|
|
4033
4517
|
const lineIsRTL = lineData?.lineIsRTL ?? false;
|
|
4034
|
-
// Create main vocals using YouLyPlus syllable structure
|
|
4035
4518
|
const mainVocalElement = b `<p
|
|
4036
4519
|
class="main-vocal-container ${lineIsRTL ? 'rtl-text' : ''}"
|
|
4037
4520
|
>
|
|
@@ -4039,25 +4522,23 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4039
4522
|
const isGrowable = groupGrowable[groupIdx];
|
|
4040
4523
|
const isGlowing = groupGlowing[groupIdx];
|
|
4041
4524
|
const isCharRise = groupCharRise[groupIdx];
|
|
4042
|
-
const
|
|
4525
|
+
const isCharDrag = groupCharDrag[groupIdx];
|
|
4526
|
+
const isAnimatedByChar = isGrowable || isCharRise || isCharDrag;
|
|
4043
4527
|
const groupLineSynced = group.some(s => s.lineSynced);
|
|
4044
4528
|
const wordText = isAnimatedByChar ? vwFullText[groupIdx] : '';
|
|
4045
4529
|
const wordDuration = isAnimatedByChar
|
|
4046
4530
|
? vwFullDuration[groupIdx]
|
|
4047
4531
|
: 0;
|
|
4048
|
-
const wordNumChars = wordText.length;
|
|
4532
|
+
const wordNumChars = wordText.replace(/\s/g, '').length;
|
|
4049
4533
|
const groupCharOffset = isAnimatedByChar
|
|
4050
4534
|
? vwCharOffset[groupIdx]
|
|
4051
4535
|
: 0;
|
|
4052
|
-
const virtualWordId =
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
const virtualWordStart = isAnimatedByChar
|
|
4056
|
-
? vwStartMs[groupIdx]
|
|
4057
|
-
: '';
|
|
4058
|
-
const virtualWordEnd = isAnimatedByChar ? vwEndMs[groupIdx] : '';
|
|
4536
|
+
const virtualWordId = `${lineIndex}:${vwStartMs[groupIdx]}:${vwEndMs[groupIdx]}`;
|
|
4537
|
+
const virtualWordStart = vwStartMs[groupIdx];
|
|
4538
|
+
const virtualWordEnd = vwEndMs[groupIdx];
|
|
4059
4539
|
let sylCharAccumulator = 0;
|
|
4060
4540
|
const groupText = group.map(s => s.text).join('');
|
|
4541
|
+
const visibleWordLength = groupText.replace(/\s/g, '').length;
|
|
4061
4542
|
const shouldAllowBreak = groupText.trim().length >= 16 ||
|
|
4062
4543
|
/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/.test(groupText);
|
|
4063
4544
|
// Calculate dynamic rise duration based on the audio duration of the word
|
|
@@ -4069,9 +4550,9 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4069
4550
|
return b `<span
|
|
4070
4551
|
class="lyrics-word${isGrowable ? ' growable' : ''}${isCharRise
|
|
4071
4552
|
? ' char-rise'
|
|
4072
|
-
: ''}${
|
|
4073
|
-
? '
|
|
4074
|
-
: ''}"
|
|
4553
|
+
: ''}${isCharDrag ? ' char-drag' : ''}${isGlowing
|
|
4554
|
+
? ' glowing'
|
|
4555
|
+
: ''}${shouldAllowBreak ? ' allow-break' : ''}"
|
|
4075
4556
|
data-virtual-word-id="${virtualWordId}"
|
|
4076
4557
|
data-virtual-word-start="${virtualWordStart}"
|
|
4077
4558
|
data-virtual-word-end="${virtualWordEnd}"
|
|
@@ -4098,13 +4579,26 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4098
4579
|
: '';
|
|
4099
4580
|
let syllableContent = text;
|
|
4100
4581
|
if (isAnimatedByChar) {
|
|
4101
|
-
let charIndexInsideSyllable = 0;
|
|
4102
4582
|
const numCharsInSyllable = text.replace(/\s/g, '').length || 1;
|
|
4583
|
+
const hasVirtualTiming = wordDuration > 0 && Number.isFinite(virtualWordStart);
|
|
4584
|
+
const syllableStartRatio = hasVirtualTiming
|
|
4585
|
+
? AmLyrics.clamp((startTimeMs - virtualWordStart) / wordDuration, 0, 1)
|
|
4586
|
+
: 0;
|
|
4587
|
+
const syllableDurationRatio = hasVirtualTiming
|
|
4588
|
+
? AmLyrics.clamp(durationMs / wordDuration, 0, 1)
|
|
4589
|
+
: 1;
|
|
4590
|
+
let charIndexInsideSyllable = 0;
|
|
4103
4591
|
syllableContent = b `${text.split('').map(char => {
|
|
4104
4592
|
if (char === ' ')
|
|
4105
4593
|
return ' ';
|
|
4106
4594
|
const charIndexInsideWord = groupCharOffset + sylCharAccumulator;
|
|
4107
|
-
const
|
|
4595
|
+
const localCharIndex = charIndexInsideSyllable;
|
|
4596
|
+
const visibleWordChars = Math.max(1, wordNumChars);
|
|
4597
|
+
const charStartPercentVal = AmLyrics.clamp(syllableStartRatio +
|
|
4598
|
+
(localCharIndex / numCharsInSyllable) *
|
|
4599
|
+
syllableDurationRatio, 0, 1);
|
|
4600
|
+
const charDurationPercentVal = syllableDurationRatio / numCharsInSyllable ||
|
|
4601
|
+
1 / visibleWordChars;
|
|
4108
4602
|
sylCharAccumulator += 1;
|
|
4109
4603
|
charIndexInsideSyllable += 1;
|
|
4110
4604
|
const minDuration = 400;
|
|
@@ -4151,21 +4645,37 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4151
4645
|
const normalizedGrowth = (charMaxScale - 1.0) / 0.1;
|
|
4152
4646
|
const effectiveDuration = (wordDuration + durationMs * 2) / 3;
|
|
4153
4647
|
const peakMultiplier = Math.min(1, Math.max(0.3, effectiveDuration / 2000));
|
|
4154
|
-
const
|
|
4648
|
+
const baseTranslateYPeak = -normalizedGrowth * (2 * peakMultiplier); // Further dampened lift peak
|
|
4155
4649
|
const position = (charIndexInsideWord + 0.5) / wordNumChars;
|
|
4156
4650
|
const horizontalOffset = (position - 0.5) * 2 * ((charMaxScale - 1.0) * 25);
|
|
4651
|
+
const isDragMotion = isCharDrag;
|
|
4652
|
+
let charTranslateYPeak = baseTranslateYPeak;
|
|
4653
|
+
if (isCharRise) {
|
|
4654
|
+
charTranslateYPeak = 0;
|
|
4655
|
+
}
|
|
4656
|
+
else if (isDragMotion) {
|
|
4657
|
+
charTranslateYPeak = -0.78;
|
|
4658
|
+
}
|
|
4659
|
+
let motionHorizontalOffset = horizontalOffset;
|
|
4660
|
+
if (isCharRise) {
|
|
4661
|
+
motionHorizontalOffset = 0;
|
|
4662
|
+
}
|
|
4663
|
+
else if (isDragMotion) {
|
|
4664
|
+
motionHorizontalOffset = 0;
|
|
4665
|
+
}
|
|
4157
4666
|
return b `<span
|
|
4158
4667
|
class="char"
|
|
4159
4668
|
data-char-index="${charIndexInsideWord}"
|
|
4160
4669
|
data-syllable-char-index="${charIndexInsideWord}"
|
|
4161
4670
|
data-wipe-start="${charStartPercentVal.toFixed(4)}"
|
|
4162
|
-
data-wipe-duration="${
|
|
4671
|
+
data-wipe-duration="${charDurationPercentVal.toFixed(4)}"
|
|
4163
4672
|
data-horizontal-offset="${horizontalOffset.toFixed(2)}"
|
|
4164
4673
|
data-max-scale="${charMaxScale.toFixed(3)}"
|
|
4165
4674
|
data-matrix-scale="${(charMaxScale * 0.98).toFixed(3)}"
|
|
4166
|
-
data-char-offset-x="${(
|
|
4675
|
+
data-char-offset-x="${(motionHorizontalOffset * 0.98).toFixed(2)}"
|
|
4167
4676
|
data-shadow-intensity="${charShadowIntensity.toFixed(3)}"
|
|
4168
4677
|
data-translate-y-peak="${charTranslateYPeak.toFixed(3)}"
|
|
4678
|
+
style="--word-wipe-width: ${visibleWordChars}ch; --char-wipe-position: -${charIndexInsideWord}ch"
|
|
4169
4679
|
>${char}</span
|
|
4170
4680
|
>`;
|
|
4171
4681
|
})}`;
|
|
@@ -4183,6 +4693,8 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4183
4693
|
data-duration="${durationMs}"
|
|
4184
4694
|
data-word-duration="${wordDuration}"
|
|
4185
4695
|
data-syllable-index="${sylIdx}"
|
|
4696
|
+
data-word-index="${groupIdx}"
|
|
4697
|
+
data-word-length="${visibleWordLength}"
|
|
4186
4698
|
data-wipe-ratio="1"
|
|
4187
4699
|
>${syllableContent}</span
|
|
4188
4700
|
>${romanizedText}</span
|
|
@@ -4415,13 +4927,14 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4415
4927
|
<button
|
|
4416
4928
|
class="download-button source-switch-btn"
|
|
4417
4929
|
title="Switch Lyrics Source"
|
|
4418
|
-
style="font-family: inherit; font-size: 11px; padding: 2px 6px; border-radius: 4px; border: 1px solid rgba(255, 255, 255, 0.2); background: transparent; cursor: pointer; color: #aaa; display: inline-flex; align-items: center;"
|
|
4419
4930
|
@click=${this.switchSource}
|
|
4420
4931
|
?disabled=${this.isFetchingAlternatives}
|
|
4421
4932
|
>
|
|
4422
4933
|
<svg
|
|
4423
|
-
class="source-switch-svg lucide lucide-arrow-down-up-icon lucide-arrow-down-up
|
|
4424
|
-
|
|
4934
|
+
class="source-switch-svg lucide lucide-arrow-down-up-icon lucide-arrow-down-up ${this
|
|
4935
|
+
.isFetchingAlternatives
|
|
4936
|
+
? 'is-loading'
|
|
4937
|
+
: ''}"
|
|
4425
4938
|
xmlns="http://www.w3.org/2000/svg"
|
|
4426
4939
|
width="12"
|
|
4427
4940
|
height="12"
|
|
@@ -4479,9 +4992,6 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4479
4992
|
}
|
|
4480
4993
|
};
|
|
4481
4994
|
AmLyrics$1.styles = i$3 `
|
|
4482
|
-
/* ==========================================================================
|
|
4483
|
-
YOULYPLUS-INSPIRED STYLING - Design Tokens & Variables
|
|
4484
|
-
========================================================================== */
|
|
4485
4995
|
:host {
|
|
4486
4996
|
--lyplus-lyrics-palette: var(
|
|
4487
4997
|
--am-lyrics-highlight-color,
|
|
@@ -4510,6 +5020,8 @@ AmLyrics$1.styles = i$3 `
|
|
|
4510
5020
|
--lyplus-blur-amount: 0.07em;
|
|
4511
5021
|
--lyplus-blur-amount-near: 0.035em;
|
|
4512
5022
|
--lyplus-fade-gap-timing-function: ease-out;
|
|
5023
|
+
--wipe-gradient-width: 0.75em;
|
|
5024
|
+
--wipe-gradient-half: 0.375em;
|
|
4513
5025
|
|
|
4514
5026
|
--lyrics-scroll-padding-top: 25%;
|
|
4515
5027
|
|
|
@@ -4537,6 +5049,9 @@ AmLyrics$1.styles = i$3 `
|
|
|
4537
5049
|
max-height: 100vh;
|
|
4538
5050
|
overflow-y: auto;
|
|
4539
5051
|
-webkit-overflow-scrolling: touch;
|
|
5052
|
+
-webkit-touch-callout: none;
|
|
5053
|
+
-webkit-user-select: none;
|
|
5054
|
+
user-select: none;
|
|
4540
5055
|
box-sizing: border-box;
|
|
4541
5056
|
scrollbar-width: none;
|
|
4542
5057
|
overflow-anchor: none;
|
|
@@ -4666,7 +5181,7 @@ AmLyrics$1.styles = i$3 `
|
|
|
4666
5181
|
.lyrics-line.bg-expanded .background-vocal-container {
|
|
4667
5182
|
max-height: 4em;
|
|
4668
5183
|
opacity: 1;
|
|
4669
|
-
will-change:
|
|
5184
|
+
will-change: opacity;
|
|
4670
5185
|
}
|
|
4671
5186
|
|
|
4672
5187
|
.lyrics-line.bg-expanded .background-vocal-wrap {
|
|
@@ -4818,11 +5333,22 @@ AmLyrics$1.styles = i$3 `
|
|
|
4818
5333
|
white-space: nowrap;
|
|
4819
5334
|
}
|
|
4820
5335
|
|
|
5336
|
+
.lyrics-word.char-drag {
|
|
5337
|
+
display: inline-block;
|
|
5338
|
+
vertical-align: baseline;
|
|
5339
|
+
white-space: nowrap;
|
|
5340
|
+
}
|
|
5341
|
+
|
|
4821
5342
|
.lyrics-word.char-rise.allow-break {
|
|
4822
5343
|
display: inline;
|
|
4823
5344
|
white-space: normal;
|
|
4824
5345
|
}
|
|
4825
5346
|
|
|
5347
|
+
.lyrics-word.char-drag.allow-break {
|
|
5348
|
+
display: inline;
|
|
5349
|
+
white-space: normal;
|
|
5350
|
+
}
|
|
5351
|
+
|
|
4826
5352
|
.lyrics-syllable-wrap {
|
|
4827
5353
|
display: inline;
|
|
4828
5354
|
}
|
|
@@ -4877,44 +5403,30 @@ AmLyrics$1.styles = i$3 `
|
|
|
4877
5403
|
.lyrics-line.active:not(.lyrics-gap)
|
|
4878
5404
|
.lyrics-syllable.pre-highlight.no-chars {
|
|
4879
5405
|
background-repeat: no-repeat;
|
|
4880
|
-
background-image:
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
var(--
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
var(--lyplus-text-primary, #fff) 100%,
|
|
4890
|
-
#0000 100%
|
|
4891
|
-
);
|
|
4892
|
-
background-size:
|
|
4893
|
-
0.5em 100%,
|
|
4894
|
-
0% 100%;
|
|
4895
|
-
background-position:
|
|
4896
|
-
-0.5em 0%,
|
|
4897
|
-
-0.25em 0%;
|
|
5406
|
+
background-image: linear-gradient(
|
|
5407
|
+
90deg,
|
|
5408
|
+
var(--lyplus-text-primary, #fff) 0%,
|
|
5409
|
+
var(--lyplus-text-primary, #fff)
|
|
5410
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5411
|
+
#0000 100%
|
|
5412
|
+
);
|
|
5413
|
+
background-size: 0% 100%;
|
|
5414
|
+
background-position: left;
|
|
4898
5415
|
}
|
|
4899
5416
|
|
|
4900
5417
|
.lyrics-line.active:not(.lyrics-gap) .lyrics-syllable.highlight.rtl-text,
|
|
4901
5418
|
.lyrics-line.active:not(.lyrics-gap)
|
|
4902
5419
|
.lyrics-syllable.pre-highlight.rtl-text {
|
|
4903
5420
|
direction: rtl;
|
|
4904
|
-
background-image:
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
transparent 100%
|
|
4914
|
-
);
|
|
4915
|
-
background-position:
|
|
4916
|
-
calc(100% + 0.5em) 0%,
|
|
4917
|
-
right 0%;
|
|
5421
|
+
background-image: linear-gradient(
|
|
5422
|
+
-90deg,
|
|
5423
|
+
var(--lyplus-text-primary) 0%,
|
|
5424
|
+
var(--lyplus-text-primary)
|
|
5425
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5426
|
+
transparent 100%
|
|
5427
|
+
);
|
|
5428
|
+
background-size: 0% 100%;
|
|
5429
|
+
background-position: right 0%;
|
|
4918
5430
|
}
|
|
4919
5431
|
|
|
4920
5432
|
/* Background vocals: muted gray wipe instead of white.
|
|
@@ -4931,18 +5443,13 @@ AmLyrics$1.styles = i$3 `
|
|
|
4931
5443
|
.lyrics-line.pre-active
|
|
4932
5444
|
.background-vocal-container
|
|
4933
5445
|
.lyrics-syllable.pre-highlight.no-chars {
|
|
4934
|
-
background-image:
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
linear-gradient(
|
|
4942
|
-
90deg,
|
|
4943
|
-
color-mix(in srgb, var(--lyplus-text-primary, #fff) 50%, #888888) 100%,
|
|
4944
|
-
#0000 100%
|
|
4945
|
-
);
|
|
5446
|
+
background-image: linear-gradient(
|
|
5447
|
+
90deg,
|
|
5448
|
+
color-mix(in srgb, var(--lyplus-text-primary, #fff) 50%, #888888) 0%,
|
|
5449
|
+
color-mix(in srgb, var(--lyplus-text-primary, #fff) 50%, #888888)
|
|
5450
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5451
|
+
#0000 100%
|
|
5452
|
+
);
|
|
4946
5453
|
}
|
|
4947
5454
|
|
|
4948
5455
|
.lyrics-line.active
|
|
@@ -4957,17 +5464,13 @@ AmLyrics$1.styles = i$3 `
|
|
|
4957
5464
|
.lyrics-line.pre-active
|
|
4958
5465
|
.background-vocal-container
|
|
4959
5466
|
.lyrics-syllable.pre-highlight.rtl-text {
|
|
4960
|
-
background-image:
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
-90deg,
|
|
4968
|
-
color-mix(in srgb, var(--lyplus-text-primary) 50%, #888888) 100%,
|
|
4969
|
-
transparent 100%
|
|
4970
|
-
);
|
|
5467
|
+
background-image: linear-gradient(
|
|
5468
|
+
-90deg,
|
|
5469
|
+
color-mix(in srgb, var(--lyplus-text-primary) 50%, #888888) 0%,
|
|
5470
|
+
color-mix(in srgb, var(--lyplus-text-primary) 50%, #888888)
|
|
5471
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5472
|
+
transparent 100%
|
|
5473
|
+
);
|
|
4971
5474
|
}
|
|
4972
5475
|
|
|
4973
5476
|
/* Non-growable words float up with a gentle curve */
|
|
@@ -4991,6 +5494,10 @@ AmLyrics$1.styles = i$3 `
|
|
|
4991
5494
|
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
4992
5495
|
}
|
|
4993
5496
|
|
|
5497
|
+
.lyrics-word.char-drag .lyrics-syllable.cleanup .char {
|
|
5498
|
+
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
5499
|
+
}
|
|
5500
|
+
|
|
4994
5501
|
.lyrics-line.persist-highlight
|
|
4995
5502
|
.lyrics-word.growable
|
|
4996
5503
|
.lyrics-syllable.finished
|
|
@@ -4998,6 +5505,10 @@ AmLyrics$1.styles = i$3 `
|
|
|
4998
5505
|
.lyrics-line.persist-highlight
|
|
4999
5506
|
.lyrics-word.char-rise
|
|
5000
5507
|
.lyrics-syllable.finished
|
|
5508
|
+
.char,
|
|
5509
|
+
.lyrics-line.persist-highlight
|
|
5510
|
+
.lyrics-word.char-drag
|
|
5511
|
+
.lyrics-syllable.finished
|
|
5001
5512
|
.char {
|
|
5002
5513
|
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
5003
5514
|
}
|
|
@@ -5108,70 +5619,56 @@ AmLyrics$1.styles = i$3 `
|
|
|
5108
5619
|
transform 0.7s ease;
|
|
5109
5620
|
}
|
|
5110
5621
|
|
|
5622
|
+
.lyrics-word.char-drag span.char {
|
|
5623
|
+
transition: color 0.18s;
|
|
5624
|
+
}
|
|
5625
|
+
|
|
5111
5626
|
/* Active char spans: structural only, wipe animation sets gradient */
|
|
5112
5627
|
.lyrics-line.active .lyrics-syllable span.char {
|
|
5113
5628
|
background-clip: text;
|
|
5114
5629
|
-webkit-background-clip: text;
|
|
5115
5630
|
background-repeat: no-repeat;
|
|
5116
|
-
background-image:
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
var(--
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
var(--lyplus-text-primary, #fff) 100%,
|
|
5126
|
-
#0000 100%
|
|
5127
|
-
);
|
|
5128
|
-
background-size:
|
|
5129
|
-
0.5em 100%,
|
|
5130
|
-
0% 100%;
|
|
5131
|
-
background-position:
|
|
5132
|
-
-0.5em 0%,
|
|
5133
|
-
-0.25em 0%;
|
|
5631
|
+
background-image: linear-gradient(
|
|
5632
|
+
90deg,
|
|
5633
|
+
var(--lyplus-text-primary, #fff) 0%,
|
|
5634
|
+
var(--lyplus-text-primary, #fff)
|
|
5635
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5636
|
+
#0000 100%
|
|
5637
|
+
);
|
|
5638
|
+
background-size: 0% 100%;
|
|
5639
|
+
background-position: left;
|
|
5134
5640
|
transition:
|
|
5135
5641
|
transform 0.7s ease,
|
|
5136
5642
|
color 0.18s;
|
|
5137
5643
|
}
|
|
5138
5644
|
|
|
5139
5645
|
.lyrics-line.active .lyrics-syllable span.char.highlight {
|
|
5140
|
-
background-image:
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
background-image:
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
var(--
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
var(--lyplus-text-primary, #fff) 100%,
|
|
5167
|
-
#0000 100%
|
|
5168
|
-
);
|
|
5169
|
-
background-size:
|
|
5170
|
-
0.75em 100%,
|
|
5171
|
-
0% 100%;
|
|
5172
|
-
background-position:
|
|
5173
|
-
-0.85em 0%,
|
|
5174
|
-
-0.25em 0%;
|
|
5646
|
+
background-image: linear-gradient(
|
|
5647
|
+
-90deg,
|
|
5648
|
+
var(--lyplus-text-primary, #fff) 0%,
|
|
5649
|
+
var(--lyplus-text-primary, #fff)
|
|
5650
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5651
|
+
#0000 100%
|
|
5652
|
+
);
|
|
5653
|
+
background-size: 0% 100%;
|
|
5654
|
+
background-position: right 0%;
|
|
5655
|
+
}
|
|
5656
|
+
|
|
5657
|
+
.lyrics-line.active .lyrics-syllable span.char.pre-wipe-lead {
|
|
5658
|
+
animation-name: pre-wipe-word-char;
|
|
5659
|
+
animation-duration: var(--pre-wipe-duration);
|
|
5660
|
+
animation-delay: var(--pre-wipe-delay);
|
|
5661
|
+
animation-timing-function: linear;
|
|
5662
|
+
animation-fill-mode: forwards;
|
|
5663
|
+
background-image: linear-gradient(
|
|
5664
|
+
90deg,
|
|
5665
|
+
var(--lyplus-text-primary, #fff) 0%,
|
|
5666
|
+
var(--lyplus-text-primary, #fff)
|
|
5667
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5668
|
+
#0000 100%
|
|
5669
|
+
);
|
|
5670
|
+
background-size: 0% 100%;
|
|
5671
|
+
background-position: var(--char-wipe-position, left) 0%;
|
|
5175
5672
|
}
|
|
5176
5673
|
|
|
5177
5674
|
/* ==========================================================================
|
|
@@ -5248,13 +5745,7 @@ AmLyrics$1.styles = i$3 `
|
|
|
5248
5745
|
color: var(--lyplus-text-primary) !important;
|
|
5249
5746
|
}
|
|
5250
5747
|
|
|
5251
|
-
.lyrics-line.
|
|
5252
|
-
animation: fade-in-line 0.14s ease-out forwards !important;
|
|
5253
|
-
color: var(--lyplus-text-primary) !important;
|
|
5254
|
-
}
|
|
5255
|
-
|
|
5256
|
-
.lyrics-line.active .lyrics-syllable.line-synced span.char,
|
|
5257
|
-
.lyrics-line.pre-active .lyrics-syllable.line-synced span.char {
|
|
5748
|
+
.lyrics-line.active .lyrics-syllable.line-synced span.char {
|
|
5258
5749
|
background-image: none !important;
|
|
5259
5750
|
background-color: var(--lyplus-text-primary) !important;
|
|
5260
5751
|
transition: background-color 120ms ease-out !important;
|
|
@@ -5427,6 +5918,49 @@ AmLyrics$1.styles = i$3 `
|
|
|
5427
5918
|
gap: 4px;
|
|
5428
5919
|
}
|
|
5429
5920
|
|
|
5921
|
+
.source-switch-btn {
|
|
5922
|
+
position: relative;
|
|
5923
|
+
display: inline-flex;
|
|
5924
|
+
align-items: center;
|
|
5925
|
+
padding: 2px 8px;
|
|
5926
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
5927
|
+
min-height: 28px;
|
|
5928
|
+
background: transparent;
|
|
5929
|
+
border-radius: 6px;
|
|
5930
|
+
color: #aaa;
|
|
5931
|
+
cursor: pointer;
|
|
5932
|
+
font-family: inherit;
|
|
5933
|
+
font-size: 11px;
|
|
5934
|
+
transition:
|
|
5935
|
+
color 0.2s ease,
|
|
5936
|
+
border-color 0.2s ease,
|
|
5937
|
+
background-color 0.2s ease,
|
|
5938
|
+
transform 0.12s ease;
|
|
5939
|
+
}
|
|
5940
|
+
|
|
5941
|
+
.source-switch-btn::before {
|
|
5942
|
+
content: '';
|
|
5943
|
+
position: absolute;
|
|
5944
|
+
inset: -6px;
|
|
5945
|
+
}
|
|
5946
|
+
|
|
5947
|
+
.source-switch-btn:active:not(:disabled) {
|
|
5948
|
+
transform: scale(0.96);
|
|
5949
|
+
}
|
|
5950
|
+
|
|
5951
|
+
.source-switch-btn:disabled {
|
|
5952
|
+
cursor: default;
|
|
5953
|
+
opacity: 0.7;
|
|
5954
|
+
}
|
|
5955
|
+
|
|
5956
|
+
.source-switch-svg {
|
|
5957
|
+
margin-right: 4px;
|
|
5958
|
+
}
|
|
5959
|
+
|
|
5960
|
+
.source-switch-svg.is-loading {
|
|
5961
|
+
animation: source-switch-spin 1s linear infinite;
|
|
5962
|
+
}
|
|
5963
|
+
|
|
5430
5964
|
.control-button {
|
|
5431
5965
|
background: transparent;
|
|
5432
5966
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
@@ -5435,7 +5969,10 @@ AmLyrics$1.styles = i$3 `
|
|
|
5435
5969
|
font-size: 0.8em;
|
|
5436
5970
|
color: rgba(255, 255, 255, 0.6);
|
|
5437
5971
|
cursor: pointer;
|
|
5438
|
-
transition:
|
|
5972
|
+
transition:
|
|
5973
|
+
color 0.2s,
|
|
5974
|
+
border-color 0.2s,
|
|
5975
|
+
background-color 0.2s;
|
|
5439
5976
|
font-weight: normal;
|
|
5440
5977
|
}
|
|
5441
5978
|
|
|
@@ -5573,137 +6110,125 @@ AmLyrics$1.styles = i$3 `
|
|
|
5573
6110
|
KEYFRAME ANIMATIONS
|
|
5574
6111
|
========================================================================== */
|
|
5575
6112
|
|
|
6113
|
+
@keyframes source-switch-spin {
|
|
6114
|
+
to {
|
|
6115
|
+
transform: rotate(360deg);
|
|
6116
|
+
}
|
|
6117
|
+
}
|
|
6118
|
+
|
|
5576
6119
|
/* Wipe animation for syllables */
|
|
5577
6120
|
@keyframes wipe {
|
|
5578
6121
|
from {
|
|
5579
|
-
background-size:
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
6122
|
+
background-size: 0% 100%;
|
|
6123
|
+
background-position: left;
|
|
6124
|
+
}
|
|
6125
|
+
to {
|
|
6126
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6127
|
+
background-position: left;
|
|
6128
|
+
}
|
|
6129
|
+
}
|
|
6130
|
+
|
|
6131
|
+
@keyframes wipe-from-pre {
|
|
6132
|
+
from {
|
|
6133
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6134
|
+
background-position: left;
|
|
5585
6135
|
}
|
|
5586
6136
|
to {
|
|
5587
|
-
background-size:
|
|
5588
|
-
|
|
5589
|
-
100% 100%;
|
|
5590
|
-
background-position:
|
|
5591
|
-
calc(100% + 0.375em) 0%,
|
|
5592
|
-
left;
|
|
6137
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6138
|
+
background-position: left;
|
|
5593
6139
|
}
|
|
5594
6140
|
}
|
|
5595
6141
|
|
|
5596
6142
|
@keyframes start-wipe {
|
|
5597
6143
|
0% {
|
|
5598
|
-
background-size:
|
|
5599
|
-
|
|
5600
|
-
0% 100%;
|
|
5601
|
-
background-position:
|
|
5602
|
-
-0.75em 0%,
|
|
5603
|
-
-0.375em 0%;
|
|
6144
|
+
background-size: 0% 100%;
|
|
6145
|
+
background-position: left;
|
|
5604
6146
|
}
|
|
5605
6147
|
100% {
|
|
5606
|
-
background-size:
|
|
5607
|
-
|
|
5608
|
-
100% 100%;
|
|
5609
|
-
background-position:
|
|
5610
|
-
calc(100% + 0.375em) 0%,
|
|
5611
|
-
left;
|
|
6148
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6149
|
+
background-position: left;
|
|
5612
6150
|
}
|
|
5613
6151
|
}
|
|
5614
6152
|
|
|
5615
6153
|
@keyframes wipe-rtl {
|
|
5616
6154
|
from {
|
|
5617
|
-
background-size:
|
|
5618
|
-
|
|
5619
|
-
0% 100%;
|
|
5620
|
-
background-position:
|
|
5621
|
-
calc(100% + 0.375em) 0%,
|
|
5622
|
-
calc(100% + 0.36em) 0%;
|
|
6155
|
+
background-size: 0% 100%;
|
|
6156
|
+
background-position: right 0%;
|
|
5623
6157
|
}
|
|
5624
6158
|
to {
|
|
5625
|
-
background-size:
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
6159
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6160
|
+
background-position: right 0%;
|
|
6161
|
+
}
|
|
6162
|
+
}
|
|
6163
|
+
|
|
6164
|
+
@keyframes wipe-from-pre-rtl {
|
|
6165
|
+
from {
|
|
6166
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6167
|
+
background-position: right 0%;
|
|
6168
|
+
}
|
|
6169
|
+
to {
|
|
6170
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6171
|
+
background-position: right 0%;
|
|
5631
6172
|
}
|
|
5632
6173
|
}
|
|
5633
6174
|
|
|
5634
6175
|
@keyframes start-wipe-rtl {
|
|
5635
6176
|
0% {
|
|
5636
|
-
background-size:
|
|
5637
|
-
|
|
5638
|
-
0% 100%;
|
|
5639
|
-
background-position:
|
|
5640
|
-
calc(100% + 0.75em) 0%,
|
|
5641
|
-
calc(100% + 0.5em) 0%;
|
|
6177
|
+
background-size: 0% 100%;
|
|
6178
|
+
background-position: right 0%;
|
|
5642
6179
|
}
|
|
5643
6180
|
100% {
|
|
5644
|
-
background-size:
|
|
5645
|
-
|
|
5646
|
-
100% 100%;
|
|
5647
|
-
background-position:
|
|
5648
|
-
-0.75em 0%,
|
|
5649
|
-
right 0%;
|
|
6181
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6182
|
+
background-position: right 0%;
|
|
5650
6183
|
}
|
|
5651
6184
|
}
|
|
5652
6185
|
|
|
5653
6186
|
@keyframes pre-wipe-universal {
|
|
5654
6187
|
from {
|
|
5655
|
-
background-size:
|
|
5656
|
-
|
|
5657
|
-
0% 100%;
|
|
5658
|
-
background-position:
|
|
5659
|
-
-0.75em 0%,
|
|
5660
|
-
left;
|
|
6188
|
+
background-size: 0% 100%;
|
|
6189
|
+
background-position: left;
|
|
5661
6190
|
}
|
|
5662
6191
|
to {
|
|
5663
|
-
background-size:
|
|
5664
|
-
|
|
5665
|
-
0% 100%;
|
|
5666
|
-
background-position:
|
|
5667
|
-
-0.375em 0%,
|
|
5668
|
-
left;
|
|
6192
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6193
|
+
background-position: left;
|
|
5669
6194
|
}
|
|
5670
6195
|
}
|
|
5671
6196
|
|
|
5672
6197
|
@keyframes pre-wipe-universal-rtl {
|
|
5673
6198
|
from {
|
|
5674
|
-
background-size:
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
6199
|
+
background-size: 0% 100%;
|
|
6200
|
+
background-position: right 0%;
|
|
6201
|
+
}
|
|
6202
|
+
to {
|
|
6203
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6204
|
+
background-position: right 0%;
|
|
6205
|
+
}
|
|
6206
|
+
}
|
|
6207
|
+
|
|
6208
|
+
@keyframes pre-wipe-word-char {
|
|
6209
|
+
from {
|
|
6210
|
+
background-size: 0px 100%;
|
|
6211
|
+
background-position: var(--char-wipe-position, left) 0%;
|
|
5680
6212
|
}
|
|
5681
6213
|
to {
|
|
5682
|
-
background-size:
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
background-position:
|
|
5686
|
-
calc(100% + 0.375em) 0%,
|
|
5687
|
-
right 0%;
|
|
6214
|
+
background-size: var(--pre-wipe-word-size, var(--wipe-gradient-width))
|
|
6215
|
+
100%;
|
|
6216
|
+
background-position: var(--char-wipe-position, left) 0%;
|
|
5688
6217
|
}
|
|
5689
6218
|
}
|
|
5690
6219
|
|
|
5691
|
-
@keyframes
|
|
6220
|
+
@keyframes wipe-word-from-pre {
|
|
5692
6221
|
from {
|
|
5693
|
-
background-size:
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
background-position:
|
|
5697
|
-
-0.75em 0%,
|
|
5698
|
-
left;
|
|
6222
|
+
background-size: var(--pre-wipe-word-size, var(--wipe-gradient-width))
|
|
6223
|
+
100%;
|
|
6224
|
+
background-position: var(--char-wipe-position, left) 0%;
|
|
5699
6225
|
}
|
|
5700
6226
|
to {
|
|
5701
|
-
background-size:
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
left;
|
|
6227
|
+
background-size: calc(
|
|
6228
|
+
var(--word-wipe-width, 100%) + var(--wipe-gradient-width, 0.75em)
|
|
6229
|
+
)
|
|
6230
|
+
100%;
|
|
6231
|
+
background-position: var(--char-wipe-position, left) 0%;
|
|
5707
6232
|
}
|
|
5708
6233
|
}
|
|
5709
6234
|
|
|
@@ -5797,6 +6322,15 @@ AmLyrics$1.styles = i$3 `
|
|
|
5797
6322
|
}
|
|
5798
6323
|
}
|
|
5799
6324
|
|
|
6325
|
+
@keyframes drag-char {
|
|
6326
|
+
0% {
|
|
6327
|
+
transform: translate3d(0, 0, 0);
|
|
6328
|
+
}
|
|
6329
|
+
100% {
|
|
6330
|
+
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
6331
|
+
}
|
|
6332
|
+
}
|
|
6333
|
+
|
|
5800
6334
|
@keyframes grow-static {
|
|
5801
6335
|
0%,
|
|
5802
6336
|
100% {
|