@uimaxbai/am-lyrics 1.5.4 → 1.5.6
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 +934 -319
- package/dist/src/am-lyrics.js.map +1 -1
- package/dist/src/react.js +934 -319
- package/dist/src/react.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/AmLyrics.ts +1246 -353
package/dist/src/am-lyrics.js
CHANGED
|
@@ -319,7 +319,7 @@ class GoogleService {
|
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
const VERSION = '1.5.
|
|
322
|
+
const VERSION = '1.5.6';
|
|
323
323
|
const INSTRUMENTAL_THRESHOLD_MS = 7000; // Show dots for gaps >= 7s
|
|
324
324
|
const FETCH_TIMEOUT_MS = 8000; // Timeout for all lyrics fetch requests
|
|
325
325
|
const SEEK_THRESHOLD_MS = 500;
|
|
@@ -328,6 +328,15 @@ const GAP_PULSE_DURATION_MS = 4000;
|
|
|
328
328
|
const GAP_PULSE_CYCLE_MS = GAP_PULSE_DURATION_MS * 2;
|
|
329
329
|
const GAP_EXIT_LEAD_MS = 600;
|
|
330
330
|
const GAP_MIN_SCALE = 0.85;
|
|
331
|
+
const NEXT_WORD_PRE_WIPE_MAX_GAP_MS = 180;
|
|
332
|
+
const NEXT_WORD_PRE_WIPE_MIN_DURATION_MS = 80;
|
|
333
|
+
const NEXT_WORD_PRE_WIPE_MAX_DURATION_MS = 240;
|
|
334
|
+
const BASE_WIPE_GRADIENT_EM = 0.75;
|
|
335
|
+
const LONG_WORD_WIPE_EXTRA_EM = 0.45;
|
|
336
|
+
const LONG_WORD_WIPE_EXTRA_RATIO = 0.35;
|
|
337
|
+
const SHORT_WORD_DRAG_MIN_DURATION_MS = 760;
|
|
338
|
+
const SHORT_WORD_GLOW_MIN_DURATION_MS = 1320;
|
|
339
|
+
const WORD_PRE_WIPE_HANDOFF_LEAD_MS = 100;
|
|
331
340
|
/**
|
|
332
341
|
* Fetch with an automatic timeout via AbortSignal.
|
|
333
342
|
* Rejects if the request takes longer than `timeoutMs`.
|
|
@@ -389,6 +398,7 @@ class AmLyrics extends i {
|
|
|
389
398
|
this.activeLineIds = new Set();
|
|
390
399
|
this.currentPrimaryActiveLine = null;
|
|
391
400
|
this.lastPrimaryActiveLine = null;
|
|
401
|
+
this.backgroundExpandedLine = null;
|
|
392
402
|
// Scroll animation state
|
|
393
403
|
this.scrollAnimationState = null;
|
|
394
404
|
this.currentScrollOffset = 0;
|
|
@@ -486,11 +496,12 @@ class AmLyrics extends i {
|
|
|
486
496
|
this.preActiveLineElements = [];
|
|
487
497
|
this.positionedLineElements = [];
|
|
488
498
|
this.activeGapLineElements = [];
|
|
499
|
+
this.clearBackgroundExpandedLine();
|
|
489
500
|
// Stop all running animations and clear highlights immediately
|
|
490
501
|
if (this.lyricsContainer) {
|
|
491
|
-
const activeLines = this.lyricsContainer.querySelectorAll('.lyrics-line.active, .lyrics-line.pre-active, .lyrics-line.bg-expanded');
|
|
502
|
+
const activeLines = this.lyricsContainer.querySelectorAll('.lyrics-line.active, .lyrics-line.pre-active, .lyrics-line.bg-expanded, .lyrics-line.scroll-exiting');
|
|
492
503
|
activeLines.forEach(line => {
|
|
493
|
-
line.classList.remove('active', 'pre-active', 'bg-expanded');
|
|
504
|
+
line.classList.remove('active', 'pre-active', 'bg-expanded', 'scroll-exiting');
|
|
494
505
|
AmLyrics.resetSyllables(line);
|
|
495
506
|
});
|
|
496
507
|
const activeGaps = this.lyricsContainer.querySelectorAll('.lyrics-gap.active, .lyrics-gap.gap-exiting');
|
|
@@ -518,7 +529,7 @@ class AmLyrics extends i {
|
|
|
518
529
|
switchBtn.disabled = this.isFetchingAlternatives;
|
|
519
530
|
}
|
|
520
531
|
if (svgEl) {
|
|
521
|
-
svgEl.
|
|
532
|
+
svgEl.classList.toggle('is-loading', this.isFetchingAlternatives);
|
|
522
533
|
}
|
|
523
534
|
if (labelEl) {
|
|
524
535
|
labelEl.textContent = this.isFetchingAlternatives
|
|
@@ -691,6 +702,7 @@ class AmLyrics extends i {
|
|
|
691
702
|
this.preActiveLineElements = [];
|
|
692
703
|
this.positionedLineElements = [];
|
|
693
704
|
this.activeGapLineElements = [];
|
|
705
|
+
this.clearBackgroundExpandedLine();
|
|
694
706
|
if (this.lyricsContainer) {
|
|
695
707
|
this.isProgrammaticScroll = true;
|
|
696
708
|
this.lyricsContainer.scrollTop = 0;
|
|
@@ -758,14 +770,23 @@ class AmLyrics extends i {
|
|
|
758
770
|
return 21;
|
|
759
771
|
return 30;
|
|
760
772
|
}
|
|
773
|
+
static getDisplaySourceLabel(sourceLabel) {
|
|
774
|
+
return sourceLabel.toLowerCase().includes('lyricsplus')
|
|
775
|
+
? 'QQ'
|
|
776
|
+
: sourceLabel;
|
|
777
|
+
}
|
|
778
|
+
static getSourceKey(sourceLabel) {
|
|
779
|
+
const lower = (sourceLabel || '').trim().toLowerCase();
|
|
780
|
+
if (!lower)
|
|
781
|
+
return '';
|
|
782
|
+
if (lower.includes('lyricsplus') || lower === 'qq')
|
|
783
|
+
return 'qq';
|
|
784
|
+
return lower.replace(/\s+/g, ' ');
|
|
785
|
+
}
|
|
761
786
|
static mergeAndSortSources(collectedSources) {
|
|
762
787
|
const uniqueSourcesMap = new Map();
|
|
763
788
|
for (const source of collectedSources) {
|
|
764
|
-
const normalizedSource = source.source
|
|
765
|
-
.toLowerCase()
|
|
766
|
-
.includes('lyricsplus')
|
|
767
|
-
? 'QQ'
|
|
768
|
-
: source.source;
|
|
789
|
+
const normalizedSource = AmLyrics.getDisplaySourceLabel(source.source);
|
|
769
790
|
if (!uniqueSourcesMap.has(normalizedSource)) {
|
|
770
791
|
uniqueSourcesMap.set(normalizedSource, {
|
|
771
792
|
...source,
|
|
@@ -776,9 +797,43 @@ class AmLyrics extends i {
|
|
|
776
797
|
return Array.from(uniqueSourcesMap.values()).sort((a, b) => AmLyrics.getRankForCollected(a.source, a.lines) -
|
|
777
798
|
AmLyrics.getRankForCollected(b.source, b.lines));
|
|
778
799
|
}
|
|
800
|
+
findCurrentSourceIndex(sources = this.availableSources, sourceLabel = this.lyricsSource, lines = this.lyrics) {
|
|
801
|
+
const identityIndex = sources.findIndex(source => source.lines === lines);
|
|
802
|
+
if (identityIndex !== -1)
|
|
803
|
+
return identityIndex;
|
|
804
|
+
const sourceKey = AmLyrics.getSourceKey(sourceLabel);
|
|
805
|
+
if (!sourceKey)
|
|
806
|
+
return -1;
|
|
807
|
+
return sources.findIndex(source => AmLyrics.getSourceKey(source.source) === sourceKey);
|
|
808
|
+
}
|
|
809
|
+
static getNextSourceIndex(sources, currentIndex, currentSourceLabel, currentLines) {
|
|
810
|
+
if (sources.length <= 1)
|
|
811
|
+
return -1;
|
|
812
|
+
if (currentIndex !== -1) {
|
|
813
|
+
return (currentIndex + 1) % sources.length;
|
|
814
|
+
}
|
|
815
|
+
const currentKey = AmLyrics.getSourceKey(currentSourceLabel);
|
|
816
|
+
const fallbackIndex = sources.findIndex(source => source.lines !== currentLines &&
|
|
817
|
+
AmLyrics.getSourceKey(source.source) !== currentKey);
|
|
818
|
+
return fallbackIndex === -1 ? 0 : fallbackIndex;
|
|
819
|
+
}
|
|
820
|
+
async applySourceAtIndex(index) {
|
|
821
|
+
const sourceResult = this.availableSources[index];
|
|
822
|
+
if (!sourceResult)
|
|
823
|
+
return;
|
|
824
|
+
this.currentSourceIndex = index;
|
|
825
|
+
this.lyrics = sourceResult.lines;
|
|
826
|
+
this.lyricsSource = sourceResult.source;
|
|
827
|
+
if (sourceResult.songwriters) {
|
|
828
|
+
this.songwriters = sourceResult.songwriters;
|
|
829
|
+
}
|
|
830
|
+
await this.onLyricsLoaded();
|
|
831
|
+
}
|
|
779
832
|
async switchSource() {
|
|
780
833
|
if (this.isFetchingAlternatives)
|
|
781
834
|
return;
|
|
835
|
+
const currentSourceLabel = this.lyricsSource;
|
|
836
|
+
const currentLines = this.lyrics;
|
|
782
837
|
if (!this.hasFetchedAllProviders) {
|
|
783
838
|
this.isFetchingAlternatives = true;
|
|
784
839
|
this._updateFooter();
|
|
@@ -821,10 +876,9 @@ class AmLyrics extends i {
|
|
|
821
876
|
...this.availableSources,
|
|
822
877
|
...newSources,
|
|
823
878
|
]);
|
|
824
|
-
// Re-sync current index since sorting
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
this.currentSourceIndex = 0;
|
|
879
|
+
// Re-sync current index since sorting or label normalization can
|
|
880
|
+
// shift the currently displayed source underneath the old index.
|
|
881
|
+
this.currentSourceIndex = this.findCurrentSourceIndex(this.availableSources, currentSourceLabel, currentLines);
|
|
828
882
|
}
|
|
829
883
|
}
|
|
830
884
|
}
|
|
@@ -835,15 +889,11 @@ class AmLyrics extends i {
|
|
|
835
889
|
}
|
|
836
890
|
}
|
|
837
891
|
if (this.availableSources.length > 1) {
|
|
838
|
-
this.
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
this.lyricsSource = sourceResult.source;
|
|
843
|
-
if (sourceResult.songwriters) {
|
|
844
|
-
this.songwriters = sourceResult.songwriters;
|
|
892
|
+
const currentIndex = this.findCurrentSourceIndex(this.availableSources, currentSourceLabel, currentLines);
|
|
893
|
+
const nextIndex = AmLyrics.getNextSourceIndex(this.availableSources, currentIndex, currentSourceLabel, currentLines);
|
|
894
|
+
if (nextIndex !== -1) {
|
|
895
|
+
await this.applySourceAtIndex(nextIndex);
|
|
845
896
|
}
|
|
846
|
-
await this.onLyricsLoaded();
|
|
847
897
|
}
|
|
848
898
|
}
|
|
849
899
|
async resolveSongMetadata() {
|
|
@@ -1895,13 +1945,15 @@ class AmLyrics extends i {
|
|
|
1895
1945
|
if (!newActiveLines.includes(lineIndex)) {
|
|
1896
1946
|
const lineElement = this._getLineElement(lineIndex);
|
|
1897
1947
|
if (lineElement) {
|
|
1898
|
-
if (isSeek ||
|
|
1948
|
+
if (isSeek ||
|
|
1949
|
+
this.isUserScrolling ||
|
|
1950
|
+
AmLyrics.isLineSyncedLine(this.lyrics?.[lineIndex])) {
|
|
1899
1951
|
AmLyrics.unfinishSyllables(lineElement);
|
|
1900
1952
|
}
|
|
1901
1953
|
else {
|
|
1902
1954
|
AmLyrics.finishSyllablesUpToTime(lineElement, newTime);
|
|
1903
1955
|
}
|
|
1904
|
-
lineElement.classList.remove('active', 'bg-expanded');
|
|
1956
|
+
lineElement.classList.remove('active', 'bg-expanded', 'scroll-exiting');
|
|
1905
1957
|
if (lineElement.classList.contains('pre-active')) {
|
|
1906
1958
|
lineElement.classList.remove('pre-active');
|
|
1907
1959
|
}
|
|
@@ -1911,13 +1963,14 @@ class AmLyrics extends i {
|
|
|
1911
1963
|
}
|
|
1912
1964
|
}
|
|
1913
1965
|
}
|
|
1914
|
-
// Add 'active'
|
|
1966
|
+
// Add 'active' to newly active lines. Background expansion is driven
|
|
1967
|
+
// separately by the current scroll target.
|
|
1915
1968
|
for (const lineIndex of newActiveLines) {
|
|
1916
1969
|
if (!oldActiveLines.includes(lineIndex)) {
|
|
1917
1970
|
const lineElement = this._getLineElement(lineIndex);
|
|
1918
1971
|
if (lineElement) {
|
|
1919
|
-
lineElement.classList.add('active'
|
|
1920
|
-
lineElement.classList.remove('pre-active');
|
|
1972
|
+
lineElement.classList.add('active');
|
|
1973
|
+
lineElement.classList.remove('pre-active', 'scroll-exiting');
|
|
1921
1974
|
const preIdx = this.preActiveLineElements.indexOf(lineElement);
|
|
1922
1975
|
if (preIdx !== -1)
|
|
1923
1976
|
this.preActiveLineElements.splice(preIdx, 1);
|
|
@@ -2122,7 +2175,11 @@ class AmLyrics extends i {
|
|
|
2122
2175
|
}
|
|
2123
2176
|
}
|
|
2124
2177
|
updated(changedProperties) {
|
|
2125
|
-
|
|
2178
|
+
const lyricsDomBecameRenderable = changedProperties.has('lyrics') ||
|
|
2179
|
+
(changedProperties.has('isLoading') &&
|
|
2180
|
+
!this.isLoading &&
|
|
2181
|
+
Boolean(this.lyrics));
|
|
2182
|
+
if (lyricsDomBecameRenderable) {
|
|
2126
2183
|
this._invalidateCaches();
|
|
2127
2184
|
this._ensureLineDataCache();
|
|
2128
2185
|
this._updateCachedIsUnsynced();
|
|
@@ -2136,8 +2193,12 @@ class AmLyrics extends i {
|
|
|
2136
2193
|
for (const lineIndex of activeLines) {
|
|
2137
2194
|
const lineEl = this._getLineElement(lineIndex);
|
|
2138
2195
|
if (lineEl)
|
|
2139
|
-
lineEl.classList.add('active'
|
|
2196
|
+
lineEl.classList.add('active');
|
|
2140
2197
|
}
|
|
2198
|
+
const primaryActiveLine = this.getPrimaryActiveLineIndex(activeLines);
|
|
2199
|
+
this.setBackgroundExpandedLine(primaryActiveLine !== null
|
|
2200
|
+
? this._getLineElement(primaryActiveLine)
|
|
2201
|
+
: null);
|
|
2141
2202
|
// Trigger a faux time-change so that updateSyllablesForLine fires
|
|
2142
2203
|
// to setup inline syllable CSS wipe animations for whatever the current time is
|
|
2143
2204
|
this._onTimeChanged(0, this.currentTime);
|
|
@@ -2176,6 +2237,7 @@ class AmLyrics extends i {
|
|
|
2176
2237
|
this.preActiveLineElements = [];
|
|
2177
2238
|
this.positionedLineElements = [];
|
|
2178
2239
|
this.activeGapLineElements = [];
|
|
2240
|
+
this.clearBackgroundExpandedLine();
|
|
2179
2241
|
this.setUserScrolling(false);
|
|
2180
2242
|
// Cancel any running animations
|
|
2181
2243
|
if (this.animationFrameId) {
|
|
@@ -2230,6 +2292,7 @@ class AmLyrics extends i {
|
|
|
2230
2292
|
// Don't override it with a scroll back to the last lyric.
|
|
2231
2293
|
const footer = this.lyricsContainer.querySelector('.lyrics-footer');
|
|
2232
2294
|
if (footer?.classList.contains('active')) {
|
|
2295
|
+
this.setBackgroundExpandedLine(null);
|
|
2233
2296
|
return;
|
|
2234
2297
|
}
|
|
2235
2298
|
// 1. Compute scroll lookahead based on gap to next line (YouLyPlus style)
|
|
@@ -2270,17 +2333,23 @@ class AmLyrics extends i {
|
|
|
2270
2333
|
targetElement = this._getLineElement(targetLineIdx);
|
|
2271
2334
|
}
|
|
2272
2335
|
}
|
|
2273
|
-
if (!targetElement)
|
|
2336
|
+
if (!targetElement) {
|
|
2337
|
+
this.setBackgroundExpandedLine(null);
|
|
2274
2338
|
return;
|
|
2275
|
-
|
|
2276
|
-
|
|
2339
|
+
}
|
|
2340
|
+
const scrollDuration = scrollLookAheadMs;
|
|
2341
|
+
if (targetElement !== this.currentPrimaryActiveLine || forceScroll) {
|
|
2342
|
+
targetElement.style.setProperty('--scroll-duration', `${scrollDuration}ms`);
|
|
2343
|
+
}
|
|
2344
|
+
this.setBackgroundExpandedLine(targetElement);
|
|
2345
|
+
// Unblur the upcoming target line early while the separate bg-expanded
|
|
2346
|
+
// class starts background vocal height/opacity in sync with scroll.
|
|
2277
2347
|
if (!targetElement.classList.contains('active')) {
|
|
2278
2348
|
targetElement.classList.add('pre-active');
|
|
2279
2349
|
if (!this.preActiveLineElements.includes(targetElement)) {
|
|
2280
2350
|
this.preActiveLineElements.push(targetElement);
|
|
2281
2351
|
}
|
|
2282
2352
|
}
|
|
2283
|
-
const scrollDuration = scrollLookAheadMs;
|
|
2284
2353
|
this.focusLine(targetElement, forceScroll, scrollDuration);
|
|
2285
2354
|
}
|
|
2286
2355
|
_getTextWidth(text, font) {
|
|
@@ -2353,6 +2422,7 @@ class AmLyrics extends i {
|
|
|
2353
2422
|
this.preActiveLineElements = [];
|
|
2354
2423
|
this.positionedLineElements = [];
|
|
2355
2424
|
this.activeGapLineElements = [];
|
|
2425
|
+
this.clearBackgroundExpandedLine();
|
|
2356
2426
|
this.visibilityObserver?.disconnect();
|
|
2357
2427
|
this.visibilityObserver = undefined;
|
|
2358
2428
|
}
|
|
@@ -2388,6 +2458,7 @@ class AmLyrics extends i {
|
|
|
2388
2458
|
const groupGrowable = new Array(wordGroups.length).fill(false);
|
|
2389
2459
|
const groupGlowing = new Array(wordGroups.length).fill(false);
|
|
2390
2460
|
const groupCharRise = new Array(wordGroups.length).fill(false);
|
|
2461
|
+
const groupCharDrag = new Array(wordGroups.length).fill(false);
|
|
2391
2462
|
const vwFullText = new Array(wordGroups.length).fill('');
|
|
2392
2463
|
const vwFullDuration = new Array(wordGroups.length).fill(0);
|
|
2393
2464
|
const vwCharOffset = new Array(wordGroups.length).fill(0);
|
|
@@ -2423,28 +2494,42 @@ class AmLyrics extends i {
|
|
|
2423
2494
|
const isLineSynced = line.isWordSynced === false || line.text.some(s => s.lineSynced);
|
|
2424
2495
|
let isGrowableVW = canAnimateByChar && wordLen > 0 && wordLen <= 7;
|
|
2425
2496
|
if (isGrowableVW) {
|
|
2426
|
-
if (wordLen
|
|
2497
|
+
if (wordLen <= 1) {
|
|
2427
2498
|
isGrowableVW =
|
|
2428
2499
|
combinedDuration >= 1050 && combinedDuration >= wordLen * 525;
|
|
2429
2500
|
}
|
|
2501
|
+
else if (wordLen <= 3) {
|
|
2502
|
+
isGrowableVW =
|
|
2503
|
+
combinedDuration >=
|
|
2504
|
+
SHORT_WORD_GLOW_MIN_DURATION_MS + (wordLen - 2) * 140;
|
|
2505
|
+
}
|
|
2430
2506
|
else {
|
|
2431
2507
|
isGrowableVW =
|
|
2432
2508
|
combinedDuration >= 850 && combinedDuration >= wordLen * 190;
|
|
2433
2509
|
}
|
|
2434
2510
|
}
|
|
2435
2511
|
const hasCharRiseDuration = combinedDuration >= Math.max(700, wordLen * 85);
|
|
2512
|
+
const hasTinyWordDragDuration = wordLen >= 2 &&
|
|
2513
|
+
wordLen <= 3 &&
|
|
2514
|
+
combinedDuration >=
|
|
2515
|
+
Math.max(SHORT_WORD_DRAG_MIN_DURATION_MS, wordLen * 150);
|
|
2436
2516
|
const hasLongShortWordDuration = wordLen >= 4 && combinedDuration >= Math.max(1300, wordLen * 260);
|
|
2437
2517
|
const isCharRiseVW = canAnimateByChar &&
|
|
2438
2518
|
!isLineSynced &&
|
|
2439
2519
|
!isGrowableVW &&
|
|
2440
2520
|
((wordLen >= 8 && hasCharRiseDuration) ||
|
|
2441
2521
|
(wordLen < 8 && hasLongShortWordDuration));
|
|
2522
|
+
const isCharDragVW = canAnimateByChar &&
|
|
2523
|
+
!isLineSynced &&
|
|
2524
|
+
!isGrowableVW &&
|
|
2525
|
+
hasTinyWordDragDuration;
|
|
2442
2526
|
const isGlowingVW = isGrowableVW && !isLineSynced;
|
|
2443
2527
|
let charOff = 0;
|
|
2444
2528
|
for (let gi = vwStart; gi <= vwEnd; gi += 1) {
|
|
2445
2529
|
groupGrowable[gi] = isGrowableVW;
|
|
2446
2530
|
groupGlowing[gi] = isGlowingVW;
|
|
2447
2531
|
groupCharRise[gi] = isCharRiseVW;
|
|
2532
|
+
groupCharDrag[gi] = isCharDragVW;
|
|
2448
2533
|
vwFullText[gi] = combinedText;
|
|
2449
2534
|
vwFullDuration[gi] = combinedDuration;
|
|
2450
2535
|
vwCharOffset[gi] = charOff;
|
|
@@ -2460,6 +2545,7 @@ class AmLyrics extends i {
|
|
|
2460
2545
|
groupGrowable,
|
|
2461
2546
|
groupGlowing,
|
|
2462
2547
|
groupCharRise,
|
|
2548
|
+
groupCharDrag,
|
|
2463
2549
|
vwFullText,
|
|
2464
2550
|
vwFullDuration,
|
|
2465
2551
|
vwCharOffset,
|
|
@@ -2479,48 +2565,106 @@ class AmLyrics extends i {
|
|
|
2479
2565
|
return;
|
|
2480
2566
|
const computedStyle = getComputedStyle(referenceSyllable);
|
|
2481
2567
|
const { font } = computedStyle; // Full font string
|
|
2482
|
-
const fontSize = parseFloat(computedStyle.fontSize);
|
|
2483
|
-
const charTimedWords = this.shadowRoot.querySelectorAll('.lyrics-word.growable, .lyrics-word.char-rise');
|
|
2484
|
-
if (
|
|
2568
|
+
const fontSize = Number.parseFloat(computedStyle.fontSize) || 16;
|
|
2569
|
+
const charTimedWords = Array.from(this.shadowRoot.querySelectorAll('.lyrics-word.growable, .lyrics-word.char-rise, .lyrics-word.char-drag'));
|
|
2570
|
+
if (charTimedWords.length === 0)
|
|
2485
2571
|
return;
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2572
|
+
const wordsByVirtualId = new Map();
|
|
2573
|
+
charTimedWords.forEach((wordSpan, index) => {
|
|
2574
|
+
const virtualWordId = wordSpan.dataset.virtualWordId || `word-${index}`;
|
|
2575
|
+
const words = wordsByVirtualId.get(virtualWordId);
|
|
2576
|
+
if (words) {
|
|
2577
|
+
words.push(wordSpan);
|
|
2578
|
+
}
|
|
2579
|
+
else {
|
|
2580
|
+
wordsByVirtualId.set(virtualWordId, [wordSpan]);
|
|
2581
|
+
}
|
|
2582
|
+
});
|
|
2583
|
+
wordsByVirtualId.forEach(wordSpans => {
|
|
2489
2584
|
const syllables = [];
|
|
2490
|
-
|
|
2491
|
-
const
|
|
2492
|
-
|
|
2493
|
-
|
|
2585
|
+
wordSpans.forEach(wordSpan => {
|
|
2586
|
+
const syllableWraps = wordSpan.querySelectorAll('.lyrics-syllable-wrap');
|
|
2587
|
+
syllableWraps.forEach(wrap => {
|
|
2588
|
+
const syl = wrap.querySelector('.lyrics-syllable');
|
|
2589
|
+
if (syl)
|
|
2590
|
+
syllables.push(syl);
|
|
2591
|
+
});
|
|
2494
2592
|
});
|
|
2495
|
-
syllables.
|
|
2496
|
-
const
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
const
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
const
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2593
|
+
const charSpans = syllables.flatMap(syl => {
|
|
2594
|
+
const spans = Array.from(syl.querySelectorAll('.char'));
|
|
2595
|
+
const target = syl;
|
|
2596
|
+
target._cachedCharSpans = spans;
|
|
2597
|
+
return spans;
|
|
2598
|
+
});
|
|
2599
|
+
if (charSpans.length === 0)
|
|
2600
|
+
return;
|
|
2601
|
+
wordSpans.forEach(wordSpan => {
|
|
2602
|
+
const target = wordSpan;
|
|
2603
|
+
target._cachedVirtualWordElements = wordSpans;
|
|
2604
|
+
target._cachedVirtualWordCharSpans = charSpans;
|
|
2605
|
+
});
|
|
2606
|
+
const syllableEntries = syllables.map(syl => {
|
|
2607
|
+
const spans = syl._cachedCharSpans;
|
|
2608
|
+
const charWidths = spans.map(span => this._getTextWidth(span.textContent || '', font));
|
|
2609
|
+
const totalWidth = charWidths.reduce((a, b) => a + b, 0);
|
|
2610
|
+
return {
|
|
2611
|
+
syl,
|
|
2612
|
+
spans,
|
|
2613
|
+
charWidths,
|
|
2614
|
+
totalWidth,
|
|
2615
|
+
start: parseFloat(syl.getAttribute('data-start-time') || ''),
|
|
2616
|
+
end: parseFloat(syl.getAttribute('data-end-time') || ''),
|
|
2617
|
+
};
|
|
2618
|
+
});
|
|
2619
|
+
const totalWordWidth = syllableEntries.reduce((total, entry) => total + entry.totalWidth, 0);
|
|
2620
|
+
if (totalWordWidth <= 0)
|
|
2621
|
+
return;
|
|
2622
|
+
const virtualWordStart = Math.min(...syllableEntries
|
|
2623
|
+
.map(entry => entry.start)
|
|
2624
|
+
.filter(start => Number.isFinite(start)));
|
|
2625
|
+
const virtualWordEnd = Math.max(...syllableEntries
|
|
2626
|
+
.map(entry => entry.end)
|
|
2627
|
+
.filter(end => Number.isFinite(end)));
|
|
2628
|
+
const virtualWordDuration = virtualWordEnd - virtualWordStart;
|
|
2629
|
+
const hasTimedSyllables = Number.isFinite(virtualWordStart) &&
|
|
2630
|
+
Number.isFinite(virtualWordEnd) &&
|
|
2631
|
+
virtualWordDuration > 0;
|
|
2632
|
+
const wordVelocityPxPerMs = hasTimedSyllables
|
|
2633
|
+
? totalWordWidth / virtualWordDuration
|
|
2634
|
+
: 0;
|
|
2635
|
+
const gradientLeadWidthPx = (BASE_WIPE_GRADIENT_EM * Math.max(1, fontSize)) / 2;
|
|
2636
|
+
const measuredPreWipeDuration = wordVelocityPxPerMs > 0
|
|
2637
|
+
? gradientLeadWidthPx / wordVelocityPxPerMs
|
|
2638
|
+
: 100;
|
|
2639
|
+
let cumulativeCharWidth = 0;
|
|
2640
|
+
syllableEntries.forEach(entry => {
|
|
2641
|
+
let cumulativeSyllableWidth = 0;
|
|
2642
|
+
const syllableDuration = entry.end - entry.start;
|
|
2643
|
+
const useSyllableTiming = hasTimedSyllables &&
|
|
2644
|
+
Number.isFinite(entry.start) &&
|
|
2645
|
+
Number.isFinite(entry.end) &&
|
|
2646
|
+
syllableDuration > 0 &&
|
|
2647
|
+
entry.totalWidth > 0;
|
|
2648
|
+
entry.spans.forEach((span, index) => {
|
|
2649
|
+
const charWidth = entry.charWidths[index];
|
|
2650
|
+
let startPercent = cumulativeCharWidth / totalWordWidth;
|
|
2651
|
+
let durationPercent = charWidth / totalWordWidth;
|
|
2652
|
+
if (useSyllableTiming) {
|
|
2653
|
+
const charStartMs = entry.start -
|
|
2654
|
+
virtualWordStart +
|
|
2655
|
+
(cumulativeSyllableWidth / entry.totalWidth) * syllableDuration;
|
|
2656
|
+
const charDurationMs = (charWidth / entry.totalWidth) * syllableDuration;
|
|
2657
|
+
startPercent = AmLyrics.clamp(charStartMs / virtualWordDuration, 0, 1);
|
|
2658
|
+
durationPercent = AmLyrics.clamp(charDurationMs / virtualWordDuration, 0, 1);
|
|
2522
2659
|
}
|
|
2660
|
+
const target = span;
|
|
2661
|
+
target.dataset.wipeStart = startPercent.toFixed(4);
|
|
2662
|
+
target.dataset.wipeDuration = durationPercent.toFixed(4);
|
|
2663
|
+
target.dataset.preWipeDuration = measuredPreWipeDuration.toFixed(2);
|
|
2664
|
+
target.style.setProperty('--word-wipe-width', `${totalWordWidth}px`);
|
|
2665
|
+
target.style.setProperty('--char-wipe-position', `${-cumulativeCharWidth}px`);
|
|
2523
2666
|
cumulativeCharWidth += charWidth;
|
|
2667
|
+
cumulativeSyllableWidth += charWidth;
|
|
2524
2668
|
});
|
|
2525
2669
|
});
|
|
2526
2670
|
});
|
|
@@ -2528,6 +2672,33 @@ class AmLyrics extends i {
|
|
|
2528
2672
|
static arraysEqual(a, b) {
|
|
2529
2673
|
return a.length === b.length && a.every((val, i) => val === b[i]);
|
|
2530
2674
|
}
|
|
2675
|
+
static isLineSyncedLine(line) {
|
|
2676
|
+
if (!line)
|
|
2677
|
+
return false;
|
|
2678
|
+
return line.isWordSynced === false || line.text.some(s => s.lineSynced);
|
|
2679
|
+
}
|
|
2680
|
+
getLineHighlightEndTime(index) {
|
|
2681
|
+
if (!this.lyrics)
|
|
2682
|
+
return 0;
|
|
2683
|
+
const line = this.lyrics[index];
|
|
2684
|
+
if (!line)
|
|
2685
|
+
return 0;
|
|
2686
|
+
const rawEnd = Math.max(line.endtime, line.timestamp);
|
|
2687
|
+
const nextLine = this.lyrics[index + 1];
|
|
2688
|
+
if (!nextLine || nextLine.timestamp <= line.timestamp) {
|
|
2689
|
+
return rawEnd > line.timestamp ? rawEnd + 200 : rawEnd;
|
|
2690
|
+
}
|
|
2691
|
+
if (rawEnd > line.timestamp) {
|
|
2692
|
+
if (nextLine.timestamp < rawEnd) {
|
|
2693
|
+
return rawEnd;
|
|
2694
|
+
}
|
|
2695
|
+
const gapToNext = nextLine.timestamp - rawEnd;
|
|
2696
|
+
if (gapToNext >= INSTRUMENTAL_THRESHOLD_MS) {
|
|
2697
|
+
return rawEnd;
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
return nextLine.timestamp;
|
|
2701
|
+
}
|
|
2531
2702
|
static getLineIndexFromElement(lineElement) {
|
|
2532
2703
|
if (!lineElement)
|
|
2533
2704
|
return null;
|
|
@@ -2558,6 +2729,26 @@ class AmLyrics extends i {
|
|
|
2558
2729
|
}
|
|
2559
2730
|
this.preActiveLineElements = keptLines;
|
|
2560
2731
|
}
|
|
2732
|
+
setBackgroundExpandedLine(lineElement) {
|
|
2733
|
+
const target = lineElement &&
|
|
2734
|
+
!lineElement.classList.contains('lyrics-gap') &&
|
|
2735
|
+
lineElement.querySelector('.background-vocal-container')
|
|
2736
|
+
? lineElement
|
|
2737
|
+
: null;
|
|
2738
|
+
if (this.backgroundExpandedLine === target) {
|
|
2739
|
+
if (target && !target.classList.contains('bg-expanded')) {
|
|
2740
|
+
target.classList.add('bg-expanded');
|
|
2741
|
+
}
|
|
2742
|
+
return;
|
|
2743
|
+
}
|
|
2744
|
+
this.backgroundExpandedLine?.classList.remove('bg-expanded');
|
|
2745
|
+
this.backgroundExpandedLine = target;
|
|
2746
|
+
target?.classList.add('bg-expanded');
|
|
2747
|
+
}
|
|
2748
|
+
clearBackgroundExpandedLine() {
|
|
2749
|
+
this.backgroundExpandedLine?.classList.remove('bg-expanded');
|
|
2750
|
+
this.backgroundExpandedLine = null;
|
|
2751
|
+
}
|
|
2561
2752
|
getPrimaryActiveLineIndex(activeIndices) {
|
|
2562
2753
|
if (activeIndices.length === 0)
|
|
2563
2754
|
return null;
|
|
@@ -2634,7 +2825,12 @@ class AmLyrics extends i {
|
|
|
2634
2825
|
// effectiveEndTimes). Lines stay active until their extended end,
|
|
2635
2826
|
// so we no longer need to remove .active here.
|
|
2636
2827
|
this.lastPrimaryActiveLine = this.currentPrimaryActiveLine;
|
|
2828
|
+
if (this.lastPrimaryActiveLine) {
|
|
2829
|
+
this.lastPrimaryActiveLine.style.setProperty('--scroll-duration', `${scrollDuration ?? SCROLL_ANIMATION_DURATION_MS}ms`);
|
|
2830
|
+
this.lastPrimaryActiveLine.classList.add('scroll-exiting');
|
|
2831
|
+
}
|
|
2637
2832
|
this.currentPrimaryActiveLine = lineElement;
|
|
2833
|
+
this.currentPrimaryActiveLine.classList.remove('scroll-exiting');
|
|
2638
2834
|
const lineIndex = AmLyrics.getLineIndexFromElement(lineElement);
|
|
2639
2835
|
if (lineIndex !== null) {
|
|
2640
2836
|
this.lastActiveIndex = lineIndex;
|
|
@@ -2747,9 +2943,10 @@ class AmLyrics extends i {
|
|
|
2747
2943
|
const activeLines = [];
|
|
2748
2944
|
for (let i = 0; i < this.lyrics.length; i += 1) {
|
|
2749
2945
|
const line = this.lyrics[i];
|
|
2946
|
+
const highlightEndTime = this.getLineHighlightEndTime(i);
|
|
2750
2947
|
if (line.timestamp > time)
|
|
2751
2948
|
break;
|
|
2752
|
-
if (time >= line.timestamp && time <
|
|
2949
|
+
if (time >= line.timestamp && time < highlightEndTime) {
|
|
2753
2950
|
activeLines.push(i);
|
|
2754
2951
|
}
|
|
2755
2952
|
}
|
|
@@ -2946,7 +3143,7 @@ class AmLyrics extends i {
|
|
|
2946
3143
|
allLines.forEach(lineEl => {
|
|
2947
3144
|
AmLyrics.resetSyllables(lineEl);
|
|
2948
3145
|
// Remove scroll-animate class and properties to stop any scroll animations
|
|
2949
|
-
lineEl.classList.remove('scroll-animate');
|
|
3146
|
+
lineEl.classList.remove('scroll-animate', 'scroll-exiting');
|
|
2950
3147
|
lineEl.style.removeProperty('--scroll-delta');
|
|
2951
3148
|
lineEl.style.removeProperty('--lyrics-line-delay');
|
|
2952
3149
|
});
|
|
@@ -2974,6 +3171,7 @@ class AmLyrics extends i {
|
|
|
2974
3171
|
this.lastPrimaryActiveLine = null;
|
|
2975
3172
|
this.activeLineIds.clear();
|
|
2976
3173
|
this.animatingLines = [];
|
|
3174
|
+
this.setBackgroundExpandedLine(null);
|
|
2977
3175
|
// Find the clicked line element and scroll to it with forceScroll (like YouLyPlus)
|
|
2978
3176
|
// Timestamps are already in milliseconds — match the data-start-time attribute directly
|
|
2979
3177
|
const clickedLineElement = this.lyricsContainer?.querySelector(`.lyrics-line[data-start-time="${line.text[0]?.timestamp || 0}"]`);
|
|
@@ -2990,6 +3188,7 @@ class AmLyrics extends i {
|
|
|
2990
3188
|
this.isClickSeeking = false;
|
|
2991
3189
|
}, 800);
|
|
2992
3190
|
this.scrollToActiveLineYouLy(clickedLineElement, true);
|
|
3191
|
+
this.setBackgroundExpandedLine(clickedLineElement);
|
|
2993
3192
|
}
|
|
2994
3193
|
const event = new CustomEvent('line-click', {
|
|
2995
3194
|
detail: {
|
|
@@ -3167,26 +3366,30 @@ class AmLyrics extends i {
|
|
|
3167
3366
|
return;
|
|
3168
3367
|
const duration = Math.min(450, scrollDuration ?? SCROLL_ANIMATION_DURATION_MS);
|
|
3169
3368
|
const delayIncrement = duration * 0.1;
|
|
3369
|
+
const maxStaggerSteps = 4;
|
|
3170
3370
|
const lookAhead = 20;
|
|
3171
3371
|
const len = lineArray.length;
|
|
3172
3372
|
const start = Math.max(0, referenceIndex - lookAhead);
|
|
3173
3373
|
const end = Math.min(len, referenceIndex + lookAhead);
|
|
3174
3374
|
let maxAnimationDuration = 0;
|
|
3175
3375
|
const newAnimatingLines = [];
|
|
3376
|
+
const lineDelays = new Map();
|
|
3176
3377
|
const scrollingDown = delta >= 0;
|
|
3177
3378
|
if (scrollingDown) {
|
|
3178
3379
|
let delayCounter = 0;
|
|
3179
3380
|
for (let i = start; i < end; i += 1) {
|
|
3180
3381
|
const line = lineArray[i];
|
|
3181
|
-
const delay = i >= referenceIndex
|
|
3382
|
+
const delay = i >= referenceIndex
|
|
3383
|
+
? Math.min(delayCounter, maxStaggerSteps) * delayIncrement
|
|
3384
|
+
: 0;
|
|
3182
3385
|
if (i >= referenceIndex && !line.classList.contains('lyrics-gap')) {
|
|
3183
3386
|
delayCounter += 1;
|
|
3184
3387
|
}
|
|
3185
3388
|
line.style.setProperty('--scroll-delta', `${delta}px`);
|
|
3186
3389
|
line.style.setProperty('--lyrics-line-delay', `${delay}ms`);
|
|
3187
|
-
|
|
3390
|
+
lineDelays.set(line, delay);
|
|
3188
3391
|
newAnimatingLines.push(line);
|
|
3189
|
-
const lineDuration = duration + delay;
|
|
3392
|
+
const lineDuration = duration + 100 + delay;
|
|
3190
3393
|
if (lineDuration > maxAnimationDuration) {
|
|
3191
3394
|
maxAnimationDuration = lineDuration;
|
|
3192
3395
|
}
|
|
@@ -3196,20 +3399,28 @@ class AmLyrics extends i {
|
|
|
3196
3399
|
let delayCounter = 0;
|
|
3197
3400
|
for (let i = end - 1; i >= start; i -= 1) {
|
|
3198
3401
|
const line = lineArray[i];
|
|
3199
|
-
const delay = i <= referenceIndex
|
|
3402
|
+
const delay = i <= referenceIndex
|
|
3403
|
+
? Math.min(delayCounter, maxStaggerSteps) * delayIncrement
|
|
3404
|
+
: 0;
|
|
3200
3405
|
if (i <= referenceIndex && !line.classList.contains('lyrics-gap')) {
|
|
3201
3406
|
delayCounter += 1;
|
|
3202
3407
|
}
|
|
3203
3408
|
line.style.setProperty('--scroll-delta', `${delta}px`);
|
|
3204
3409
|
line.style.setProperty('--lyrics-line-delay', `${delay}ms`);
|
|
3205
|
-
|
|
3410
|
+
lineDelays.set(line, delay);
|
|
3206
3411
|
newAnimatingLines.push(line);
|
|
3207
|
-
const lineDuration = duration + delay;
|
|
3412
|
+
const lineDuration = duration + 100 + delay;
|
|
3208
3413
|
if (lineDuration > maxAnimationDuration) {
|
|
3209
3414
|
maxAnimationDuration = lineDuration;
|
|
3210
3415
|
}
|
|
3211
3416
|
}
|
|
3212
3417
|
}
|
|
3418
|
+
/* Preserve the staggered starts, but make every line settle together.
|
|
3419
|
+
This keeps the selected line from drifting after its neighbours. */
|
|
3420
|
+
for (const line of newAnimatingLines) {
|
|
3421
|
+
const delay = lineDelays.get(line) ?? 0;
|
|
3422
|
+
line.style.setProperty('--scroll-duration', `${Math.max(100, maxAnimationDuration - delay)}ms`);
|
|
3423
|
+
}
|
|
3213
3424
|
// --- Step 3: Force reflow so the browser sees the class removal ---
|
|
3214
3425
|
// Use offsetHeight which is cheaper than getBoundingClientRect
|
|
3215
3426
|
// eslint-disable-next-line no-void
|
|
@@ -3335,30 +3546,255 @@ class AmLyrics extends i {
|
|
|
3335
3546
|
}
|
|
3336
3547
|
/**
|
|
3337
3548
|
* Update syllable highlight animation - apply CSS wipe animation
|
|
3338
|
-
* (Exact copy from YouLyPlus _updateSyllableAnimation)
|
|
3339
3549
|
*/
|
|
3550
|
+
static clamp(value, min, max) {
|
|
3551
|
+
return Math.min(max, Math.max(min, value));
|
|
3552
|
+
}
|
|
3553
|
+
static getVisibleCharacterCount(element) {
|
|
3554
|
+
const attrLength = parseFloat(element.getAttribute('data-word-length') || '');
|
|
3555
|
+
if (Number.isFinite(attrLength) && attrLength > 0)
|
|
3556
|
+
return attrLength;
|
|
3557
|
+
return (element.textContent || '').replace(/\s/g, '').length;
|
|
3558
|
+
}
|
|
3559
|
+
static getLongWordWipeScale(charCount) {
|
|
3560
|
+
if (charCount <= 6)
|
|
3561
|
+
return 1;
|
|
3562
|
+
return (1 +
|
|
3563
|
+
AmLyrics.clamp((charCount - 6) / 10, 0, 1) * LONG_WORD_WIPE_EXTRA_RATIO);
|
|
3564
|
+
}
|
|
3565
|
+
static applyWipeShape(element, charCount) {
|
|
3566
|
+
const extra = AmLyrics.clamp((charCount - 6) / 10, 0, 1) * LONG_WORD_WIPE_EXTRA_EM;
|
|
3567
|
+
const width = BASE_WIPE_GRADIENT_EM + extra;
|
|
3568
|
+
element.style.setProperty('--wipe-gradient-width', `${width.toFixed(3)}em`);
|
|
3569
|
+
element.style.setProperty('--wipe-gradient-half', `${(width / 2).toFixed(3)}em`);
|
|
3570
|
+
}
|
|
3571
|
+
static ensureWordWipeGeometry(charSpans, charCount) {
|
|
3572
|
+
if (charSpans.length === 0)
|
|
3573
|
+
return;
|
|
3574
|
+
const approxWidthCh = Math.max(1, charCount || charSpans.length);
|
|
3575
|
+
charSpans.forEach((span, index) => {
|
|
3576
|
+
if (!span.style.getPropertyValue('--word-wipe-width')) {
|
|
3577
|
+
span.style.setProperty('--word-wipe-width', `${approxWidthCh}ch`);
|
|
3578
|
+
}
|
|
3579
|
+
if (!span.style.getPropertyValue('--char-wipe-position')) {
|
|
3580
|
+
const startPct = Number.parseFloat(span.dataset.wipeStart || `${index / Math.max(1, charSpans.length)}`);
|
|
3581
|
+
span.style.setProperty('--char-wipe-position', `${-(AmLyrics.clamp(startPct, 0, 1) * approxWidthCh)}ch`);
|
|
3582
|
+
}
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3585
|
+
static clearPreHighlight(syllable) {
|
|
3586
|
+
const target = syllable;
|
|
3587
|
+
target.classList.remove('pre-highlight');
|
|
3588
|
+
target.style.removeProperty('--pre-wipe-duration');
|
|
3589
|
+
target.style.removeProperty('--pre-wipe-delay');
|
|
3590
|
+
target.style.animation = '';
|
|
3591
|
+
target
|
|
3592
|
+
.querySelectorAll('.pre-wipe-lead')
|
|
3593
|
+
.forEach(element => AmLyrics.clearPreWipeLead(element));
|
|
3594
|
+
}
|
|
3595
|
+
static clearPreWipeLead(element) {
|
|
3596
|
+
element.classList.remove('pre-wipe-lead');
|
|
3597
|
+
element.style.removeProperty('--pre-wipe-duration');
|
|
3598
|
+
element.style.removeProperty('--pre-wipe-delay');
|
|
3599
|
+
}
|
|
3600
|
+
static hasTextBoundaryAfter(syllable) {
|
|
3601
|
+
return /\s$/.test(syllable.textContent || '');
|
|
3602
|
+
}
|
|
3603
|
+
static getSyllableWordIndex(syllable) {
|
|
3604
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3605
|
+
const virtualWordId = wordElement?.dataset.virtualWordId;
|
|
3606
|
+
if (virtualWordId) {
|
|
3607
|
+
return `virtual:${virtualWordId}`;
|
|
3608
|
+
}
|
|
3609
|
+
const virtualWordStart = wordElement?.dataset.virtualWordStart;
|
|
3610
|
+
const virtualWordEnd = wordElement?.dataset.virtualWordEnd;
|
|
3611
|
+
if (virtualWordStart || virtualWordEnd) {
|
|
3612
|
+
return `virtual:${virtualWordStart || ''}:${virtualWordEnd || ''}`;
|
|
3613
|
+
}
|
|
3614
|
+
return (syllable.getAttribute('data-word-index') ||
|
|
3615
|
+
syllable.getAttribute('data-syllable-index') ||
|
|
3616
|
+
'');
|
|
3617
|
+
}
|
|
3618
|
+
static getNextWordSyllable(syllables, index) {
|
|
3619
|
+
const current = syllables[index];
|
|
3620
|
+
const currentWordIndex = AmLyrics.getSyllableWordIndex(current);
|
|
3621
|
+
const previousSyllable = current;
|
|
3622
|
+
for (let i = index + 1; i < syllables.length; i += 1) {
|
|
3623
|
+
const candidate = syllables[i];
|
|
3624
|
+
if (candidate.classList.contains('transliteration')) {
|
|
3625
|
+
// eslint-disable-next-line no-continue
|
|
3626
|
+
continue;
|
|
3627
|
+
}
|
|
3628
|
+
const candidateWordIndex = AmLyrics.getSyllableWordIndex(candidate);
|
|
3629
|
+
if (candidateWordIndex === currentWordIndex ||
|
|
3630
|
+
!AmLyrics.hasTextBoundaryAfter(previousSyllable)) {
|
|
3631
|
+
return null;
|
|
3632
|
+
}
|
|
3633
|
+
return candidate;
|
|
3634
|
+
}
|
|
3635
|
+
return null;
|
|
3636
|
+
}
|
|
3637
|
+
static getPreviousNonTransliterationSyllable(syllables, index) {
|
|
3638
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
3639
|
+
const candidate = syllables[i];
|
|
3640
|
+
if (!candidate.classList.contains('transliteration')) {
|
|
3641
|
+
return candidate;
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
return null;
|
|
3645
|
+
}
|
|
3646
|
+
static getRenderedWordSyllables(syllable) {
|
|
3647
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3648
|
+
const wordElements = AmLyrics.getCachedVirtualWordElements(wordElement);
|
|
3649
|
+
const wordSyllables = wordElements.flatMap(element => Array.from(element.querySelectorAll('.lyrics-syllable')));
|
|
3650
|
+
return wordSyllables.filter(wordSyllable => !wordSyllable.classList.contains('transliteration'));
|
|
3651
|
+
}
|
|
3652
|
+
static getWordElementForSyllable(syllable) {
|
|
3653
|
+
return syllable.parentElement?.parentElement;
|
|
3654
|
+
}
|
|
3655
|
+
static getWordPreWipeKey(syllable) {
|
|
3656
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3657
|
+
return (wordElement?.dataset.virtualWordId ||
|
|
3658
|
+
`${syllable.getAttribute('data-start-time') || ''}:${AmLyrics.getSyllableWordIndex(syllable)}`);
|
|
3659
|
+
}
|
|
3660
|
+
static isPreWipeArmed(syllable) {
|
|
3661
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3662
|
+
const target = wordElement;
|
|
3663
|
+
return Boolean(target?._wordPreWipeKey === AmLyrics.getWordPreWipeKey(syllable));
|
|
3664
|
+
}
|
|
3665
|
+
static applyWordPreWipe(nextSyllable, wordSyllables, currentTimeMs, preWipeStartMs, preWipeDurationMs) {
|
|
3666
|
+
if (AmLyrics.isPreWipeArmed(nextSyllable))
|
|
3667
|
+
return;
|
|
3668
|
+
const wordElement = AmLyrics.getWordElementForSyllable(nextSyllable);
|
|
3669
|
+
const wordElements = AmLyrics.getCachedVirtualWordElements(wordElement);
|
|
3670
|
+
const charSpans = AmLyrics.getCachedVirtualWordCharSpans(wordElement, []);
|
|
3671
|
+
const elapsedPreWipe = currentTimeMs - preWipeStartMs;
|
|
3672
|
+
const charCount = charSpans.length ||
|
|
3673
|
+
wordSyllables.reduce((total, wordSyllable) => total + AmLyrics.getVisibleCharacterCount(wordSyllable), 0) ||
|
|
3674
|
+
AmLyrics.getVisibleCharacterCount(nextSyllable);
|
|
3675
|
+
AmLyrics.ensureWordWipeGeometry(charSpans, charCount);
|
|
3676
|
+
const leadChar = charSpans[0];
|
|
3677
|
+
const leadCharSyllable = leadChar?.closest('.lyrics-syllable');
|
|
3678
|
+
const preWipeSyllable = leadCharSyllable || wordSyllables[0] || nextSyllable;
|
|
3679
|
+
AmLyrics.applyWipeShape(preWipeSyllable, charCount);
|
|
3680
|
+
preWipeSyllable.style.setProperty('--pre-wipe-duration', `${preWipeDurationMs}ms`);
|
|
3681
|
+
preWipeSyllable.style.setProperty('--pre-wipe-delay', `${-elapsedPreWipe}ms`);
|
|
3682
|
+
preWipeSyllable.classList.add('pre-highlight');
|
|
3683
|
+
// Character-animated words still need a single word-leading gradient.
|
|
3684
|
+
// Giving every glyph this state creates one gradient per character and
|
|
3685
|
+
// makes the whole word enter the pre-wipe continuation simultaneously.
|
|
3686
|
+
if (leadChar) {
|
|
3687
|
+
AmLyrics.applyWipeShape(leadChar, charCount);
|
|
3688
|
+
leadChar.style.setProperty('--pre-wipe-duration', `${preWipeDurationMs}ms`);
|
|
3689
|
+
leadChar.style.setProperty('--pre-wipe-delay', `${-elapsedPreWipe}ms`);
|
|
3690
|
+
leadChar.classList.add('pre-wipe-lead');
|
|
3691
|
+
}
|
|
3692
|
+
wordElements.forEach(element => {
|
|
3693
|
+
const target = element;
|
|
3694
|
+
target._wordPreWipeKey = AmLyrics.getWordPreWipeKey(nextSyllable);
|
|
3695
|
+
});
|
|
3696
|
+
}
|
|
3697
|
+
static maybePreWipeNextWord(syllables, index, currentTimeMs, currentEndTimeMs) {
|
|
3698
|
+
const syllable = syllables[index];
|
|
3699
|
+
if (syllable.classList.contains('line-synced') ||
|
|
3700
|
+
syllable.classList.contains('transliteration') ||
|
|
3701
|
+
syllable.closest('.lyrics-gap')) {
|
|
3702
|
+
return;
|
|
3703
|
+
}
|
|
3704
|
+
const currentWordReady = syllable.classList.contains('finished') ||
|
|
3705
|
+
currentTimeMs >= currentEndTimeMs - WORD_PRE_WIPE_HANDOFF_LEAD_MS;
|
|
3706
|
+
if (!currentWordReady) {
|
|
3707
|
+
return;
|
|
3708
|
+
}
|
|
3709
|
+
const nextSyllable = AmLyrics.getNextWordSyllable(syllables, index);
|
|
3710
|
+
if (!nextSyllable ||
|
|
3711
|
+
nextSyllable.classList.contains('line-synced') ||
|
|
3712
|
+
nextSyllable.classList.contains('transliteration') ||
|
|
3713
|
+
nextSyllable.closest('.lyrics-gap') ||
|
|
3714
|
+
nextSyllable.classList.contains('highlight') ||
|
|
3715
|
+
nextSyllable.classList.contains('finished')) {
|
|
3716
|
+
return;
|
|
3717
|
+
}
|
|
3718
|
+
const nextStartTimeMs = nextSyllable._cachedStartTime;
|
|
3719
|
+
if (!Number.isFinite(nextStartTimeMs))
|
|
3720
|
+
return;
|
|
3721
|
+
const gapMs = nextStartTimeMs - currentEndTimeMs;
|
|
3722
|
+
if (gapMs > NEXT_WORD_PRE_WIPE_MAX_GAP_MS || gapMs < -50) {
|
|
3723
|
+
return;
|
|
3724
|
+
}
|
|
3725
|
+
const nextWordSyllables = AmLyrics.getRenderedWordSyllables(nextSyllable);
|
|
3726
|
+
const preWipeSyllables = nextWordSyllables.length > 0 ? nextWordSyllables : [nextSyllable];
|
|
3727
|
+
const wordElement = AmLyrics.getWordElementForSyllable(nextSyllable);
|
|
3728
|
+
const wordCharSpans = AmLyrics.getCachedVirtualWordCharSpans(wordElement, []);
|
|
3729
|
+
const charCount = wordCharSpans.length ||
|
|
3730
|
+
preWipeSyllables.reduce((total, wordSyllable) => total + AmLyrics.getVisibleCharacterCount(wordSyllable), 0);
|
|
3731
|
+
if (charCount <= 0)
|
|
3732
|
+
return;
|
|
3733
|
+
const preWipeDuration = AmLyrics.clamp(64 + charCount * 9, NEXT_WORD_PRE_WIPE_MIN_DURATION_MS, NEXT_WORD_PRE_WIPE_MAX_DURATION_MS);
|
|
3734
|
+
const preWipeStart = Math.max(nextStartTimeMs - preWipeDuration, currentEndTimeMs - WORD_PRE_WIPE_HANDOFF_LEAD_MS);
|
|
3735
|
+
if (currentTimeMs < preWipeStart || currentTimeMs >= nextStartTimeMs) {
|
|
3736
|
+
return;
|
|
3737
|
+
}
|
|
3738
|
+
AmLyrics.applyWordPreWipe(nextSyllable, preWipeSyllables, currentTimeMs, preWipeStart, preWipeDuration);
|
|
3739
|
+
}
|
|
3740
|
+
static getCachedCharSpans(element) {
|
|
3741
|
+
const cacheTarget = element;
|
|
3742
|
+
if (!cacheTarget._cachedCharSpans) {
|
|
3743
|
+
cacheTarget._cachedCharSpans = Array.from(element.querySelectorAll('span.char'));
|
|
3744
|
+
}
|
|
3745
|
+
return cacheTarget._cachedCharSpans;
|
|
3746
|
+
}
|
|
3747
|
+
static getCachedVirtualWordElements(wordElement) {
|
|
3748
|
+
if (!wordElement)
|
|
3749
|
+
return [];
|
|
3750
|
+
const cacheTarget = wordElement;
|
|
3751
|
+
if (cacheTarget._cachedVirtualWordElements) {
|
|
3752
|
+
return cacheTarget._cachedVirtualWordElements;
|
|
3753
|
+
}
|
|
3754
|
+
const { virtualWordId } = wordElement.dataset;
|
|
3755
|
+
let wordElements = [wordElement];
|
|
3756
|
+
if (virtualWordId && wordElement.parentElement) {
|
|
3757
|
+
wordElements = Array.from(wordElement.parentElement.querySelectorAll('.lyrics-word')).filter(el => el.dataset.virtualWordId === virtualWordId);
|
|
3758
|
+
}
|
|
3759
|
+
wordElements.forEach(element => {
|
|
3760
|
+
const target = element;
|
|
3761
|
+
target._cachedVirtualWordElements = wordElements;
|
|
3762
|
+
});
|
|
3763
|
+
return wordElements;
|
|
3764
|
+
}
|
|
3765
|
+
static getCachedVirtualWordCharSpans(wordElement, fallbackCharSpans) {
|
|
3766
|
+
if (!wordElement)
|
|
3767
|
+
return fallbackCharSpans;
|
|
3768
|
+
const cacheTarget = wordElement;
|
|
3769
|
+
if (cacheTarget._cachedVirtualWordCharSpans) {
|
|
3770
|
+
return cacheTarget._cachedVirtualWordCharSpans;
|
|
3771
|
+
}
|
|
3772
|
+
const wordElements = AmLyrics.getCachedVirtualWordElements(wordElement);
|
|
3773
|
+
const charSpans = wordElements.flatMap(word => Array.from(word.querySelectorAll('span.char')));
|
|
3774
|
+
const result = charSpans.length > 0 ? charSpans : fallbackCharSpans;
|
|
3775
|
+
wordElements.forEach(element => {
|
|
3776
|
+
const target = element;
|
|
3777
|
+
target._cachedVirtualWordCharSpans = result;
|
|
3778
|
+
});
|
|
3779
|
+
return result;
|
|
3780
|
+
}
|
|
3340
3781
|
static updateSyllableAnimation(syllable, elapsedTimeMs = 0) {
|
|
3341
3782
|
if (syllable.classList.contains('highlight'))
|
|
3342
3783
|
return;
|
|
3343
3784
|
const { classList } = syllable;
|
|
3785
|
+
const hadPreHighlight = classList.contains('pre-highlight');
|
|
3344
3786
|
const isRTL = classList.contains('rtl-text');
|
|
3345
|
-
const charSpans =
|
|
3787
|
+
const charSpans = AmLyrics.getCachedCharSpans(syllable);
|
|
3346
3788
|
const wordElement = syllable.parentElement?.parentElement; // syllable-wrap -> word
|
|
3347
|
-
const
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
else if (wordElement) {
|
|
3354
|
-
wordElements = [wordElement];
|
|
3355
|
-
}
|
|
3356
|
-
const allWordCharSpans = wordElements.flatMap(word => Array.from(word.querySelectorAll('span.char')));
|
|
3357
|
-
const isGrowable = wordElement?.classList.contains('growable');
|
|
3358
|
-
const isCharRise = wordElement?.classList.contains('char-rise');
|
|
3789
|
+
const typedWordElement = wordElement;
|
|
3790
|
+
const allWordElements = AmLyrics.getCachedVirtualWordElements(typedWordElement);
|
|
3791
|
+
const allWordCharSpans = AmLyrics.getCachedVirtualWordCharSpans(typedWordElement, charSpans);
|
|
3792
|
+
const isGrowable = typedWordElement?.classList.contains('growable');
|
|
3793
|
+
const isCharRise = typedWordElement?.classList.contains('char-rise');
|
|
3794
|
+
const isCharDrag = typedWordElement?.classList.contains('char-drag');
|
|
3359
3795
|
const isFirstSyllable = syllable.getAttribute('data-syllable-index') === '0';
|
|
3360
3796
|
const syllableStartMs = parseFloat(syllable.getAttribute('data-start-time') || '0');
|
|
3361
|
-
const virtualWordStartMs = parseFloat(
|
|
3797
|
+
const virtualWordStartMs = parseFloat(typedWordElement?.dataset.virtualWordStart || '');
|
|
3362
3798
|
const isFirstInVirtualWord = isFirstSyllable &&
|
|
3363
3799
|
(!Number.isFinite(virtualWordStartMs) ||
|
|
3364
3800
|
Math.abs(syllableStartMs - virtualWordStartMs) < 0.5);
|
|
@@ -3369,7 +3805,10 @@ class AmLyrics extends i {
|
|
|
3369
3805
|
const wordDurationMs = parseFloat(syllable.getAttribute('data-word-duration') ||
|
|
3370
3806
|
syllable.getAttribute('data-duration') ||
|
|
3371
3807
|
'0') || syllableDurationMs;
|
|
3372
|
-
|
|
3808
|
+
const wordElapsedTimeMs = Number.isFinite(virtualWordStartMs)
|
|
3809
|
+
? elapsedTimeMs + (syllableStartMs - virtualWordStartMs)
|
|
3810
|
+
: elapsedTimeMs;
|
|
3811
|
+
const charWipeDurationMs = Math.max(wordDurationMs, syllableDurationMs);
|
|
3373
3812
|
const charAnimationsMap = new Map();
|
|
3374
3813
|
const styleUpdates = [];
|
|
3375
3814
|
// Step 1: Grow Pass
|
|
@@ -3417,41 +3856,81 @@ class AmLyrics extends i {
|
|
|
3417
3856
|
charAnimationsMap.set(span, `rise-char ${riseDurationMs}ms ease-in-out ${riseDelay}ms forwards`);
|
|
3418
3857
|
});
|
|
3419
3858
|
}
|
|
3859
|
+
if (isCharDrag && isFirstInVirtualWord && allWordCharSpans.length > 0) {
|
|
3860
|
+
const finalDuration = Math.max(wordDurationMs, syllableDurationMs);
|
|
3861
|
+
const baseDelayPerChar = AmLyrics.clamp(finalDuration * 0.15, 64, 118);
|
|
3862
|
+
const dragDurationMs = AmLyrics.clamp(finalDuration * 0.82, 560, 900);
|
|
3863
|
+
allWordCharSpans.forEach(span => {
|
|
3864
|
+
const charIndex = parseFloat(span.dataset.syllableCharIndex || '0');
|
|
3865
|
+
const dragDelay = baseDelayPerChar * charIndex;
|
|
3866
|
+
charAnimationsMap.set(span, `drag-char ${dragDurationMs}ms ease ${dragDelay}ms forwards`);
|
|
3867
|
+
});
|
|
3868
|
+
}
|
|
3420
3869
|
// Step 2: Wipe Pass
|
|
3421
3870
|
if (charSpans.length > 0) {
|
|
3422
|
-
|
|
3871
|
+
const wipeCharCount = allWordCharSpans.length ||
|
|
3872
|
+
charSpans.length ||
|
|
3873
|
+
AmLyrics.getVisibleCharacterCount(syllable);
|
|
3874
|
+
const wipeScale = AmLyrics.getLongWordWipeScale(wipeCharCount);
|
|
3875
|
+
AmLyrics.applyWipeShape(syllable, wipeCharCount);
|
|
3876
|
+
AmLyrics.ensureWordWipeGeometry(allWordCharSpans, wipeCharCount);
|
|
3877
|
+
allWordCharSpans.forEach(span => AmLyrics.applyWipeShape(span, wipeCharCount));
|
|
3878
|
+
const hasWordLevelWipe = !isFirstInVirtualWord &&
|
|
3879
|
+
(Boolean(typedWordElement?._wordWipeStarted) ||
|
|
3880
|
+
allWordCharSpans.some(span => span.style.animation.includes('wipe')));
|
|
3881
|
+
let charSpansToAnimate = charSpans;
|
|
3882
|
+
if (isFirstInVirtualWord) {
|
|
3883
|
+
charSpansToAnimate = allWordCharSpans;
|
|
3884
|
+
}
|
|
3885
|
+
else if (hasWordLevelWipe) {
|
|
3886
|
+
charSpansToAnimate = [];
|
|
3887
|
+
}
|
|
3888
|
+
if (charSpansToAnimate.length > 0 && allWordElements.length > 0) {
|
|
3889
|
+
allWordElements.forEach(element => {
|
|
3890
|
+
const target = element;
|
|
3891
|
+
target._wordWipeStarted = true;
|
|
3892
|
+
target._wordPreWipeKey = undefined;
|
|
3893
|
+
});
|
|
3894
|
+
}
|
|
3895
|
+
charSpansToAnimate.forEach((span, charIndex) => {
|
|
3423
3896
|
const startPct = parseFloat(span.dataset.wipeStart || '0');
|
|
3424
3897
|
const durationPct = parseFloat(span.dataset.wipeDuration || '0');
|
|
3425
|
-
const
|
|
3426
|
-
const
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3898
|
+
const globalCharIndex = parseFloat(span.dataset.syllableCharIndex || `${charIndex}`);
|
|
3899
|
+
const hadCharPreWipe = span.classList.contains('pre-wipe-lead') ||
|
|
3900
|
+
(hadPreHighlight && globalCharIndex === 0);
|
|
3901
|
+
const charStartMs = charWipeDurationMs * startPct;
|
|
3902
|
+
const remainingWordWipeMs = Math.max(0, charWipeDurationMs - charStartMs);
|
|
3903
|
+
const wipeDelay = charStartMs - wordElapsedTimeMs;
|
|
3904
|
+
const wipeDuration = Math.min(charWipeDurationMs * durationPct * wipeScale, remainingWordWipeMs);
|
|
3905
|
+
const useStartAnimation = isFirstInContainer && globalCharIndex === 0 && !hadCharPreWipe;
|
|
3906
|
+
let charWipeAnimation = 'char-wipe';
|
|
3907
|
+
if (hadCharPreWipe) {
|
|
3908
|
+
charWipeAnimation = 'char-wipe';
|
|
3431
3909
|
}
|
|
3432
|
-
else {
|
|
3433
|
-
charWipeAnimation =
|
|
3910
|
+
else if (useStartAnimation) {
|
|
3911
|
+
charWipeAnimation = 'char-start-wipe';
|
|
3434
3912
|
}
|
|
3435
3913
|
const existingAnimation = charAnimationsMap.get(span) || span.style.animation || '';
|
|
3436
3914
|
const animationParts = [];
|
|
3437
3915
|
if (existingAnimation &&
|
|
3438
3916
|
(existingAnimation.includes('grow-dynamic') ||
|
|
3439
|
-
existingAnimation.includes('rise-char')
|
|
3917
|
+
existingAnimation.includes('rise-char') ||
|
|
3918
|
+
existingAnimation.includes('drag-char'))) {
|
|
3440
3919
|
animationParts.push(existingAnimation.split(',')[0].trim());
|
|
3441
3920
|
}
|
|
3442
|
-
if (
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
const
|
|
3447
|
-
const preWipeDuration = Math.min(
|
|
3448
|
-
const animDelay = arrivalTime - preWipeDuration;
|
|
3921
|
+
if (globalCharIndex > 0 &&
|
|
3922
|
+
!hadCharPreWipe &&
|
|
3923
|
+
wipeDelay > 0 &&
|
|
3924
|
+
wipeDuration > 0) {
|
|
3925
|
+
const measuredDuration = Number.parseFloat(span.dataset.preWipeDuration || '100');
|
|
3926
|
+
const preWipeDuration = Math.min(measuredDuration, wipeDuration * 0.9, charWipeDurationMs * 0.08, wipeDelay);
|
|
3449
3927
|
if (preWipeDuration >= 16) {
|
|
3450
|
-
animationParts.push(`pre-wipe
|
|
3928
|
+
animationParts.push(`char-pre-wipe ${preWipeDuration}ms linear ${wipeDelay - preWipeDuration}ms none`);
|
|
3451
3929
|
}
|
|
3452
3930
|
}
|
|
3453
3931
|
if (wipeDuration > 0) {
|
|
3454
|
-
|
|
3932
|
+
const wipeFillMode = hadCharPreWipe ? 'both' : 'forwards';
|
|
3933
|
+
animationParts.push(`${charWipeAnimation} ${wipeDuration}ms linear ${wipeDelay}ms ${wipeFillMode}`);
|
|
3455
3934
|
}
|
|
3456
3935
|
if (animationParts.length > 0) {
|
|
3457
3936
|
charAnimationsMap.set(span, animationParts.join(', '));
|
|
@@ -3461,9 +3940,15 @@ class AmLyrics extends i {
|
|
|
3461
3940
|
else {
|
|
3462
3941
|
// Syllable-level wipe for regular (non-growable) words without chars
|
|
3463
3942
|
const wipeRatio = parseFloat(syllable.getAttribute('data-wipe-ratio') || '1');
|
|
3464
|
-
const
|
|
3943
|
+
const wipeCharCount = AmLyrics.getVisibleCharacterCount(syllable);
|
|
3944
|
+
const wipeScale = AmLyrics.getLongWordWipeScale(wipeCharCount);
|
|
3945
|
+
const visualDuration = syllableDurationMs * wipeRatio * wipeScale;
|
|
3946
|
+
AmLyrics.applyWipeShape(syllable, wipeCharCount);
|
|
3465
3947
|
let wipeAnimation = 'wipe';
|
|
3466
|
-
if (
|
|
3948
|
+
if (hadPreHighlight) {
|
|
3949
|
+
wipeAnimation = isRTL ? 'wipe-from-pre-rtl' : 'wipe-from-pre';
|
|
3950
|
+
}
|
|
3951
|
+
else if (isFirstInContainer) {
|
|
3467
3952
|
wipeAnimation = isRTL ? 'start-wipe-rtl' : 'start-wipe';
|
|
3468
3953
|
}
|
|
3469
3954
|
else {
|
|
@@ -3476,16 +3961,25 @@ class AmLyrics extends i {
|
|
|
3476
3961
|
syllable.style.animation = `${currentWipeAnimation} ${visualDuration}ms ${isGap ? 'ease-out' : 'linear'} ${-elapsedTimeMs}ms forwards`;
|
|
3477
3962
|
}
|
|
3478
3963
|
// --- WRITE PHASE ---
|
|
3964
|
+
if (allWordElements.length > 0) {
|
|
3965
|
+
allWordElements.forEach(element => {
|
|
3966
|
+
const target = element;
|
|
3967
|
+
target._wordPreWipeKey = undefined;
|
|
3968
|
+
});
|
|
3969
|
+
}
|
|
3479
3970
|
classList.remove('pre-highlight');
|
|
3480
3971
|
classList.add('highlight');
|
|
3972
|
+
allWordCharSpans.forEach(span => AmLyrics.clearPreWipeLead(span));
|
|
3973
|
+
// Apply keyframe variables before assigning animation strings so the
|
|
3974
|
+
// first painted frame never uses fallback transform values.
|
|
3975
|
+
for (const update of styleUpdates) {
|
|
3976
|
+
update.element.style.setProperty(update.property, update.value);
|
|
3977
|
+
}
|
|
3481
3978
|
for (const [span, animationString] of charAnimationsMap.entries()) {
|
|
3482
3979
|
span.style.willChange = 'transform';
|
|
3980
|
+
span.style.removeProperty('background-color');
|
|
3483
3981
|
span.style.animation = animationString;
|
|
3484
3982
|
}
|
|
3485
|
-
// Apply style updates
|
|
3486
|
-
for (const update of styleUpdates) {
|
|
3487
|
-
update.element.style.setProperty(update.property, update.value);
|
|
3488
|
-
}
|
|
3489
3983
|
}
|
|
3490
3984
|
/**
|
|
3491
3985
|
* Reset syllable animation state
|
|
@@ -3509,10 +4003,19 @@ class AmLyrics extends i {
|
|
|
3509
4003
|
el.style.animation = '';
|
|
3510
4004
|
el.style.transition = 'none';
|
|
3511
4005
|
el.style.backgroundColor = 'var(--lyplus-text-secondary)';
|
|
4006
|
+
AmLyrics.clearPreWipeLead(el);
|
|
3512
4007
|
}
|
|
3513
4008
|
// Immediately remove all state classes
|
|
3514
4009
|
syllable.classList.remove('highlight', 'finished', 'pre-highlight', 'cleanup');
|
|
3515
4010
|
}
|
|
4011
|
+
static resetWordAnimationState(line) {
|
|
4012
|
+
const wordElements = line.querySelectorAll('.lyrics-word');
|
|
4013
|
+
wordElements.forEach(wordElement => {
|
|
4014
|
+
const target = wordElement;
|
|
4015
|
+
target._wordPreWipeKey = undefined;
|
|
4016
|
+
target._wordWipeStarted = false;
|
|
4017
|
+
});
|
|
4018
|
+
}
|
|
3516
4019
|
/**
|
|
3517
4020
|
* Reset all syllables in a line — batches deferred cleanup into a single rAF
|
|
3518
4021
|
*/
|
|
@@ -3520,6 +4023,7 @@ class AmLyrics extends i {
|
|
|
3520
4023
|
if (!line)
|
|
3521
4024
|
return;
|
|
3522
4025
|
line.classList.remove('persist-highlight');
|
|
4026
|
+
AmLyrics.resetWordAnimationState(line);
|
|
3523
4027
|
// eslint-disable-next-line no-param-reassign
|
|
3524
4028
|
line._cachedSyllableElements = null;
|
|
3525
4029
|
const syllables = line.getElementsByClassName('lyrics-syllable');
|
|
@@ -3551,6 +4055,7 @@ class AmLyrics extends i {
|
|
|
3551
4055
|
if (!line)
|
|
3552
4056
|
return;
|
|
3553
4057
|
line.classList.remove('persist-highlight');
|
|
4058
|
+
AmLyrics.resetWordAnimationState(line);
|
|
3554
4059
|
const syllables = line.getElementsByClassName('lyrics-syllable');
|
|
3555
4060
|
for (let i = 0; i < syllables.length; i += 1) {
|
|
3556
4061
|
const s = syllables[i];
|
|
@@ -3568,6 +4073,7 @@ class AmLyrics extends i {
|
|
|
3568
4073
|
el.style.removeProperty('background-color');
|
|
3569
4074
|
el.style.removeProperty('transition');
|
|
3570
4075
|
el.style.removeProperty('filter');
|
|
4076
|
+
AmLyrics.clearPreWipeLead(el);
|
|
3571
4077
|
}
|
|
3572
4078
|
}
|
|
3573
4079
|
}
|
|
@@ -3604,19 +4110,26 @@ class AmLyrics extends i {
|
|
|
3604
4110
|
syllable.style.animation = '';
|
|
3605
4111
|
syllable.style.removeProperty('--pre-wipe-duration');
|
|
3606
4112
|
syllable.style.removeProperty('--pre-wipe-delay');
|
|
4113
|
+
syllable.style.removeProperty('background-color');
|
|
4114
|
+
AmLyrics.applyWipeShape(syllable, AmLyrics.getVisibleCharacterCount(syllable));
|
|
3607
4115
|
const chars = syllable.querySelectorAll('span.char');
|
|
3608
4116
|
for (let ci = 0; ci < chars.length; ci += 1) {
|
|
3609
4117
|
const charEl = chars[ci];
|
|
3610
4118
|
const currentAnim = charEl.style.animation || '';
|
|
3611
4119
|
if (currentAnim.includes('grow-dynamic') ||
|
|
3612
|
-
currentAnim.includes('rise-char')
|
|
4120
|
+
currentAnim.includes('rise-char') ||
|
|
4121
|
+
currentAnim.includes('drag-char')) {
|
|
3613
4122
|
const parts = currentAnim.split(',').map(p => p.trim());
|
|
3614
|
-
const transformAnim = parts.find(p => p.includes('grow-dynamic') ||
|
|
4123
|
+
const transformAnim = parts.find(p => p.includes('grow-dynamic') ||
|
|
4124
|
+
p.includes('rise-char') ||
|
|
4125
|
+
p.includes('drag-char'));
|
|
3615
4126
|
charEl.style.animation = transformAnim || '';
|
|
3616
4127
|
}
|
|
3617
4128
|
else {
|
|
3618
4129
|
charEl.style.animation = '';
|
|
3619
4130
|
}
|
|
4131
|
+
charEl.style.backgroundColor = 'var(--lyplus-text-primary)';
|
|
4132
|
+
AmLyrics.clearPreWipeLead(charEl);
|
|
3620
4133
|
}
|
|
3621
4134
|
}
|
|
3622
4135
|
}
|
|
@@ -3657,14 +4170,16 @@ class AmLyrics extends i {
|
|
|
3657
4170
|
// Early exit check
|
|
3658
4171
|
if (!(currentTimeMs < startTime - 1000 && !hasActiveState)) {
|
|
3659
4172
|
let preHighlightReset = false;
|
|
3660
|
-
//
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
4173
|
+
// Before the syllable starts, pre-highlight only belongs beside a
|
|
4174
|
+
// previous active word. Once the syllable starts, updateSyllableAnimation
|
|
4175
|
+
// consumes the class so the actual wipe can continue from the pre-wipe
|
|
4176
|
+
// pose instead of restarting from the beginning.
|
|
4177
|
+
if (hasPreHighlight && currentTimeMs < startTime) {
|
|
4178
|
+
const prevSyllable = AmLyrics.getPreviousNonTransliterationSyllable(syllables, i);
|
|
4179
|
+
const previousCarriesHighlight = prevSyllable?.classList.contains('highlight') ||
|
|
4180
|
+
prevSyllable?.classList.contains('finished');
|
|
4181
|
+
if (!previousCarriesHighlight) {
|
|
4182
|
+
AmLyrics.clearPreHighlight(syllable);
|
|
3668
4183
|
preHighlightReset = true;
|
|
3669
4184
|
}
|
|
3670
4185
|
}
|
|
@@ -3692,6 +4207,7 @@ class AmLyrics extends i {
|
|
|
3692
4207
|
// Not yet started
|
|
3693
4208
|
AmLyrics.resetSyllable(syllable);
|
|
3694
4209
|
}
|
|
4210
|
+
AmLyrics.maybePreWipeNextWord(syllables, i, currentTimeMs, endTime);
|
|
3695
4211
|
}
|
|
3696
4212
|
}
|
|
3697
4213
|
}
|
|
@@ -4001,6 +4517,9 @@ class AmLyrics extends i {
|
|
|
4001
4517
|
data-end-time="${endTimeMs}"
|
|
4002
4518
|
data-duration="${durationMs}"
|
|
4003
4519
|
data-syllable-index="${syllableIndex}"
|
|
4520
|
+
data-word-index="${syllableIndex}"
|
|
4521
|
+
data-word-length="${syllable.text.replace(/\s/g, '')
|
|
4522
|
+
.length}"
|
|
4004
4523
|
data-wipe-ratio="1"
|
|
4005
4524
|
>${syllable.text}</span
|
|
4006
4525
|
>${bgRomanizedText}</span
|
|
@@ -4022,13 +4541,13 @@ class AmLyrics extends i {
|
|
|
4022
4541
|
const groupGrowable = lineData?.groupGrowable ?? [];
|
|
4023
4542
|
const groupGlowing = lineData?.groupGlowing ?? [];
|
|
4024
4543
|
const groupCharRise = lineData?.groupCharRise ?? [];
|
|
4544
|
+
const groupCharDrag = lineData?.groupCharDrag ?? [];
|
|
4025
4545
|
const vwFullText = lineData?.vwFullText ?? [];
|
|
4026
4546
|
const vwFullDuration = lineData?.vwFullDuration ?? [];
|
|
4027
4547
|
const vwCharOffset = lineData?.vwCharOffset ?? [];
|
|
4028
4548
|
const vwStartMs = lineData?.vwStartMs ?? [];
|
|
4029
4549
|
const vwEndMs = lineData?.vwEndMs ?? [];
|
|
4030
4550
|
const lineIsRTL = lineData?.lineIsRTL ?? false;
|
|
4031
|
-
// Create main vocals using YouLyPlus syllable structure
|
|
4032
4551
|
const mainVocalElement = b `<p
|
|
4033
4552
|
class="main-vocal-container ${lineIsRTL ? 'rtl-text' : ''}"
|
|
4034
4553
|
>
|
|
@@ -4036,25 +4555,23 @@ class AmLyrics extends i {
|
|
|
4036
4555
|
const isGrowable = groupGrowable[groupIdx];
|
|
4037
4556
|
const isGlowing = groupGlowing[groupIdx];
|
|
4038
4557
|
const isCharRise = groupCharRise[groupIdx];
|
|
4039
|
-
const
|
|
4558
|
+
const isCharDrag = groupCharDrag[groupIdx];
|
|
4559
|
+
const isAnimatedByChar = isGrowable || isCharRise || isCharDrag;
|
|
4040
4560
|
const groupLineSynced = group.some(s => s.lineSynced);
|
|
4041
4561
|
const wordText = isAnimatedByChar ? vwFullText[groupIdx] : '';
|
|
4042
4562
|
const wordDuration = isAnimatedByChar
|
|
4043
4563
|
? vwFullDuration[groupIdx]
|
|
4044
4564
|
: 0;
|
|
4045
|
-
const wordNumChars = wordText.length;
|
|
4565
|
+
const wordNumChars = wordText.replace(/\s/g, '').length;
|
|
4046
4566
|
const groupCharOffset = isAnimatedByChar
|
|
4047
4567
|
? vwCharOffset[groupIdx]
|
|
4048
4568
|
: 0;
|
|
4049
|
-
const virtualWordId =
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
const virtualWordStart = isAnimatedByChar
|
|
4053
|
-
? vwStartMs[groupIdx]
|
|
4054
|
-
: '';
|
|
4055
|
-
const virtualWordEnd = isAnimatedByChar ? vwEndMs[groupIdx] : '';
|
|
4569
|
+
const virtualWordId = `${lineIndex}:${vwStartMs[groupIdx]}:${vwEndMs[groupIdx]}`;
|
|
4570
|
+
const virtualWordStart = vwStartMs[groupIdx];
|
|
4571
|
+
const virtualWordEnd = vwEndMs[groupIdx];
|
|
4056
4572
|
let sylCharAccumulator = 0;
|
|
4057
4573
|
const groupText = group.map(s => s.text).join('');
|
|
4574
|
+
const visibleWordLength = groupText.replace(/\s/g, '').length;
|
|
4058
4575
|
const shouldAllowBreak = groupText.trim().length >= 16 ||
|
|
4059
4576
|
/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/.test(groupText);
|
|
4060
4577
|
// Calculate dynamic rise duration based on the audio duration of the word
|
|
@@ -4066,9 +4583,9 @@ class AmLyrics extends i {
|
|
|
4066
4583
|
return b `<span
|
|
4067
4584
|
class="lyrics-word${isGrowable ? ' growable' : ''}${isCharRise
|
|
4068
4585
|
? ' char-rise'
|
|
4069
|
-
: ''}${
|
|
4070
|
-
? '
|
|
4071
|
-
: ''}"
|
|
4586
|
+
: ''}${isCharDrag ? ' char-drag' : ''}${isGlowing
|
|
4587
|
+
? ' glowing'
|
|
4588
|
+
: ''}${shouldAllowBreak ? ' allow-break' : ''}"
|
|
4072
4589
|
data-virtual-word-id="${virtualWordId}"
|
|
4073
4590
|
data-virtual-word-start="${virtualWordStart}"
|
|
4074
4591
|
data-virtual-word-end="${virtualWordEnd}"
|
|
@@ -4095,13 +4612,26 @@ class AmLyrics extends i {
|
|
|
4095
4612
|
: '';
|
|
4096
4613
|
let syllableContent = text;
|
|
4097
4614
|
if (isAnimatedByChar) {
|
|
4098
|
-
let charIndexInsideSyllable = 0;
|
|
4099
4615
|
const numCharsInSyllable = text.replace(/\s/g, '').length || 1;
|
|
4616
|
+
const hasVirtualTiming = wordDuration > 0 && Number.isFinite(virtualWordStart);
|
|
4617
|
+
const syllableStartRatio = hasVirtualTiming
|
|
4618
|
+
? AmLyrics.clamp((startTimeMs - virtualWordStart) / wordDuration, 0, 1)
|
|
4619
|
+
: 0;
|
|
4620
|
+
const syllableDurationRatio = hasVirtualTiming
|
|
4621
|
+
? AmLyrics.clamp(durationMs / wordDuration, 0, 1)
|
|
4622
|
+
: 1;
|
|
4623
|
+
let charIndexInsideSyllable = 0;
|
|
4100
4624
|
syllableContent = b `${text.split('').map(char => {
|
|
4101
4625
|
if (char === ' ')
|
|
4102
4626
|
return ' ';
|
|
4103
4627
|
const charIndexInsideWord = groupCharOffset + sylCharAccumulator;
|
|
4104
|
-
const
|
|
4628
|
+
const localCharIndex = charIndexInsideSyllable;
|
|
4629
|
+
const visibleWordChars = Math.max(1, wordNumChars);
|
|
4630
|
+
const charStartPercentVal = AmLyrics.clamp(syllableStartRatio +
|
|
4631
|
+
(localCharIndex / numCharsInSyllable) *
|
|
4632
|
+
syllableDurationRatio, 0, 1);
|
|
4633
|
+
const charDurationPercentVal = syllableDurationRatio / numCharsInSyllable ||
|
|
4634
|
+
1 / visibleWordChars;
|
|
4105
4635
|
sylCharAccumulator += 1;
|
|
4106
4636
|
charIndexInsideSyllable += 1;
|
|
4107
4637
|
const minDuration = 400;
|
|
@@ -4148,21 +4678,37 @@ class AmLyrics extends i {
|
|
|
4148
4678
|
const normalizedGrowth = (charMaxScale - 1.0) / 0.1;
|
|
4149
4679
|
const effectiveDuration = (wordDuration + durationMs * 2) / 3;
|
|
4150
4680
|
const peakMultiplier = Math.min(1, Math.max(0.3, effectiveDuration / 2000));
|
|
4151
|
-
const
|
|
4681
|
+
const baseTranslateYPeak = -normalizedGrowth * (2 * peakMultiplier); // Further dampened lift peak
|
|
4152
4682
|
const position = (charIndexInsideWord + 0.5) / wordNumChars;
|
|
4153
4683
|
const horizontalOffset = (position - 0.5) * 2 * ((charMaxScale - 1.0) * 25);
|
|
4684
|
+
const isDragMotion = isCharDrag;
|
|
4685
|
+
let charTranslateYPeak = baseTranslateYPeak;
|
|
4686
|
+
if (isCharRise) {
|
|
4687
|
+
charTranslateYPeak = 0;
|
|
4688
|
+
}
|
|
4689
|
+
else if (isDragMotion) {
|
|
4690
|
+
charTranslateYPeak = -0.78;
|
|
4691
|
+
}
|
|
4692
|
+
let motionHorizontalOffset = horizontalOffset;
|
|
4693
|
+
if (isCharRise) {
|
|
4694
|
+
motionHorizontalOffset = 0;
|
|
4695
|
+
}
|
|
4696
|
+
else if (isDragMotion) {
|
|
4697
|
+
motionHorizontalOffset = 0;
|
|
4698
|
+
}
|
|
4154
4699
|
return b `<span
|
|
4155
4700
|
class="char"
|
|
4156
4701
|
data-char-index="${charIndexInsideWord}"
|
|
4157
4702
|
data-syllable-char-index="${charIndexInsideWord}"
|
|
4158
4703
|
data-wipe-start="${charStartPercentVal.toFixed(4)}"
|
|
4159
|
-
data-wipe-duration="${
|
|
4704
|
+
data-wipe-duration="${charDurationPercentVal.toFixed(4)}"
|
|
4160
4705
|
data-horizontal-offset="${horizontalOffset.toFixed(2)}"
|
|
4161
4706
|
data-max-scale="${charMaxScale.toFixed(3)}"
|
|
4162
4707
|
data-matrix-scale="${(charMaxScale * 0.98).toFixed(3)}"
|
|
4163
|
-
data-char-offset-x="${(
|
|
4708
|
+
data-char-offset-x="${(motionHorizontalOffset * 0.98).toFixed(2)}"
|
|
4164
4709
|
data-shadow-intensity="${charShadowIntensity.toFixed(3)}"
|
|
4165
4710
|
data-translate-y-peak="${charTranslateYPeak.toFixed(3)}"
|
|
4711
|
+
style="--word-wipe-width: ${visibleWordChars}ch; --char-wipe-position: -${charIndexInsideWord}ch"
|
|
4166
4712
|
>${char}</span
|
|
4167
4713
|
>`;
|
|
4168
4714
|
})}`;
|
|
@@ -4180,6 +4726,8 @@ class AmLyrics extends i {
|
|
|
4180
4726
|
data-duration="${durationMs}"
|
|
4181
4727
|
data-word-duration="${wordDuration}"
|
|
4182
4728
|
data-syllable-index="${sylIdx}"
|
|
4729
|
+
data-word-index="${groupIdx}"
|
|
4730
|
+
data-word-length="${visibleWordLength}"
|
|
4183
4731
|
data-wipe-ratio="1"
|
|
4184
4732
|
>${syllableContent}</span
|
|
4185
4733
|
>${romanizedText}</span
|
|
@@ -4412,13 +4960,14 @@ class AmLyrics extends i {
|
|
|
4412
4960
|
<button
|
|
4413
4961
|
class="download-button source-switch-btn"
|
|
4414
4962
|
title="Switch Lyrics Source"
|
|
4415
|
-
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;"
|
|
4416
4963
|
@click=${this.switchSource}
|
|
4417
4964
|
?disabled=${this.isFetchingAlternatives}
|
|
4418
4965
|
>
|
|
4419
4966
|
<svg
|
|
4420
|
-
class="source-switch-svg lucide lucide-arrow-down-up-icon lucide-arrow-down-up
|
|
4421
|
-
|
|
4967
|
+
class="source-switch-svg lucide lucide-arrow-down-up-icon lucide-arrow-down-up ${this
|
|
4968
|
+
.isFetchingAlternatives
|
|
4969
|
+
? 'is-loading'
|
|
4970
|
+
: ''}"
|
|
4422
4971
|
xmlns="http://www.w3.org/2000/svg"
|
|
4423
4972
|
width="12"
|
|
4424
4973
|
height="12"
|
|
@@ -4476,9 +5025,6 @@ class AmLyrics extends i {
|
|
|
4476
5025
|
}
|
|
4477
5026
|
}
|
|
4478
5027
|
AmLyrics.styles = i$3 `
|
|
4479
|
-
/* ==========================================================================
|
|
4480
|
-
YOULYPLUS-INSPIRED STYLING - Design Tokens & Variables
|
|
4481
|
-
========================================================================== */
|
|
4482
5028
|
:host {
|
|
4483
5029
|
--lyplus-lyrics-palette: var(
|
|
4484
5030
|
--am-lyrics-highlight-color,
|
|
@@ -4507,6 +5053,8 @@ AmLyrics.styles = i$3 `
|
|
|
4507
5053
|
--lyplus-blur-amount: 0.07em;
|
|
4508
5054
|
--lyplus-blur-amount-near: 0.035em;
|
|
4509
5055
|
--lyplus-fade-gap-timing-function: ease-out;
|
|
5056
|
+
--wipe-gradient-width: 0.75em;
|
|
5057
|
+
--wipe-gradient-half: 0.375em;
|
|
4510
5058
|
|
|
4511
5059
|
--lyrics-scroll-padding-top: 25%;
|
|
4512
5060
|
|
|
@@ -4534,6 +5082,9 @@ AmLyrics.styles = i$3 `
|
|
|
4534
5082
|
max-height: 100vh;
|
|
4535
5083
|
overflow-y: auto;
|
|
4536
5084
|
-webkit-overflow-scrolling: touch;
|
|
5085
|
+
-webkit-touch-callout: none;
|
|
5086
|
+
-webkit-user-select: none;
|
|
5087
|
+
user-select: none;
|
|
4537
5088
|
box-sizing: border-box;
|
|
4538
5089
|
scrollbar-width: none;
|
|
4539
5090
|
overflow-anchor: none;
|
|
@@ -4663,7 +5214,7 @@ AmLyrics.styles = i$3 `
|
|
|
4663
5214
|
.lyrics-line.bg-expanded .background-vocal-container {
|
|
4664
5215
|
max-height: 4em;
|
|
4665
5216
|
opacity: 1;
|
|
4666
|
-
will-change:
|
|
5217
|
+
will-change: opacity;
|
|
4667
5218
|
}
|
|
4668
5219
|
|
|
4669
5220
|
.lyrics-line.bg-expanded .background-vocal-wrap {
|
|
@@ -4680,6 +5231,18 @@ AmLyrics.styles = i$3 `
|
|
|
4680
5231
|
opacity: 1;
|
|
4681
5232
|
}
|
|
4682
5233
|
|
|
5234
|
+
/* Predictive scrolling begins before the next timestamp. Start dimming
|
|
5235
|
+
the outgoing line at the same moment so it settles with the scroll. */
|
|
5236
|
+
.lyrics-line.scroll-exiting {
|
|
5237
|
+
opacity: 0.8;
|
|
5238
|
+
color: var(--lyplus-text-secondary);
|
|
5239
|
+
transition:
|
|
5240
|
+
opacity var(--scroll-duration, 400ms) cubic-bezier(0.41, 0, 0.12, 0.99),
|
|
5241
|
+
transform var(--scroll-duration, 400ms)
|
|
5242
|
+
cubic-bezier(0.41, 0, 0.12, 0.99) var(--lyrics-line-delay, 0ms),
|
|
5243
|
+
filter var(--scroll-duration, 400ms) ease;
|
|
5244
|
+
}
|
|
5245
|
+
|
|
4683
5246
|
.lyrics-line.persist-highlight {
|
|
4684
5247
|
filter: none !important;
|
|
4685
5248
|
opacity: 1;
|
|
@@ -4815,11 +5378,22 @@ AmLyrics.styles = i$3 `
|
|
|
4815
5378
|
white-space: nowrap;
|
|
4816
5379
|
}
|
|
4817
5380
|
|
|
5381
|
+
.lyrics-word.char-drag {
|
|
5382
|
+
display: inline-block;
|
|
5383
|
+
vertical-align: baseline;
|
|
5384
|
+
white-space: nowrap;
|
|
5385
|
+
}
|
|
5386
|
+
|
|
4818
5387
|
.lyrics-word.char-rise.allow-break {
|
|
4819
5388
|
display: inline;
|
|
4820
5389
|
white-space: normal;
|
|
4821
5390
|
}
|
|
4822
5391
|
|
|
5392
|
+
.lyrics-word.char-drag.allow-break {
|
|
5393
|
+
display: inline;
|
|
5394
|
+
white-space: normal;
|
|
5395
|
+
}
|
|
5396
|
+
|
|
4823
5397
|
.lyrics-syllable-wrap {
|
|
4824
5398
|
display: inline;
|
|
4825
5399
|
}
|
|
@@ -4874,44 +5448,30 @@ AmLyrics.styles = i$3 `
|
|
|
4874
5448
|
.lyrics-line.active:not(.lyrics-gap)
|
|
4875
5449
|
.lyrics-syllable.pre-highlight.no-chars {
|
|
4876
5450
|
background-repeat: no-repeat;
|
|
4877
|
-
background-image:
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
var(--
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
var(--lyplus-text-primary, #fff) 100%,
|
|
4887
|
-
#0000 100%
|
|
4888
|
-
);
|
|
4889
|
-
background-size:
|
|
4890
|
-
0.5em 100%,
|
|
4891
|
-
0% 100%;
|
|
4892
|
-
background-position:
|
|
4893
|
-
-0.5em 0%,
|
|
4894
|
-
-0.25em 0%;
|
|
5451
|
+
background-image: linear-gradient(
|
|
5452
|
+
90deg,
|
|
5453
|
+
var(--lyplus-text-primary, #fff) 0%,
|
|
5454
|
+
var(--lyplus-text-primary, #fff)
|
|
5455
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5456
|
+
#0000 100%
|
|
5457
|
+
);
|
|
5458
|
+
background-size: 0% 100%;
|
|
5459
|
+
background-position: left;
|
|
4895
5460
|
}
|
|
4896
5461
|
|
|
4897
5462
|
.lyrics-line.active:not(.lyrics-gap) .lyrics-syllable.highlight.rtl-text,
|
|
4898
5463
|
.lyrics-line.active:not(.lyrics-gap)
|
|
4899
5464
|
.lyrics-syllable.pre-highlight.rtl-text {
|
|
4900
5465
|
direction: rtl;
|
|
4901
|
-
background-image:
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
transparent 100%
|
|
4911
|
-
);
|
|
4912
|
-
background-position:
|
|
4913
|
-
calc(100% + 0.5em) 0%,
|
|
4914
|
-
right 0%;
|
|
5466
|
+
background-image: linear-gradient(
|
|
5467
|
+
-90deg,
|
|
5468
|
+
var(--lyplus-text-primary) 0%,
|
|
5469
|
+
var(--lyplus-text-primary)
|
|
5470
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5471
|
+
transparent 100%
|
|
5472
|
+
);
|
|
5473
|
+
background-size: 0% 100%;
|
|
5474
|
+
background-position: right 0%;
|
|
4915
5475
|
}
|
|
4916
5476
|
|
|
4917
5477
|
/* Background vocals: muted gray wipe instead of white.
|
|
@@ -4928,18 +5488,13 @@ AmLyrics.styles = i$3 `
|
|
|
4928
5488
|
.lyrics-line.pre-active
|
|
4929
5489
|
.background-vocal-container
|
|
4930
5490
|
.lyrics-syllable.pre-highlight.no-chars {
|
|
4931
|
-
background-image:
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
linear-gradient(
|
|
4939
|
-
90deg,
|
|
4940
|
-
color-mix(in srgb, var(--lyplus-text-primary, #fff) 50%, #888888) 100%,
|
|
4941
|
-
#0000 100%
|
|
4942
|
-
);
|
|
5491
|
+
background-image: linear-gradient(
|
|
5492
|
+
90deg,
|
|
5493
|
+
color-mix(in srgb, var(--lyplus-text-primary, #fff) 50%, #888888) 0%,
|
|
5494
|
+
color-mix(in srgb, var(--lyplus-text-primary, #fff) 50%, #888888)
|
|
5495
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5496
|
+
#0000 100%
|
|
5497
|
+
);
|
|
4943
5498
|
}
|
|
4944
5499
|
|
|
4945
5500
|
.lyrics-line.active
|
|
@@ -4954,17 +5509,13 @@ AmLyrics.styles = i$3 `
|
|
|
4954
5509
|
.lyrics-line.pre-active
|
|
4955
5510
|
.background-vocal-container
|
|
4956
5511
|
.lyrics-syllable.pre-highlight.rtl-text {
|
|
4957
|
-
background-image:
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
-90deg,
|
|
4965
|
-
color-mix(in srgb, var(--lyplus-text-primary) 50%, #888888) 100%,
|
|
4966
|
-
transparent 100%
|
|
4967
|
-
);
|
|
5512
|
+
background-image: linear-gradient(
|
|
5513
|
+
-90deg,
|
|
5514
|
+
color-mix(in srgb, var(--lyplus-text-primary) 50%, #888888) 0%,
|
|
5515
|
+
color-mix(in srgb, var(--lyplus-text-primary) 50%, #888888)
|
|
5516
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5517
|
+
transparent 100%
|
|
5518
|
+
);
|
|
4968
5519
|
}
|
|
4969
5520
|
|
|
4970
5521
|
/* Non-growable words float up with a gentle curve */
|
|
@@ -4988,6 +5539,10 @@ AmLyrics.styles = i$3 `
|
|
|
4988
5539
|
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
4989
5540
|
}
|
|
4990
5541
|
|
|
5542
|
+
.lyrics-word.char-drag .lyrics-syllable.cleanup .char {
|
|
5543
|
+
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
5544
|
+
}
|
|
5545
|
+
|
|
4991
5546
|
.lyrics-line.persist-highlight
|
|
4992
5547
|
.lyrics-word.growable
|
|
4993
5548
|
.lyrics-syllable.finished
|
|
@@ -4995,6 +5550,10 @@ AmLyrics.styles = i$3 `
|
|
|
4995
5550
|
.lyrics-line.persist-highlight
|
|
4996
5551
|
.lyrics-word.char-rise
|
|
4997
5552
|
.lyrics-syllable.finished
|
|
5553
|
+
.char,
|
|
5554
|
+
.lyrics-line.persist-highlight
|
|
5555
|
+
.lyrics-word.char-drag
|
|
5556
|
+
.lyrics-syllable.finished
|
|
4998
5557
|
.char {
|
|
4999
5558
|
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
5000
5559
|
}
|
|
@@ -5105,6 +5664,10 @@ AmLyrics.styles = i$3 `
|
|
|
5105
5664
|
transform 0.7s ease;
|
|
5106
5665
|
}
|
|
5107
5666
|
|
|
5667
|
+
.lyrics-word.char-drag span.char {
|
|
5668
|
+
transition: color 0.18s;
|
|
5669
|
+
}
|
|
5670
|
+
|
|
5108
5671
|
/* Active char spans: structural only, wipe animation sets gradient */
|
|
5109
5672
|
.lyrics-line.active .lyrics-syllable span.char {
|
|
5110
5673
|
background-clip: text;
|
|
@@ -5123,52 +5686,34 @@ AmLyrics.styles = i$3 `
|
|
|
5123
5686
|
#0000 100%
|
|
5124
5687
|
);
|
|
5125
5688
|
background-size:
|
|
5126
|
-
0.
|
|
5689
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
5127
5690
|
0% 100%;
|
|
5128
5691
|
background-position:
|
|
5129
|
-
-0.
|
|
5130
|
-
|
|
5692
|
+
calc(-1 * var(--wipe-gradient-width, 0.75em)) 0%,
|
|
5693
|
+
left;
|
|
5131
5694
|
transition:
|
|
5132
5695
|
transform 0.7s ease,
|
|
5133
5696
|
color 0.18s;
|
|
5134
5697
|
}
|
|
5135
5698
|
|
|
5136
5699
|
.lyrics-line.active .lyrics-syllable span.char.highlight {
|
|
5137
|
-
background-image:
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
#0000 100%
|
|
5147
|
-
);
|
|
5148
|
-
background-position:
|
|
5149
|
-
calc(100% + 0.5em) 0%,
|
|
5150
|
-
calc(100% + 0.25em) 0%;
|
|
5700
|
+
background-image: linear-gradient(
|
|
5701
|
+
-90deg,
|
|
5702
|
+
var(--lyplus-text-primary, #fff) 0%,
|
|
5703
|
+
var(--lyplus-text-primary, #fff)
|
|
5704
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5705
|
+
#0000 100%
|
|
5706
|
+
);
|
|
5707
|
+
background-size: 0% 100%;
|
|
5708
|
+
background-position: right 0%;
|
|
5151
5709
|
}
|
|
5152
5710
|
|
|
5153
|
-
.lyrics-line.active .lyrics-syllable
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
#0000 100%
|
|
5160
|
-
),
|
|
5161
|
-
linear-gradient(
|
|
5162
|
-
90deg,
|
|
5163
|
-
var(--lyplus-text-primary, #fff) 100%,
|
|
5164
|
-
#0000 100%
|
|
5165
|
-
);
|
|
5166
|
-
background-size:
|
|
5167
|
-
0.75em 100%,
|
|
5168
|
-
0% 100%;
|
|
5169
|
-
background-position:
|
|
5170
|
-
-0.85em 0%,
|
|
5171
|
-
-0.25em 0%;
|
|
5711
|
+
.lyrics-line.active .lyrics-syllable span.char.pre-wipe-lead {
|
|
5712
|
+
animation-name: char-pre-wipe;
|
|
5713
|
+
animation-duration: var(--pre-wipe-duration);
|
|
5714
|
+
animation-delay: var(--pre-wipe-delay);
|
|
5715
|
+
animation-timing-function: linear;
|
|
5716
|
+
animation-fill-mode: forwards;
|
|
5172
5717
|
}
|
|
5173
5718
|
|
|
5174
5719
|
/* ==========================================================================
|
|
@@ -5245,13 +5790,7 @@ AmLyrics.styles = i$3 `
|
|
|
5245
5790
|
color: var(--lyplus-text-primary) !important;
|
|
5246
5791
|
}
|
|
5247
5792
|
|
|
5248
|
-
.lyrics-line.
|
|
5249
|
-
animation: fade-in-line 0.14s ease-out forwards !important;
|
|
5250
|
-
color: var(--lyplus-text-primary) !important;
|
|
5251
|
-
}
|
|
5252
|
-
|
|
5253
|
-
.lyrics-line.active .lyrics-syllable.line-synced span.char,
|
|
5254
|
-
.lyrics-line.pre-active .lyrics-syllable.line-synced span.char {
|
|
5793
|
+
.lyrics-line.active .lyrics-syllable.line-synced span.char {
|
|
5255
5794
|
background-image: none !important;
|
|
5256
5795
|
background-color: var(--lyplus-text-primary) !important;
|
|
5257
5796
|
transition: background-color 120ms ease-out !important;
|
|
@@ -5424,6 +5963,49 @@ AmLyrics.styles = i$3 `
|
|
|
5424
5963
|
gap: 4px;
|
|
5425
5964
|
}
|
|
5426
5965
|
|
|
5966
|
+
.source-switch-btn {
|
|
5967
|
+
position: relative;
|
|
5968
|
+
display: inline-flex;
|
|
5969
|
+
align-items: center;
|
|
5970
|
+
padding: 2px 8px;
|
|
5971
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
5972
|
+
min-height: 28px;
|
|
5973
|
+
background: transparent;
|
|
5974
|
+
border-radius: 6px;
|
|
5975
|
+
color: #aaa;
|
|
5976
|
+
cursor: pointer;
|
|
5977
|
+
font-family: inherit;
|
|
5978
|
+
font-size: 11px;
|
|
5979
|
+
transition:
|
|
5980
|
+
color 0.2s ease,
|
|
5981
|
+
border-color 0.2s ease,
|
|
5982
|
+
background-color 0.2s ease,
|
|
5983
|
+
transform 0.12s ease;
|
|
5984
|
+
}
|
|
5985
|
+
|
|
5986
|
+
.source-switch-btn::before {
|
|
5987
|
+
content: '';
|
|
5988
|
+
position: absolute;
|
|
5989
|
+
inset: -6px;
|
|
5990
|
+
}
|
|
5991
|
+
|
|
5992
|
+
.source-switch-btn:active:not(:disabled) {
|
|
5993
|
+
transform: scale(0.96);
|
|
5994
|
+
}
|
|
5995
|
+
|
|
5996
|
+
.source-switch-btn:disabled {
|
|
5997
|
+
cursor: default;
|
|
5998
|
+
opacity: 0.7;
|
|
5999
|
+
}
|
|
6000
|
+
|
|
6001
|
+
.source-switch-svg {
|
|
6002
|
+
margin-right: 4px;
|
|
6003
|
+
}
|
|
6004
|
+
|
|
6005
|
+
.source-switch-svg.is-loading {
|
|
6006
|
+
animation: source-switch-spin 1s linear infinite;
|
|
6007
|
+
}
|
|
6008
|
+
|
|
5427
6009
|
.control-button {
|
|
5428
6010
|
background: transparent;
|
|
5429
6011
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
@@ -5432,7 +6014,10 @@ AmLyrics.styles = i$3 `
|
|
|
5432
6014
|
font-size: 0.8em;
|
|
5433
6015
|
color: rgba(255, 255, 255, 0.6);
|
|
5434
6016
|
cursor: pointer;
|
|
5435
|
-
transition:
|
|
6017
|
+
transition:
|
|
6018
|
+
color 0.2s,
|
|
6019
|
+
border-color 0.2s,
|
|
6020
|
+
background-color 0.2s;
|
|
5436
6021
|
font-weight: normal;
|
|
5437
6022
|
}
|
|
5438
6023
|
|
|
@@ -5570,136 +6155,157 @@ AmLyrics.styles = i$3 `
|
|
|
5570
6155
|
KEYFRAME ANIMATIONS
|
|
5571
6156
|
========================================================================== */
|
|
5572
6157
|
|
|
6158
|
+
@keyframes source-switch-spin {
|
|
6159
|
+
to {
|
|
6160
|
+
transform: rotate(360deg);
|
|
6161
|
+
}
|
|
6162
|
+
}
|
|
6163
|
+
|
|
5573
6164
|
/* Wipe animation for syllables */
|
|
5574
6165
|
@keyframes wipe {
|
|
5575
6166
|
from {
|
|
5576
|
-
background-size:
|
|
5577
|
-
|
|
5578
|
-
0% 100%;
|
|
5579
|
-
background-position:
|
|
5580
|
-
-0.375em 0%,
|
|
5581
|
-
left;
|
|
6167
|
+
background-size: 0% 100%;
|
|
6168
|
+
background-position: left;
|
|
5582
6169
|
}
|
|
5583
6170
|
to {
|
|
5584
|
-
background-size:
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
6171
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6172
|
+
background-position: left;
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
|
|
6176
|
+
@keyframes wipe-from-pre {
|
|
6177
|
+
from {
|
|
6178
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6179
|
+
background-position: left;
|
|
6180
|
+
}
|
|
6181
|
+
to {
|
|
6182
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6183
|
+
background-position: left;
|
|
5590
6184
|
}
|
|
5591
6185
|
}
|
|
5592
6186
|
|
|
5593
6187
|
@keyframes start-wipe {
|
|
5594
6188
|
0% {
|
|
5595
|
-
background-size:
|
|
5596
|
-
|
|
5597
|
-
0% 100%;
|
|
5598
|
-
background-position:
|
|
5599
|
-
-0.75em 0%,
|
|
5600
|
-
-0.375em 0%;
|
|
6189
|
+
background-size: 0% 100%;
|
|
6190
|
+
background-position: left;
|
|
5601
6191
|
}
|
|
5602
6192
|
100% {
|
|
5603
|
-
background-size:
|
|
5604
|
-
|
|
5605
|
-
100% 100%;
|
|
5606
|
-
background-position:
|
|
5607
|
-
calc(100% + 0.375em) 0%,
|
|
5608
|
-
left;
|
|
6193
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6194
|
+
background-position: left;
|
|
5609
6195
|
}
|
|
5610
6196
|
}
|
|
5611
6197
|
|
|
5612
6198
|
@keyframes wipe-rtl {
|
|
5613
6199
|
from {
|
|
5614
|
-
background-size:
|
|
5615
|
-
|
|
5616
|
-
0% 100%;
|
|
5617
|
-
background-position:
|
|
5618
|
-
calc(100% + 0.375em) 0%,
|
|
5619
|
-
calc(100% + 0.36em) 0%;
|
|
6200
|
+
background-size: 0% 100%;
|
|
6201
|
+
background-position: right 0%;
|
|
5620
6202
|
}
|
|
5621
6203
|
to {
|
|
5622
|
-
background-size:
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
6204
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6205
|
+
background-position: right 0%;
|
|
6206
|
+
}
|
|
6207
|
+
}
|
|
6208
|
+
|
|
6209
|
+
@keyframes wipe-from-pre-rtl {
|
|
6210
|
+
from {
|
|
6211
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6212
|
+
background-position: right 0%;
|
|
6213
|
+
}
|
|
6214
|
+
to {
|
|
6215
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6216
|
+
background-position: right 0%;
|
|
5628
6217
|
}
|
|
5629
6218
|
}
|
|
5630
6219
|
|
|
5631
6220
|
@keyframes start-wipe-rtl {
|
|
5632
6221
|
0% {
|
|
5633
|
-
background-size:
|
|
5634
|
-
|
|
5635
|
-
0% 100%;
|
|
5636
|
-
background-position:
|
|
5637
|
-
calc(100% + 0.75em) 0%,
|
|
5638
|
-
calc(100% + 0.5em) 0%;
|
|
6222
|
+
background-size: 0% 100%;
|
|
6223
|
+
background-position: right 0%;
|
|
5639
6224
|
}
|
|
5640
6225
|
100% {
|
|
5641
|
-
background-size:
|
|
5642
|
-
|
|
5643
|
-
100% 100%;
|
|
5644
|
-
background-position:
|
|
5645
|
-
-0.75em 0%,
|
|
5646
|
-
right 0%;
|
|
6226
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6227
|
+
background-position: right 0%;
|
|
5647
6228
|
}
|
|
5648
6229
|
}
|
|
5649
6230
|
|
|
5650
6231
|
@keyframes pre-wipe-universal {
|
|
6232
|
+
from {
|
|
6233
|
+
background-size: 0% 100%;
|
|
6234
|
+
background-position: left;
|
|
6235
|
+
}
|
|
6236
|
+
to {
|
|
6237
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6238
|
+
background-position: left;
|
|
6239
|
+
}
|
|
6240
|
+
}
|
|
6241
|
+
|
|
6242
|
+
@keyframes pre-wipe-universal-rtl {
|
|
6243
|
+
from {
|
|
6244
|
+
background-size: 0% 100%;
|
|
6245
|
+
background-position: right 0%;
|
|
6246
|
+
}
|
|
6247
|
+
to {
|
|
6248
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6249
|
+
background-position: right 0%;
|
|
6250
|
+
}
|
|
6251
|
+
}
|
|
6252
|
+
|
|
6253
|
+
/* Character-rendered words use a separate moving gradient in front of
|
|
6254
|
+
their solid fill. This makes the individual glyph wipes read as one
|
|
6255
|
+
continuous word-level wipe. */
|
|
6256
|
+
@keyframes char-pre-wipe {
|
|
5651
6257
|
from {
|
|
5652
6258
|
background-size:
|
|
5653
|
-
0.75em 100%,
|
|
6259
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
5654
6260
|
0% 100%;
|
|
5655
6261
|
background-position:
|
|
5656
|
-
-0.75em 0%,
|
|
6262
|
+
calc(-1 * var(--wipe-gradient-width, 0.75em)) 0%,
|
|
5657
6263
|
left;
|
|
5658
6264
|
}
|
|
5659
6265
|
to {
|
|
5660
6266
|
background-size:
|
|
5661
|
-
0.75em 100%,
|
|
6267
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
5662
6268
|
0% 100%;
|
|
5663
6269
|
background-position:
|
|
5664
|
-
-0.375em 0%,
|
|
6270
|
+
calc(-1 * var(--wipe-gradient-half, 0.375em)) 0%,
|
|
5665
6271
|
left;
|
|
5666
6272
|
}
|
|
5667
6273
|
}
|
|
5668
6274
|
|
|
5669
|
-
@keyframes
|
|
6275
|
+
@keyframes char-start-wipe {
|
|
5670
6276
|
from {
|
|
5671
6277
|
background-size:
|
|
5672
|
-
0.75em 100%,
|
|
6278
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
5673
6279
|
0% 100%;
|
|
5674
6280
|
background-position:
|
|
5675
|
-
calc(
|
|
5676
|
-
|
|
6281
|
+
calc(-1 * var(--wipe-gradient-width, 0.75em)) 0%,
|
|
6282
|
+
left;
|
|
5677
6283
|
}
|
|
5678
6284
|
to {
|
|
5679
6285
|
background-size:
|
|
5680
|
-
0.75em 100%,
|
|
5681
|
-
|
|
6286
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
6287
|
+
100% 100%;
|
|
5682
6288
|
background-position:
|
|
5683
|
-
calc(100% + 0.375em) 0%,
|
|
5684
|
-
|
|
6289
|
+
calc(100% + var(--wipe-gradient-half, 0.375em)) 0%,
|
|
6290
|
+
left;
|
|
5685
6291
|
}
|
|
5686
6292
|
}
|
|
5687
6293
|
|
|
5688
|
-
@keyframes
|
|
6294
|
+
@keyframes char-wipe {
|
|
5689
6295
|
from {
|
|
5690
6296
|
background-size:
|
|
5691
|
-
0.75em 100%,
|
|
6297
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
5692
6298
|
0% 100%;
|
|
5693
6299
|
background-position:
|
|
5694
|
-
-0.
|
|
6300
|
+
calc(-1 * var(--wipe-gradient-half, 0.375em)) 0%,
|
|
5695
6301
|
left;
|
|
5696
6302
|
}
|
|
5697
6303
|
to {
|
|
5698
6304
|
background-size:
|
|
5699
|
-
0.75em 100%,
|
|
5700
|
-
|
|
6305
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
6306
|
+
100% 100%;
|
|
5701
6307
|
background-position:
|
|
5702
|
-
-0.375em 0%,
|
|
6308
|
+
calc(100% + var(--wipe-gradient-half, 0.375em)) 0%,
|
|
5703
6309
|
left;
|
|
5704
6310
|
}
|
|
5705
6311
|
}
|
|
@@ -5794,6 +6400,15 @@ AmLyrics.styles = i$3 `
|
|
|
5794
6400
|
}
|
|
5795
6401
|
}
|
|
5796
6402
|
|
|
6403
|
+
@keyframes drag-char {
|
|
6404
|
+
0% {
|
|
6405
|
+
transform: translate3d(0, 0, 0);
|
|
6406
|
+
}
|
|
6407
|
+
100% {
|
|
6408
|
+
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6411
|
+
|
|
5797
6412
|
@keyframes grow-static {
|
|
5798
6413
|
0%,
|
|
5799
6414
|
100% {
|