@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/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.6';
|
|
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 = 100;
|
|
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,11 +499,12 @@ 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
|
-
const activeLines = this.lyricsContainer.querySelectorAll('.lyrics-line.active, .lyrics-line.pre-active, .lyrics-line.bg-expanded');
|
|
505
|
+
const activeLines = this.lyricsContainer.querySelectorAll('.lyrics-line.active, .lyrics-line.pre-active, .lyrics-line.bg-expanded, .lyrics-line.scroll-exiting');
|
|
495
506
|
activeLines.forEach(line => {
|
|
496
|
-
line.classList.remove('active', 'pre-active', 'bg-expanded');
|
|
507
|
+
line.classList.remove('active', 'pre-active', 'bg-expanded', 'scroll-exiting');
|
|
497
508
|
AmLyrics.resetSyllables(line);
|
|
498
509
|
});
|
|
499
510
|
const activeGaps = this.lyricsContainer.querySelectorAll('.lyrics-gap.active, .lyrics-gap.gap-exiting');
|
|
@@ -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,13 +1948,15 @@ 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 {
|
|
1905
1957
|
AmLyrics.finishSyllablesUpToTime(lineElement, newTime);
|
|
1906
1958
|
}
|
|
1907
|
-
lineElement.classList.remove('active', 'bg-expanded');
|
|
1959
|
+
lineElement.classList.remove('active', 'bg-expanded', 'scroll-exiting');
|
|
1908
1960
|
if (lineElement.classList.contains('pre-active')) {
|
|
1909
1961
|
lineElement.classList.remove('pre-active');
|
|
1910
1962
|
}
|
|
@@ -1914,13 +1966,14 @@ 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'
|
|
1923
|
-
lineElement.classList.remove('pre-active');
|
|
1975
|
+
lineElement.classList.add('active');
|
|
1976
|
+
lineElement.classList.remove('pre-active', 'scroll-exiting');
|
|
1924
1977
|
const preIdx = this.preActiveLineElements.indexOf(lineElement);
|
|
1925
1978
|
if (preIdx !== -1)
|
|
1926
1979
|
this.preActiveLineElements.splice(preIdx, 1);
|
|
@@ -2125,7 +2178,11 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2125
2178
|
}
|
|
2126
2179
|
}
|
|
2127
2180
|
updated(changedProperties) {
|
|
2128
|
-
|
|
2181
|
+
const lyricsDomBecameRenderable = changedProperties.has('lyrics') ||
|
|
2182
|
+
(changedProperties.has('isLoading') &&
|
|
2183
|
+
!this.isLoading &&
|
|
2184
|
+
Boolean(this.lyrics));
|
|
2185
|
+
if (lyricsDomBecameRenderable) {
|
|
2129
2186
|
this._invalidateCaches();
|
|
2130
2187
|
this._ensureLineDataCache();
|
|
2131
2188
|
this._updateCachedIsUnsynced();
|
|
@@ -2139,8 +2196,12 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2139
2196
|
for (const lineIndex of activeLines) {
|
|
2140
2197
|
const lineEl = this._getLineElement(lineIndex);
|
|
2141
2198
|
if (lineEl)
|
|
2142
|
-
lineEl.classList.add('active'
|
|
2199
|
+
lineEl.classList.add('active');
|
|
2143
2200
|
}
|
|
2201
|
+
const primaryActiveLine = this.getPrimaryActiveLineIndex(activeLines);
|
|
2202
|
+
this.setBackgroundExpandedLine(primaryActiveLine !== null
|
|
2203
|
+
? this._getLineElement(primaryActiveLine)
|
|
2204
|
+
: null);
|
|
2144
2205
|
// Trigger a faux time-change so that updateSyllablesForLine fires
|
|
2145
2206
|
// to setup inline syllable CSS wipe animations for whatever the current time is
|
|
2146
2207
|
this._onTimeChanged(0, this.currentTime);
|
|
@@ -2179,6 +2240,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2179
2240
|
this.preActiveLineElements = [];
|
|
2180
2241
|
this.positionedLineElements = [];
|
|
2181
2242
|
this.activeGapLineElements = [];
|
|
2243
|
+
this.clearBackgroundExpandedLine();
|
|
2182
2244
|
this.setUserScrolling(false);
|
|
2183
2245
|
// Cancel any running animations
|
|
2184
2246
|
if (this.animationFrameId) {
|
|
@@ -2233,6 +2295,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2233
2295
|
// Don't override it with a scroll back to the last lyric.
|
|
2234
2296
|
const footer = this.lyricsContainer.querySelector('.lyrics-footer');
|
|
2235
2297
|
if (footer?.classList.contains('active')) {
|
|
2298
|
+
this.setBackgroundExpandedLine(null);
|
|
2236
2299
|
return;
|
|
2237
2300
|
}
|
|
2238
2301
|
// 1. Compute scroll lookahead based on gap to next line (YouLyPlus style)
|
|
@@ -2273,17 +2336,23 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2273
2336
|
targetElement = this._getLineElement(targetLineIdx);
|
|
2274
2337
|
}
|
|
2275
2338
|
}
|
|
2276
|
-
if (!targetElement)
|
|
2339
|
+
if (!targetElement) {
|
|
2340
|
+
this.setBackgroundExpandedLine(null);
|
|
2277
2341
|
return;
|
|
2278
|
-
|
|
2279
|
-
|
|
2342
|
+
}
|
|
2343
|
+
const scrollDuration = scrollLookAheadMs;
|
|
2344
|
+
if (targetElement !== this.currentPrimaryActiveLine || forceScroll) {
|
|
2345
|
+
targetElement.style.setProperty('--scroll-duration', `${scrollDuration}ms`);
|
|
2346
|
+
}
|
|
2347
|
+
this.setBackgroundExpandedLine(targetElement);
|
|
2348
|
+
// Unblur the upcoming target line early while the separate bg-expanded
|
|
2349
|
+
// class starts background vocal height/opacity in sync with scroll.
|
|
2280
2350
|
if (!targetElement.classList.contains('active')) {
|
|
2281
2351
|
targetElement.classList.add('pre-active');
|
|
2282
2352
|
if (!this.preActiveLineElements.includes(targetElement)) {
|
|
2283
2353
|
this.preActiveLineElements.push(targetElement);
|
|
2284
2354
|
}
|
|
2285
2355
|
}
|
|
2286
|
-
const scrollDuration = scrollLookAheadMs;
|
|
2287
2356
|
this.focusLine(targetElement, forceScroll, scrollDuration);
|
|
2288
2357
|
}
|
|
2289
2358
|
_getTextWidth(text, font) {
|
|
@@ -2356,6 +2425,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2356
2425
|
this.preActiveLineElements = [];
|
|
2357
2426
|
this.positionedLineElements = [];
|
|
2358
2427
|
this.activeGapLineElements = [];
|
|
2428
|
+
this.clearBackgroundExpandedLine();
|
|
2359
2429
|
this.visibilityObserver?.disconnect();
|
|
2360
2430
|
this.visibilityObserver = undefined;
|
|
2361
2431
|
}
|
|
@@ -2391,6 +2461,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2391
2461
|
const groupGrowable = new Array(wordGroups.length).fill(false);
|
|
2392
2462
|
const groupGlowing = new Array(wordGroups.length).fill(false);
|
|
2393
2463
|
const groupCharRise = new Array(wordGroups.length).fill(false);
|
|
2464
|
+
const groupCharDrag = new Array(wordGroups.length).fill(false);
|
|
2394
2465
|
const vwFullText = new Array(wordGroups.length).fill('');
|
|
2395
2466
|
const vwFullDuration = new Array(wordGroups.length).fill(0);
|
|
2396
2467
|
const vwCharOffset = new Array(wordGroups.length).fill(0);
|
|
@@ -2426,28 +2497,42 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2426
2497
|
const isLineSynced = line.isWordSynced === false || line.text.some(s => s.lineSynced);
|
|
2427
2498
|
let isGrowableVW = canAnimateByChar && wordLen > 0 && wordLen <= 7;
|
|
2428
2499
|
if (isGrowableVW) {
|
|
2429
|
-
if (wordLen
|
|
2500
|
+
if (wordLen <= 1) {
|
|
2430
2501
|
isGrowableVW =
|
|
2431
2502
|
combinedDuration >= 1050 && combinedDuration >= wordLen * 525;
|
|
2432
2503
|
}
|
|
2504
|
+
else if (wordLen <= 3) {
|
|
2505
|
+
isGrowableVW =
|
|
2506
|
+
combinedDuration >=
|
|
2507
|
+
SHORT_WORD_GLOW_MIN_DURATION_MS + (wordLen - 2) * 140;
|
|
2508
|
+
}
|
|
2433
2509
|
else {
|
|
2434
2510
|
isGrowableVW =
|
|
2435
2511
|
combinedDuration >= 850 && combinedDuration >= wordLen * 190;
|
|
2436
2512
|
}
|
|
2437
2513
|
}
|
|
2438
2514
|
const hasCharRiseDuration = combinedDuration >= Math.max(700, wordLen * 85);
|
|
2515
|
+
const hasTinyWordDragDuration = wordLen >= 2 &&
|
|
2516
|
+
wordLen <= 3 &&
|
|
2517
|
+
combinedDuration >=
|
|
2518
|
+
Math.max(SHORT_WORD_DRAG_MIN_DURATION_MS, wordLen * 150);
|
|
2439
2519
|
const hasLongShortWordDuration = wordLen >= 4 && combinedDuration >= Math.max(1300, wordLen * 260);
|
|
2440
2520
|
const isCharRiseVW = canAnimateByChar &&
|
|
2441
2521
|
!isLineSynced &&
|
|
2442
2522
|
!isGrowableVW &&
|
|
2443
2523
|
((wordLen >= 8 && hasCharRiseDuration) ||
|
|
2444
2524
|
(wordLen < 8 && hasLongShortWordDuration));
|
|
2525
|
+
const isCharDragVW = canAnimateByChar &&
|
|
2526
|
+
!isLineSynced &&
|
|
2527
|
+
!isGrowableVW &&
|
|
2528
|
+
hasTinyWordDragDuration;
|
|
2445
2529
|
const isGlowingVW = isGrowableVW && !isLineSynced;
|
|
2446
2530
|
let charOff = 0;
|
|
2447
2531
|
for (let gi = vwStart; gi <= vwEnd; gi += 1) {
|
|
2448
2532
|
groupGrowable[gi] = isGrowableVW;
|
|
2449
2533
|
groupGlowing[gi] = isGlowingVW;
|
|
2450
2534
|
groupCharRise[gi] = isCharRiseVW;
|
|
2535
|
+
groupCharDrag[gi] = isCharDragVW;
|
|
2451
2536
|
vwFullText[gi] = combinedText;
|
|
2452
2537
|
vwFullDuration[gi] = combinedDuration;
|
|
2453
2538
|
vwCharOffset[gi] = charOff;
|
|
@@ -2463,6 +2548,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2463
2548
|
groupGrowable,
|
|
2464
2549
|
groupGlowing,
|
|
2465
2550
|
groupCharRise,
|
|
2551
|
+
groupCharDrag,
|
|
2466
2552
|
vwFullText,
|
|
2467
2553
|
vwFullDuration,
|
|
2468
2554
|
vwCharOffset,
|
|
@@ -2482,48 +2568,106 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2482
2568
|
return;
|
|
2483
2569
|
const computedStyle = getComputedStyle(referenceSyllable);
|
|
2484
2570
|
const { font } = computedStyle; // Full font string
|
|
2485
|
-
const fontSize = parseFloat(computedStyle.fontSize);
|
|
2486
|
-
const charTimedWords = this.shadowRoot.querySelectorAll('.lyrics-word.growable, .lyrics-word.char-rise');
|
|
2487
|
-
if (
|
|
2571
|
+
const fontSize = Number.parseFloat(computedStyle.fontSize) || 16;
|
|
2572
|
+
const charTimedWords = Array.from(this.shadowRoot.querySelectorAll('.lyrics-word.growable, .lyrics-word.char-rise, .lyrics-word.char-drag'));
|
|
2573
|
+
if (charTimedWords.length === 0)
|
|
2488
2574
|
return;
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2575
|
+
const wordsByVirtualId = new Map();
|
|
2576
|
+
charTimedWords.forEach((wordSpan, index) => {
|
|
2577
|
+
const virtualWordId = wordSpan.dataset.virtualWordId || `word-${index}`;
|
|
2578
|
+
const words = wordsByVirtualId.get(virtualWordId);
|
|
2579
|
+
if (words) {
|
|
2580
|
+
words.push(wordSpan);
|
|
2581
|
+
}
|
|
2582
|
+
else {
|
|
2583
|
+
wordsByVirtualId.set(virtualWordId, [wordSpan]);
|
|
2584
|
+
}
|
|
2585
|
+
});
|
|
2586
|
+
wordsByVirtualId.forEach(wordSpans => {
|
|
2492
2587
|
const syllables = [];
|
|
2493
|
-
|
|
2494
|
-
const
|
|
2495
|
-
|
|
2496
|
-
|
|
2588
|
+
wordSpans.forEach(wordSpan => {
|
|
2589
|
+
const syllableWraps = wordSpan.querySelectorAll('.lyrics-syllable-wrap');
|
|
2590
|
+
syllableWraps.forEach(wrap => {
|
|
2591
|
+
const syl = wrap.querySelector('.lyrics-syllable');
|
|
2592
|
+
if (syl)
|
|
2593
|
+
syllables.push(syl);
|
|
2594
|
+
});
|
|
2497
2595
|
});
|
|
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
|
-
|
|
2596
|
+
const charSpans = syllables.flatMap(syl => {
|
|
2597
|
+
const spans = Array.from(syl.querySelectorAll('.char'));
|
|
2598
|
+
const target = syl;
|
|
2599
|
+
target._cachedCharSpans = spans;
|
|
2600
|
+
return spans;
|
|
2601
|
+
});
|
|
2602
|
+
if (charSpans.length === 0)
|
|
2603
|
+
return;
|
|
2604
|
+
wordSpans.forEach(wordSpan => {
|
|
2605
|
+
const target = wordSpan;
|
|
2606
|
+
target._cachedVirtualWordElements = wordSpans;
|
|
2607
|
+
target._cachedVirtualWordCharSpans = charSpans;
|
|
2608
|
+
});
|
|
2609
|
+
const syllableEntries = syllables.map(syl => {
|
|
2610
|
+
const spans = syl._cachedCharSpans;
|
|
2611
|
+
const charWidths = spans.map(span => this._getTextWidth(span.textContent || '', font));
|
|
2612
|
+
const totalWidth = charWidths.reduce((a, b) => a + b, 0);
|
|
2613
|
+
return {
|
|
2614
|
+
syl,
|
|
2615
|
+
spans,
|
|
2616
|
+
charWidths,
|
|
2617
|
+
totalWidth,
|
|
2618
|
+
start: parseFloat(syl.getAttribute('data-start-time') || ''),
|
|
2619
|
+
end: parseFloat(syl.getAttribute('data-end-time') || ''),
|
|
2620
|
+
};
|
|
2621
|
+
});
|
|
2622
|
+
const totalWordWidth = syllableEntries.reduce((total, entry) => total + entry.totalWidth, 0);
|
|
2623
|
+
if (totalWordWidth <= 0)
|
|
2624
|
+
return;
|
|
2625
|
+
const virtualWordStart = Math.min(...syllableEntries
|
|
2626
|
+
.map(entry => entry.start)
|
|
2627
|
+
.filter(start => Number.isFinite(start)));
|
|
2628
|
+
const virtualWordEnd = Math.max(...syllableEntries
|
|
2629
|
+
.map(entry => entry.end)
|
|
2630
|
+
.filter(end => Number.isFinite(end)));
|
|
2631
|
+
const virtualWordDuration = virtualWordEnd - virtualWordStart;
|
|
2632
|
+
const hasTimedSyllables = Number.isFinite(virtualWordStart) &&
|
|
2633
|
+
Number.isFinite(virtualWordEnd) &&
|
|
2634
|
+
virtualWordDuration > 0;
|
|
2635
|
+
const wordVelocityPxPerMs = hasTimedSyllables
|
|
2636
|
+
? totalWordWidth / virtualWordDuration
|
|
2637
|
+
: 0;
|
|
2638
|
+
const gradientLeadWidthPx = (BASE_WIPE_GRADIENT_EM * Math.max(1, fontSize)) / 2;
|
|
2639
|
+
const measuredPreWipeDuration = wordVelocityPxPerMs > 0
|
|
2640
|
+
? gradientLeadWidthPx / wordVelocityPxPerMs
|
|
2641
|
+
: 100;
|
|
2642
|
+
let cumulativeCharWidth = 0;
|
|
2643
|
+
syllableEntries.forEach(entry => {
|
|
2644
|
+
let cumulativeSyllableWidth = 0;
|
|
2645
|
+
const syllableDuration = entry.end - entry.start;
|
|
2646
|
+
const useSyllableTiming = hasTimedSyllables &&
|
|
2647
|
+
Number.isFinite(entry.start) &&
|
|
2648
|
+
Number.isFinite(entry.end) &&
|
|
2649
|
+
syllableDuration > 0 &&
|
|
2650
|
+
entry.totalWidth > 0;
|
|
2651
|
+
entry.spans.forEach((span, index) => {
|
|
2652
|
+
const charWidth = entry.charWidths[index];
|
|
2653
|
+
let startPercent = cumulativeCharWidth / totalWordWidth;
|
|
2654
|
+
let durationPercent = charWidth / totalWordWidth;
|
|
2655
|
+
if (useSyllableTiming) {
|
|
2656
|
+
const charStartMs = entry.start -
|
|
2657
|
+
virtualWordStart +
|
|
2658
|
+
(cumulativeSyllableWidth / entry.totalWidth) * syllableDuration;
|
|
2659
|
+
const charDurationMs = (charWidth / entry.totalWidth) * syllableDuration;
|
|
2660
|
+
startPercent = AmLyrics.clamp(charStartMs / virtualWordDuration, 0, 1);
|
|
2661
|
+
durationPercent = AmLyrics.clamp(charDurationMs / virtualWordDuration, 0, 1);
|
|
2525
2662
|
}
|
|
2663
|
+
const target = span;
|
|
2664
|
+
target.dataset.wipeStart = startPercent.toFixed(4);
|
|
2665
|
+
target.dataset.wipeDuration = durationPercent.toFixed(4);
|
|
2666
|
+
target.dataset.preWipeDuration = measuredPreWipeDuration.toFixed(2);
|
|
2667
|
+
target.style.setProperty('--word-wipe-width', `${totalWordWidth}px`);
|
|
2668
|
+
target.style.setProperty('--char-wipe-position', `${-cumulativeCharWidth}px`);
|
|
2526
2669
|
cumulativeCharWidth += charWidth;
|
|
2670
|
+
cumulativeSyllableWidth += charWidth;
|
|
2527
2671
|
});
|
|
2528
2672
|
});
|
|
2529
2673
|
});
|
|
@@ -2531,6 +2675,33 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2531
2675
|
static arraysEqual(a, b) {
|
|
2532
2676
|
return a.length === b.length && a.every((val, i) => val === b[i]);
|
|
2533
2677
|
}
|
|
2678
|
+
static isLineSyncedLine(line) {
|
|
2679
|
+
if (!line)
|
|
2680
|
+
return false;
|
|
2681
|
+
return line.isWordSynced === false || line.text.some(s => s.lineSynced);
|
|
2682
|
+
}
|
|
2683
|
+
getLineHighlightEndTime(index) {
|
|
2684
|
+
if (!this.lyrics)
|
|
2685
|
+
return 0;
|
|
2686
|
+
const line = this.lyrics[index];
|
|
2687
|
+
if (!line)
|
|
2688
|
+
return 0;
|
|
2689
|
+
const rawEnd = Math.max(line.endtime, line.timestamp);
|
|
2690
|
+
const nextLine = this.lyrics[index + 1];
|
|
2691
|
+
if (!nextLine || nextLine.timestamp <= line.timestamp) {
|
|
2692
|
+
return rawEnd > line.timestamp ? rawEnd + 200 : rawEnd;
|
|
2693
|
+
}
|
|
2694
|
+
if (rawEnd > line.timestamp) {
|
|
2695
|
+
if (nextLine.timestamp < rawEnd) {
|
|
2696
|
+
return rawEnd;
|
|
2697
|
+
}
|
|
2698
|
+
const gapToNext = nextLine.timestamp - rawEnd;
|
|
2699
|
+
if (gapToNext >= INSTRUMENTAL_THRESHOLD_MS) {
|
|
2700
|
+
return rawEnd;
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
return nextLine.timestamp;
|
|
2704
|
+
}
|
|
2534
2705
|
static getLineIndexFromElement(lineElement) {
|
|
2535
2706
|
if (!lineElement)
|
|
2536
2707
|
return null;
|
|
@@ -2561,6 +2732,26 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2561
2732
|
}
|
|
2562
2733
|
this.preActiveLineElements = keptLines;
|
|
2563
2734
|
}
|
|
2735
|
+
setBackgroundExpandedLine(lineElement) {
|
|
2736
|
+
const target = lineElement &&
|
|
2737
|
+
!lineElement.classList.contains('lyrics-gap') &&
|
|
2738
|
+
lineElement.querySelector('.background-vocal-container')
|
|
2739
|
+
? lineElement
|
|
2740
|
+
: null;
|
|
2741
|
+
if (this.backgroundExpandedLine === target) {
|
|
2742
|
+
if (target && !target.classList.contains('bg-expanded')) {
|
|
2743
|
+
target.classList.add('bg-expanded');
|
|
2744
|
+
}
|
|
2745
|
+
return;
|
|
2746
|
+
}
|
|
2747
|
+
this.backgroundExpandedLine?.classList.remove('bg-expanded');
|
|
2748
|
+
this.backgroundExpandedLine = target;
|
|
2749
|
+
target?.classList.add('bg-expanded');
|
|
2750
|
+
}
|
|
2751
|
+
clearBackgroundExpandedLine() {
|
|
2752
|
+
this.backgroundExpandedLine?.classList.remove('bg-expanded');
|
|
2753
|
+
this.backgroundExpandedLine = null;
|
|
2754
|
+
}
|
|
2564
2755
|
getPrimaryActiveLineIndex(activeIndices) {
|
|
2565
2756
|
if (activeIndices.length === 0)
|
|
2566
2757
|
return null;
|
|
@@ -2637,7 +2828,12 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2637
2828
|
// effectiveEndTimes). Lines stay active until their extended end,
|
|
2638
2829
|
// so we no longer need to remove .active here.
|
|
2639
2830
|
this.lastPrimaryActiveLine = this.currentPrimaryActiveLine;
|
|
2831
|
+
if (this.lastPrimaryActiveLine) {
|
|
2832
|
+
this.lastPrimaryActiveLine.style.setProperty('--scroll-duration', `${scrollDuration ?? SCROLL_ANIMATION_DURATION_MS}ms`);
|
|
2833
|
+
this.lastPrimaryActiveLine.classList.add('scroll-exiting');
|
|
2834
|
+
}
|
|
2640
2835
|
this.currentPrimaryActiveLine = lineElement;
|
|
2836
|
+
this.currentPrimaryActiveLine.classList.remove('scroll-exiting');
|
|
2641
2837
|
const lineIndex = AmLyrics.getLineIndexFromElement(lineElement);
|
|
2642
2838
|
if (lineIndex !== null) {
|
|
2643
2839
|
this.lastActiveIndex = lineIndex;
|
|
@@ -2750,9 +2946,10 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2750
2946
|
const activeLines = [];
|
|
2751
2947
|
for (let i = 0; i < this.lyrics.length; i += 1) {
|
|
2752
2948
|
const line = this.lyrics[i];
|
|
2949
|
+
const highlightEndTime = this.getLineHighlightEndTime(i);
|
|
2753
2950
|
if (line.timestamp > time)
|
|
2754
2951
|
break;
|
|
2755
|
-
if (time >= line.timestamp && time <
|
|
2952
|
+
if (time >= line.timestamp && time < highlightEndTime) {
|
|
2756
2953
|
activeLines.push(i);
|
|
2757
2954
|
}
|
|
2758
2955
|
}
|
|
@@ -2949,7 +3146,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2949
3146
|
allLines.forEach(lineEl => {
|
|
2950
3147
|
AmLyrics.resetSyllables(lineEl);
|
|
2951
3148
|
// Remove scroll-animate class and properties to stop any scroll animations
|
|
2952
|
-
lineEl.classList.remove('scroll-animate');
|
|
3149
|
+
lineEl.classList.remove('scroll-animate', 'scroll-exiting');
|
|
2953
3150
|
lineEl.style.removeProperty('--scroll-delta');
|
|
2954
3151
|
lineEl.style.removeProperty('--lyrics-line-delay');
|
|
2955
3152
|
});
|
|
@@ -2977,6 +3174,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2977
3174
|
this.lastPrimaryActiveLine = null;
|
|
2978
3175
|
this.activeLineIds.clear();
|
|
2979
3176
|
this.animatingLines = [];
|
|
3177
|
+
this.setBackgroundExpandedLine(null);
|
|
2980
3178
|
// Find the clicked line element and scroll to it with forceScroll (like YouLyPlus)
|
|
2981
3179
|
// Timestamps are already in milliseconds — match the data-start-time attribute directly
|
|
2982
3180
|
const clickedLineElement = this.lyricsContainer?.querySelector(`.lyrics-line[data-start-time="${line.text[0]?.timestamp || 0}"]`);
|
|
@@ -2993,6 +3191,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
2993
3191
|
this.isClickSeeking = false;
|
|
2994
3192
|
}, 800);
|
|
2995
3193
|
this.scrollToActiveLineYouLy(clickedLineElement, true);
|
|
3194
|
+
this.setBackgroundExpandedLine(clickedLineElement);
|
|
2996
3195
|
}
|
|
2997
3196
|
const event = new CustomEvent('line-click', {
|
|
2998
3197
|
detail: {
|
|
@@ -3170,26 +3369,30 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3170
3369
|
return;
|
|
3171
3370
|
const duration = Math.min(450, scrollDuration ?? SCROLL_ANIMATION_DURATION_MS);
|
|
3172
3371
|
const delayIncrement = duration * 0.1;
|
|
3372
|
+
const maxStaggerSteps = 4;
|
|
3173
3373
|
const lookAhead = 20;
|
|
3174
3374
|
const len = lineArray.length;
|
|
3175
3375
|
const start = Math.max(0, referenceIndex - lookAhead);
|
|
3176
3376
|
const end = Math.min(len, referenceIndex + lookAhead);
|
|
3177
3377
|
let maxAnimationDuration = 0;
|
|
3178
3378
|
const newAnimatingLines = [];
|
|
3379
|
+
const lineDelays = new Map();
|
|
3179
3380
|
const scrollingDown = delta >= 0;
|
|
3180
3381
|
if (scrollingDown) {
|
|
3181
3382
|
let delayCounter = 0;
|
|
3182
3383
|
for (let i = start; i < end; i += 1) {
|
|
3183
3384
|
const line = lineArray[i];
|
|
3184
|
-
const delay = i >= referenceIndex
|
|
3385
|
+
const delay = i >= referenceIndex
|
|
3386
|
+
? Math.min(delayCounter, maxStaggerSteps) * delayIncrement
|
|
3387
|
+
: 0;
|
|
3185
3388
|
if (i >= referenceIndex && !line.classList.contains('lyrics-gap')) {
|
|
3186
3389
|
delayCounter += 1;
|
|
3187
3390
|
}
|
|
3188
3391
|
line.style.setProperty('--scroll-delta', `${delta}px`);
|
|
3189
3392
|
line.style.setProperty('--lyrics-line-delay', `${delay}ms`);
|
|
3190
|
-
|
|
3393
|
+
lineDelays.set(line, delay);
|
|
3191
3394
|
newAnimatingLines.push(line);
|
|
3192
|
-
const lineDuration = duration + delay;
|
|
3395
|
+
const lineDuration = duration + 100 + delay;
|
|
3193
3396
|
if (lineDuration > maxAnimationDuration) {
|
|
3194
3397
|
maxAnimationDuration = lineDuration;
|
|
3195
3398
|
}
|
|
@@ -3199,20 +3402,28 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3199
3402
|
let delayCounter = 0;
|
|
3200
3403
|
for (let i = end - 1; i >= start; i -= 1) {
|
|
3201
3404
|
const line = lineArray[i];
|
|
3202
|
-
const delay = i <= referenceIndex
|
|
3405
|
+
const delay = i <= referenceIndex
|
|
3406
|
+
? Math.min(delayCounter, maxStaggerSteps) * delayIncrement
|
|
3407
|
+
: 0;
|
|
3203
3408
|
if (i <= referenceIndex && !line.classList.contains('lyrics-gap')) {
|
|
3204
3409
|
delayCounter += 1;
|
|
3205
3410
|
}
|
|
3206
3411
|
line.style.setProperty('--scroll-delta', `${delta}px`);
|
|
3207
3412
|
line.style.setProperty('--lyrics-line-delay', `${delay}ms`);
|
|
3208
|
-
|
|
3413
|
+
lineDelays.set(line, delay);
|
|
3209
3414
|
newAnimatingLines.push(line);
|
|
3210
|
-
const lineDuration = duration + delay;
|
|
3415
|
+
const lineDuration = duration + 100 + delay;
|
|
3211
3416
|
if (lineDuration > maxAnimationDuration) {
|
|
3212
3417
|
maxAnimationDuration = lineDuration;
|
|
3213
3418
|
}
|
|
3214
3419
|
}
|
|
3215
3420
|
}
|
|
3421
|
+
/* Preserve the staggered starts, but make every line settle together.
|
|
3422
|
+
This keeps the selected line from drifting after its neighbours. */
|
|
3423
|
+
for (const line of newAnimatingLines) {
|
|
3424
|
+
const delay = lineDelays.get(line) ?? 0;
|
|
3425
|
+
line.style.setProperty('--scroll-duration', `${Math.max(100, maxAnimationDuration - delay)}ms`);
|
|
3426
|
+
}
|
|
3216
3427
|
// --- Step 3: Force reflow so the browser sees the class removal ---
|
|
3217
3428
|
// Use offsetHeight which is cheaper than getBoundingClientRect
|
|
3218
3429
|
// eslint-disable-next-line no-void
|
|
@@ -3338,30 +3549,255 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3338
3549
|
}
|
|
3339
3550
|
/**
|
|
3340
3551
|
* Update syllable highlight animation - apply CSS wipe animation
|
|
3341
|
-
* (Exact copy from YouLyPlus _updateSyllableAnimation)
|
|
3342
3552
|
*/
|
|
3553
|
+
static clamp(value, min, max) {
|
|
3554
|
+
return Math.min(max, Math.max(min, value));
|
|
3555
|
+
}
|
|
3556
|
+
static getVisibleCharacterCount(element) {
|
|
3557
|
+
const attrLength = parseFloat(element.getAttribute('data-word-length') || '');
|
|
3558
|
+
if (Number.isFinite(attrLength) && attrLength > 0)
|
|
3559
|
+
return attrLength;
|
|
3560
|
+
return (element.textContent || '').replace(/\s/g, '').length;
|
|
3561
|
+
}
|
|
3562
|
+
static getLongWordWipeScale(charCount) {
|
|
3563
|
+
if (charCount <= 6)
|
|
3564
|
+
return 1;
|
|
3565
|
+
return (1 +
|
|
3566
|
+
AmLyrics.clamp((charCount - 6) / 10, 0, 1) * LONG_WORD_WIPE_EXTRA_RATIO);
|
|
3567
|
+
}
|
|
3568
|
+
static applyWipeShape(element, charCount) {
|
|
3569
|
+
const extra = AmLyrics.clamp((charCount - 6) / 10, 0, 1) * LONG_WORD_WIPE_EXTRA_EM;
|
|
3570
|
+
const width = BASE_WIPE_GRADIENT_EM + extra;
|
|
3571
|
+
element.style.setProperty('--wipe-gradient-width', `${width.toFixed(3)}em`);
|
|
3572
|
+
element.style.setProperty('--wipe-gradient-half', `${(width / 2).toFixed(3)}em`);
|
|
3573
|
+
}
|
|
3574
|
+
static ensureWordWipeGeometry(charSpans, charCount) {
|
|
3575
|
+
if (charSpans.length === 0)
|
|
3576
|
+
return;
|
|
3577
|
+
const approxWidthCh = Math.max(1, charCount || charSpans.length);
|
|
3578
|
+
charSpans.forEach((span, index) => {
|
|
3579
|
+
if (!span.style.getPropertyValue('--word-wipe-width')) {
|
|
3580
|
+
span.style.setProperty('--word-wipe-width', `${approxWidthCh}ch`);
|
|
3581
|
+
}
|
|
3582
|
+
if (!span.style.getPropertyValue('--char-wipe-position')) {
|
|
3583
|
+
const startPct = Number.parseFloat(span.dataset.wipeStart || `${index / Math.max(1, charSpans.length)}`);
|
|
3584
|
+
span.style.setProperty('--char-wipe-position', `${-(AmLyrics.clamp(startPct, 0, 1) * approxWidthCh)}ch`);
|
|
3585
|
+
}
|
|
3586
|
+
});
|
|
3587
|
+
}
|
|
3588
|
+
static clearPreHighlight(syllable) {
|
|
3589
|
+
const target = syllable;
|
|
3590
|
+
target.classList.remove('pre-highlight');
|
|
3591
|
+
target.style.removeProperty('--pre-wipe-duration');
|
|
3592
|
+
target.style.removeProperty('--pre-wipe-delay');
|
|
3593
|
+
target.style.animation = '';
|
|
3594
|
+
target
|
|
3595
|
+
.querySelectorAll('.pre-wipe-lead')
|
|
3596
|
+
.forEach(element => AmLyrics.clearPreWipeLead(element));
|
|
3597
|
+
}
|
|
3598
|
+
static clearPreWipeLead(element) {
|
|
3599
|
+
element.classList.remove('pre-wipe-lead');
|
|
3600
|
+
element.style.removeProperty('--pre-wipe-duration');
|
|
3601
|
+
element.style.removeProperty('--pre-wipe-delay');
|
|
3602
|
+
}
|
|
3603
|
+
static hasTextBoundaryAfter(syllable) {
|
|
3604
|
+
return /\s$/.test(syllable.textContent || '');
|
|
3605
|
+
}
|
|
3606
|
+
static getSyllableWordIndex(syllable) {
|
|
3607
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3608
|
+
const virtualWordId = wordElement?.dataset.virtualWordId;
|
|
3609
|
+
if (virtualWordId) {
|
|
3610
|
+
return `virtual:${virtualWordId}`;
|
|
3611
|
+
}
|
|
3612
|
+
const virtualWordStart = wordElement?.dataset.virtualWordStart;
|
|
3613
|
+
const virtualWordEnd = wordElement?.dataset.virtualWordEnd;
|
|
3614
|
+
if (virtualWordStart || virtualWordEnd) {
|
|
3615
|
+
return `virtual:${virtualWordStart || ''}:${virtualWordEnd || ''}`;
|
|
3616
|
+
}
|
|
3617
|
+
return (syllable.getAttribute('data-word-index') ||
|
|
3618
|
+
syllable.getAttribute('data-syllable-index') ||
|
|
3619
|
+
'');
|
|
3620
|
+
}
|
|
3621
|
+
static getNextWordSyllable(syllables, index) {
|
|
3622
|
+
const current = syllables[index];
|
|
3623
|
+
const currentWordIndex = AmLyrics.getSyllableWordIndex(current);
|
|
3624
|
+
const previousSyllable = current;
|
|
3625
|
+
for (let i = index + 1; i < syllables.length; i += 1) {
|
|
3626
|
+
const candidate = syllables[i];
|
|
3627
|
+
if (candidate.classList.contains('transliteration')) {
|
|
3628
|
+
// eslint-disable-next-line no-continue
|
|
3629
|
+
continue;
|
|
3630
|
+
}
|
|
3631
|
+
const candidateWordIndex = AmLyrics.getSyllableWordIndex(candidate);
|
|
3632
|
+
if (candidateWordIndex === currentWordIndex ||
|
|
3633
|
+
!AmLyrics.hasTextBoundaryAfter(previousSyllable)) {
|
|
3634
|
+
return null;
|
|
3635
|
+
}
|
|
3636
|
+
return candidate;
|
|
3637
|
+
}
|
|
3638
|
+
return null;
|
|
3639
|
+
}
|
|
3640
|
+
static getPreviousNonTransliterationSyllable(syllables, index) {
|
|
3641
|
+
for (let i = index - 1; i >= 0; i -= 1) {
|
|
3642
|
+
const candidate = syllables[i];
|
|
3643
|
+
if (!candidate.classList.contains('transliteration')) {
|
|
3644
|
+
return candidate;
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
return null;
|
|
3648
|
+
}
|
|
3649
|
+
static getRenderedWordSyllables(syllable) {
|
|
3650
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3651
|
+
const wordElements = AmLyrics.getCachedVirtualWordElements(wordElement);
|
|
3652
|
+
const wordSyllables = wordElements.flatMap(element => Array.from(element.querySelectorAll('.lyrics-syllable')));
|
|
3653
|
+
return wordSyllables.filter(wordSyllable => !wordSyllable.classList.contains('transliteration'));
|
|
3654
|
+
}
|
|
3655
|
+
static getWordElementForSyllable(syllable) {
|
|
3656
|
+
return syllable.parentElement?.parentElement;
|
|
3657
|
+
}
|
|
3658
|
+
static getWordPreWipeKey(syllable) {
|
|
3659
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3660
|
+
return (wordElement?.dataset.virtualWordId ||
|
|
3661
|
+
`${syllable.getAttribute('data-start-time') || ''}:${AmLyrics.getSyllableWordIndex(syllable)}`);
|
|
3662
|
+
}
|
|
3663
|
+
static isPreWipeArmed(syllable) {
|
|
3664
|
+
const wordElement = AmLyrics.getWordElementForSyllable(syllable);
|
|
3665
|
+
const target = wordElement;
|
|
3666
|
+
return Boolean(target?._wordPreWipeKey === AmLyrics.getWordPreWipeKey(syllable));
|
|
3667
|
+
}
|
|
3668
|
+
static applyWordPreWipe(nextSyllable, wordSyllables, currentTimeMs, preWipeStartMs, preWipeDurationMs) {
|
|
3669
|
+
if (AmLyrics.isPreWipeArmed(nextSyllable))
|
|
3670
|
+
return;
|
|
3671
|
+
const wordElement = AmLyrics.getWordElementForSyllable(nextSyllable);
|
|
3672
|
+
const wordElements = AmLyrics.getCachedVirtualWordElements(wordElement);
|
|
3673
|
+
const charSpans = AmLyrics.getCachedVirtualWordCharSpans(wordElement, []);
|
|
3674
|
+
const elapsedPreWipe = currentTimeMs - preWipeStartMs;
|
|
3675
|
+
const charCount = charSpans.length ||
|
|
3676
|
+
wordSyllables.reduce((total, wordSyllable) => total + AmLyrics.getVisibleCharacterCount(wordSyllable), 0) ||
|
|
3677
|
+
AmLyrics.getVisibleCharacterCount(nextSyllable);
|
|
3678
|
+
AmLyrics.ensureWordWipeGeometry(charSpans, charCount);
|
|
3679
|
+
const leadChar = charSpans[0];
|
|
3680
|
+
const leadCharSyllable = leadChar?.closest('.lyrics-syllable');
|
|
3681
|
+
const preWipeSyllable = leadCharSyllable || wordSyllables[0] || nextSyllable;
|
|
3682
|
+
AmLyrics.applyWipeShape(preWipeSyllable, charCount);
|
|
3683
|
+
preWipeSyllable.style.setProperty('--pre-wipe-duration', `${preWipeDurationMs}ms`);
|
|
3684
|
+
preWipeSyllable.style.setProperty('--pre-wipe-delay', `${-elapsedPreWipe}ms`);
|
|
3685
|
+
preWipeSyllable.classList.add('pre-highlight');
|
|
3686
|
+
// Character-animated words still need a single word-leading gradient.
|
|
3687
|
+
// Giving every glyph this state creates one gradient per character and
|
|
3688
|
+
// makes the whole word enter the pre-wipe continuation simultaneously.
|
|
3689
|
+
if (leadChar) {
|
|
3690
|
+
AmLyrics.applyWipeShape(leadChar, charCount);
|
|
3691
|
+
leadChar.style.setProperty('--pre-wipe-duration', `${preWipeDurationMs}ms`);
|
|
3692
|
+
leadChar.style.setProperty('--pre-wipe-delay', `${-elapsedPreWipe}ms`);
|
|
3693
|
+
leadChar.classList.add('pre-wipe-lead');
|
|
3694
|
+
}
|
|
3695
|
+
wordElements.forEach(element => {
|
|
3696
|
+
const target = element;
|
|
3697
|
+
target._wordPreWipeKey = AmLyrics.getWordPreWipeKey(nextSyllable);
|
|
3698
|
+
});
|
|
3699
|
+
}
|
|
3700
|
+
static maybePreWipeNextWord(syllables, index, currentTimeMs, currentEndTimeMs) {
|
|
3701
|
+
const syllable = syllables[index];
|
|
3702
|
+
if (syllable.classList.contains('line-synced') ||
|
|
3703
|
+
syllable.classList.contains('transliteration') ||
|
|
3704
|
+
syllable.closest('.lyrics-gap')) {
|
|
3705
|
+
return;
|
|
3706
|
+
}
|
|
3707
|
+
const currentWordReady = syllable.classList.contains('finished') ||
|
|
3708
|
+
currentTimeMs >= currentEndTimeMs - WORD_PRE_WIPE_HANDOFF_LEAD_MS;
|
|
3709
|
+
if (!currentWordReady) {
|
|
3710
|
+
return;
|
|
3711
|
+
}
|
|
3712
|
+
const nextSyllable = AmLyrics.getNextWordSyllable(syllables, index);
|
|
3713
|
+
if (!nextSyllable ||
|
|
3714
|
+
nextSyllable.classList.contains('line-synced') ||
|
|
3715
|
+
nextSyllable.classList.contains('transliteration') ||
|
|
3716
|
+
nextSyllable.closest('.lyrics-gap') ||
|
|
3717
|
+
nextSyllable.classList.contains('highlight') ||
|
|
3718
|
+
nextSyllable.classList.contains('finished')) {
|
|
3719
|
+
return;
|
|
3720
|
+
}
|
|
3721
|
+
const nextStartTimeMs = nextSyllable._cachedStartTime;
|
|
3722
|
+
if (!Number.isFinite(nextStartTimeMs))
|
|
3723
|
+
return;
|
|
3724
|
+
const gapMs = nextStartTimeMs - currentEndTimeMs;
|
|
3725
|
+
if (gapMs > NEXT_WORD_PRE_WIPE_MAX_GAP_MS || gapMs < -50) {
|
|
3726
|
+
return;
|
|
3727
|
+
}
|
|
3728
|
+
const nextWordSyllables = AmLyrics.getRenderedWordSyllables(nextSyllable);
|
|
3729
|
+
const preWipeSyllables = nextWordSyllables.length > 0 ? nextWordSyllables : [nextSyllable];
|
|
3730
|
+
const wordElement = AmLyrics.getWordElementForSyllable(nextSyllable);
|
|
3731
|
+
const wordCharSpans = AmLyrics.getCachedVirtualWordCharSpans(wordElement, []);
|
|
3732
|
+
const charCount = wordCharSpans.length ||
|
|
3733
|
+
preWipeSyllables.reduce((total, wordSyllable) => total + AmLyrics.getVisibleCharacterCount(wordSyllable), 0);
|
|
3734
|
+
if (charCount <= 0)
|
|
3735
|
+
return;
|
|
3736
|
+
const preWipeDuration = AmLyrics.clamp(64 + charCount * 9, NEXT_WORD_PRE_WIPE_MIN_DURATION_MS, NEXT_WORD_PRE_WIPE_MAX_DURATION_MS);
|
|
3737
|
+
const preWipeStart = Math.max(nextStartTimeMs - preWipeDuration, currentEndTimeMs - WORD_PRE_WIPE_HANDOFF_LEAD_MS);
|
|
3738
|
+
if (currentTimeMs < preWipeStart || currentTimeMs >= nextStartTimeMs) {
|
|
3739
|
+
return;
|
|
3740
|
+
}
|
|
3741
|
+
AmLyrics.applyWordPreWipe(nextSyllable, preWipeSyllables, currentTimeMs, preWipeStart, preWipeDuration);
|
|
3742
|
+
}
|
|
3743
|
+
static getCachedCharSpans(element) {
|
|
3744
|
+
const cacheTarget = element;
|
|
3745
|
+
if (!cacheTarget._cachedCharSpans) {
|
|
3746
|
+
cacheTarget._cachedCharSpans = Array.from(element.querySelectorAll('span.char'));
|
|
3747
|
+
}
|
|
3748
|
+
return cacheTarget._cachedCharSpans;
|
|
3749
|
+
}
|
|
3750
|
+
static getCachedVirtualWordElements(wordElement) {
|
|
3751
|
+
if (!wordElement)
|
|
3752
|
+
return [];
|
|
3753
|
+
const cacheTarget = wordElement;
|
|
3754
|
+
if (cacheTarget._cachedVirtualWordElements) {
|
|
3755
|
+
return cacheTarget._cachedVirtualWordElements;
|
|
3756
|
+
}
|
|
3757
|
+
const { virtualWordId } = wordElement.dataset;
|
|
3758
|
+
let wordElements = [wordElement];
|
|
3759
|
+
if (virtualWordId && wordElement.parentElement) {
|
|
3760
|
+
wordElements = Array.from(wordElement.parentElement.querySelectorAll('.lyrics-word')).filter(el => el.dataset.virtualWordId === virtualWordId);
|
|
3761
|
+
}
|
|
3762
|
+
wordElements.forEach(element => {
|
|
3763
|
+
const target = element;
|
|
3764
|
+
target._cachedVirtualWordElements = wordElements;
|
|
3765
|
+
});
|
|
3766
|
+
return wordElements;
|
|
3767
|
+
}
|
|
3768
|
+
static getCachedVirtualWordCharSpans(wordElement, fallbackCharSpans) {
|
|
3769
|
+
if (!wordElement)
|
|
3770
|
+
return fallbackCharSpans;
|
|
3771
|
+
const cacheTarget = wordElement;
|
|
3772
|
+
if (cacheTarget._cachedVirtualWordCharSpans) {
|
|
3773
|
+
return cacheTarget._cachedVirtualWordCharSpans;
|
|
3774
|
+
}
|
|
3775
|
+
const wordElements = AmLyrics.getCachedVirtualWordElements(wordElement);
|
|
3776
|
+
const charSpans = wordElements.flatMap(word => Array.from(word.querySelectorAll('span.char')));
|
|
3777
|
+
const result = charSpans.length > 0 ? charSpans : fallbackCharSpans;
|
|
3778
|
+
wordElements.forEach(element => {
|
|
3779
|
+
const target = element;
|
|
3780
|
+
target._cachedVirtualWordCharSpans = result;
|
|
3781
|
+
});
|
|
3782
|
+
return result;
|
|
3783
|
+
}
|
|
3343
3784
|
static updateSyllableAnimation(syllable, elapsedTimeMs = 0) {
|
|
3344
3785
|
if (syllable.classList.contains('highlight'))
|
|
3345
3786
|
return;
|
|
3346
3787
|
const { classList } = syllable;
|
|
3788
|
+
const hadPreHighlight = classList.contains('pre-highlight');
|
|
3347
3789
|
const isRTL = classList.contains('rtl-text');
|
|
3348
|
-
const charSpans =
|
|
3790
|
+
const charSpans = AmLyrics.getCachedCharSpans(syllable);
|
|
3349
3791
|
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');
|
|
3792
|
+
const typedWordElement = wordElement;
|
|
3793
|
+
const allWordElements = AmLyrics.getCachedVirtualWordElements(typedWordElement);
|
|
3794
|
+
const allWordCharSpans = AmLyrics.getCachedVirtualWordCharSpans(typedWordElement, charSpans);
|
|
3795
|
+
const isGrowable = typedWordElement?.classList.contains('growable');
|
|
3796
|
+
const isCharRise = typedWordElement?.classList.contains('char-rise');
|
|
3797
|
+
const isCharDrag = typedWordElement?.classList.contains('char-drag');
|
|
3362
3798
|
const isFirstSyllable = syllable.getAttribute('data-syllable-index') === '0';
|
|
3363
3799
|
const syllableStartMs = parseFloat(syllable.getAttribute('data-start-time') || '0');
|
|
3364
|
-
const virtualWordStartMs = parseFloat(
|
|
3800
|
+
const virtualWordStartMs = parseFloat(typedWordElement?.dataset.virtualWordStart || '');
|
|
3365
3801
|
const isFirstInVirtualWord = isFirstSyllable &&
|
|
3366
3802
|
(!Number.isFinite(virtualWordStartMs) ||
|
|
3367
3803
|
Math.abs(syllableStartMs - virtualWordStartMs) < 0.5);
|
|
@@ -3372,7 +3808,10 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3372
3808
|
const wordDurationMs = parseFloat(syllable.getAttribute('data-word-duration') ||
|
|
3373
3809
|
syllable.getAttribute('data-duration') ||
|
|
3374
3810
|
'0') || syllableDurationMs;
|
|
3375
|
-
|
|
3811
|
+
const wordElapsedTimeMs = Number.isFinite(virtualWordStartMs)
|
|
3812
|
+
? elapsedTimeMs + (syllableStartMs - virtualWordStartMs)
|
|
3813
|
+
: elapsedTimeMs;
|
|
3814
|
+
const charWipeDurationMs = Math.max(wordDurationMs, syllableDurationMs);
|
|
3376
3815
|
const charAnimationsMap = new Map();
|
|
3377
3816
|
const styleUpdates = [];
|
|
3378
3817
|
// Step 1: Grow Pass
|
|
@@ -3420,41 +3859,81 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3420
3859
|
charAnimationsMap.set(span, `rise-char ${riseDurationMs}ms ease-in-out ${riseDelay}ms forwards`);
|
|
3421
3860
|
});
|
|
3422
3861
|
}
|
|
3862
|
+
if (isCharDrag && isFirstInVirtualWord && allWordCharSpans.length > 0) {
|
|
3863
|
+
const finalDuration = Math.max(wordDurationMs, syllableDurationMs);
|
|
3864
|
+
const baseDelayPerChar = AmLyrics.clamp(finalDuration * 0.15, 64, 118);
|
|
3865
|
+
const dragDurationMs = AmLyrics.clamp(finalDuration * 0.82, 560, 900);
|
|
3866
|
+
allWordCharSpans.forEach(span => {
|
|
3867
|
+
const charIndex = parseFloat(span.dataset.syllableCharIndex || '0');
|
|
3868
|
+
const dragDelay = baseDelayPerChar * charIndex;
|
|
3869
|
+
charAnimationsMap.set(span, `drag-char ${dragDurationMs}ms ease ${dragDelay}ms forwards`);
|
|
3870
|
+
});
|
|
3871
|
+
}
|
|
3423
3872
|
// Step 2: Wipe Pass
|
|
3424
3873
|
if (charSpans.length > 0) {
|
|
3425
|
-
|
|
3874
|
+
const wipeCharCount = allWordCharSpans.length ||
|
|
3875
|
+
charSpans.length ||
|
|
3876
|
+
AmLyrics.getVisibleCharacterCount(syllable);
|
|
3877
|
+
const wipeScale = AmLyrics.getLongWordWipeScale(wipeCharCount);
|
|
3878
|
+
AmLyrics.applyWipeShape(syllable, wipeCharCount);
|
|
3879
|
+
AmLyrics.ensureWordWipeGeometry(allWordCharSpans, wipeCharCount);
|
|
3880
|
+
allWordCharSpans.forEach(span => AmLyrics.applyWipeShape(span, wipeCharCount));
|
|
3881
|
+
const hasWordLevelWipe = !isFirstInVirtualWord &&
|
|
3882
|
+
(Boolean(typedWordElement?._wordWipeStarted) ||
|
|
3883
|
+
allWordCharSpans.some(span => span.style.animation.includes('wipe')));
|
|
3884
|
+
let charSpansToAnimate = charSpans;
|
|
3885
|
+
if (isFirstInVirtualWord) {
|
|
3886
|
+
charSpansToAnimate = allWordCharSpans;
|
|
3887
|
+
}
|
|
3888
|
+
else if (hasWordLevelWipe) {
|
|
3889
|
+
charSpansToAnimate = [];
|
|
3890
|
+
}
|
|
3891
|
+
if (charSpansToAnimate.length > 0 && allWordElements.length > 0) {
|
|
3892
|
+
allWordElements.forEach(element => {
|
|
3893
|
+
const target = element;
|
|
3894
|
+
target._wordWipeStarted = true;
|
|
3895
|
+
target._wordPreWipeKey = undefined;
|
|
3896
|
+
});
|
|
3897
|
+
}
|
|
3898
|
+
charSpansToAnimate.forEach((span, charIndex) => {
|
|
3426
3899
|
const startPct = parseFloat(span.dataset.wipeStart || '0');
|
|
3427
3900
|
const durationPct = parseFloat(span.dataset.wipeDuration || '0');
|
|
3428
|
-
const
|
|
3429
|
-
const
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3901
|
+
const globalCharIndex = parseFloat(span.dataset.syllableCharIndex || `${charIndex}`);
|
|
3902
|
+
const hadCharPreWipe = span.classList.contains('pre-wipe-lead') ||
|
|
3903
|
+
(hadPreHighlight && globalCharIndex === 0);
|
|
3904
|
+
const charStartMs = charWipeDurationMs * startPct;
|
|
3905
|
+
const remainingWordWipeMs = Math.max(0, charWipeDurationMs - charStartMs);
|
|
3906
|
+
const wipeDelay = charStartMs - wordElapsedTimeMs;
|
|
3907
|
+
const wipeDuration = Math.min(charWipeDurationMs * durationPct * wipeScale, remainingWordWipeMs);
|
|
3908
|
+
const useStartAnimation = isFirstInContainer && globalCharIndex === 0 && !hadCharPreWipe;
|
|
3909
|
+
let charWipeAnimation = 'char-wipe';
|
|
3910
|
+
if (hadCharPreWipe) {
|
|
3911
|
+
charWipeAnimation = 'char-wipe';
|
|
3434
3912
|
}
|
|
3435
|
-
else {
|
|
3436
|
-
charWipeAnimation =
|
|
3913
|
+
else if (useStartAnimation) {
|
|
3914
|
+
charWipeAnimation = 'char-start-wipe';
|
|
3437
3915
|
}
|
|
3438
3916
|
const existingAnimation = charAnimationsMap.get(span) || span.style.animation || '';
|
|
3439
3917
|
const animationParts = [];
|
|
3440
3918
|
if (existingAnimation &&
|
|
3441
3919
|
(existingAnimation.includes('grow-dynamic') ||
|
|
3442
|
-
existingAnimation.includes('rise-char')
|
|
3920
|
+
existingAnimation.includes('rise-char') ||
|
|
3921
|
+
existingAnimation.includes('drag-char'))) {
|
|
3443
3922
|
animationParts.push(existingAnimation.split(',')[0].trim());
|
|
3444
3923
|
}
|
|
3445
|
-
if (
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
const
|
|
3450
|
-
const preWipeDuration = Math.min(
|
|
3451
|
-
const animDelay = arrivalTime - preWipeDuration;
|
|
3924
|
+
if (globalCharIndex > 0 &&
|
|
3925
|
+
!hadCharPreWipe &&
|
|
3926
|
+
wipeDelay > 0 &&
|
|
3927
|
+
wipeDuration > 0) {
|
|
3928
|
+
const measuredDuration = Number.parseFloat(span.dataset.preWipeDuration || '100');
|
|
3929
|
+
const preWipeDuration = Math.min(measuredDuration, wipeDuration * 0.9, charWipeDurationMs * 0.08, wipeDelay);
|
|
3452
3930
|
if (preWipeDuration >= 16) {
|
|
3453
|
-
animationParts.push(`pre-wipe
|
|
3931
|
+
animationParts.push(`char-pre-wipe ${preWipeDuration}ms linear ${wipeDelay - preWipeDuration}ms none`);
|
|
3454
3932
|
}
|
|
3455
3933
|
}
|
|
3456
3934
|
if (wipeDuration > 0) {
|
|
3457
|
-
|
|
3935
|
+
const wipeFillMode = hadCharPreWipe ? 'both' : 'forwards';
|
|
3936
|
+
animationParts.push(`${charWipeAnimation} ${wipeDuration}ms linear ${wipeDelay}ms ${wipeFillMode}`);
|
|
3458
3937
|
}
|
|
3459
3938
|
if (animationParts.length > 0) {
|
|
3460
3939
|
charAnimationsMap.set(span, animationParts.join(', '));
|
|
@@ -3464,9 +3943,15 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3464
3943
|
else {
|
|
3465
3944
|
// Syllable-level wipe for regular (non-growable) words without chars
|
|
3466
3945
|
const wipeRatio = parseFloat(syllable.getAttribute('data-wipe-ratio') || '1');
|
|
3467
|
-
const
|
|
3946
|
+
const wipeCharCount = AmLyrics.getVisibleCharacterCount(syllable);
|
|
3947
|
+
const wipeScale = AmLyrics.getLongWordWipeScale(wipeCharCount);
|
|
3948
|
+
const visualDuration = syllableDurationMs * wipeRatio * wipeScale;
|
|
3949
|
+
AmLyrics.applyWipeShape(syllable, wipeCharCount);
|
|
3468
3950
|
let wipeAnimation = 'wipe';
|
|
3469
|
-
if (
|
|
3951
|
+
if (hadPreHighlight) {
|
|
3952
|
+
wipeAnimation = isRTL ? 'wipe-from-pre-rtl' : 'wipe-from-pre';
|
|
3953
|
+
}
|
|
3954
|
+
else if (isFirstInContainer) {
|
|
3470
3955
|
wipeAnimation = isRTL ? 'start-wipe-rtl' : 'start-wipe';
|
|
3471
3956
|
}
|
|
3472
3957
|
else {
|
|
@@ -3479,16 +3964,25 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3479
3964
|
syllable.style.animation = `${currentWipeAnimation} ${visualDuration}ms ${isGap ? 'ease-out' : 'linear'} ${-elapsedTimeMs}ms forwards`;
|
|
3480
3965
|
}
|
|
3481
3966
|
// --- WRITE PHASE ---
|
|
3967
|
+
if (allWordElements.length > 0) {
|
|
3968
|
+
allWordElements.forEach(element => {
|
|
3969
|
+
const target = element;
|
|
3970
|
+
target._wordPreWipeKey = undefined;
|
|
3971
|
+
});
|
|
3972
|
+
}
|
|
3482
3973
|
classList.remove('pre-highlight');
|
|
3483
3974
|
classList.add('highlight');
|
|
3975
|
+
allWordCharSpans.forEach(span => AmLyrics.clearPreWipeLead(span));
|
|
3976
|
+
// Apply keyframe variables before assigning animation strings so the
|
|
3977
|
+
// first painted frame never uses fallback transform values.
|
|
3978
|
+
for (const update of styleUpdates) {
|
|
3979
|
+
update.element.style.setProperty(update.property, update.value);
|
|
3980
|
+
}
|
|
3484
3981
|
for (const [span, animationString] of charAnimationsMap.entries()) {
|
|
3485
3982
|
span.style.willChange = 'transform';
|
|
3983
|
+
span.style.removeProperty('background-color');
|
|
3486
3984
|
span.style.animation = animationString;
|
|
3487
3985
|
}
|
|
3488
|
-
// Apply style updates
|
|
3489
|
-
for (const update of styleUpdates) {
|
|
3490
|
-
update.element.style.setProperty(update.property, update.value);
|
|
3491
|
-
}
|
|
3492
3986
|
}
|
|
3493
3987
|
/**
|
|
3494
3988
|
* Reset syllable animation state
|
|
@@ -3512,10 +4006,19 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3512
4006
|
el.style.animation = '';
|
|
3513
4007
|
el.style.transition = 'none';
|
|
3514
4008
|
el.style.backgroundColor = 'var(--lyplus-text-secondary)';
|
|
4009
|
+
AmLyrics.clearPreWipeLead(el);
|
|
3515
4010
|
}
|
|
3516
4011
|
// Immediately remove all state classes
|
|
3517
4012
|
syllable.classList.remove('highlight', 'finished', 'pre-highlight', 'cleanup');
|
|
3518
4013
|
}
|
|
4014
|
+
static resetWordAnimationState(line) {
|
|
4015
|
+
const wordElements = line.querySelectorAll('.lyrics-word');
|
|
4016
|
+
wordElements.forEach(wordElement => {
|
|
4017
|
+
const target = wordElement;
|
|
4018
|
+
target._wordPreWipeKey = undefined;
|
|
4019
|
+
target._wordWipeStarted = false;
|
|
4020
|
+
});
|
|
4021
|
+
}
|
|
3519
4022
|
/**
|
|
3520
4023
|
* Reset all syllables in a line — batches deferred cleanup into a single rAF
|
|
3521
4024
|
*/
|
|
@@ -3523,6 +4026,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3523
4026
|
if (!line)
|
|
3524
4027
|
return;
|
|
3525
4028
|
line.classList.remove('persist-highlight');
|
|
4029
|
+
AmLyrics.resetWordAnimationState(line);
|
|
3526
4030
|
// eslint-disable-next-line no-param-reassign
|
|
3527
4031
|
line._cachedSyllableElements = null;
|
|
3528
4032
|
const syllables = line.getElementsByClassName('lyrics-syllable');
|
|
@@ -3554,6 +4058,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3554
4058
|
if (!line)
|
|
3555
4059
|
return;
|
|
3556
4060
|
line.classList.remove('persist-highlight');
|
|
4061
|
+
AmLyrics.resetWordAnimationState(line);
|
|
3557
4062
|
const syllables = line.getElementsByClassName('lyrics-syllable');
|
|
3558
4063
|
for (let i = 0; i < syllables.length; i += 1) {
|
|
3559
4064
|
const s = syllables[i];
|
|
@@ -3571,6 +4076,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3571
4076
|
el.style.removeProperty('background-color');
|
|
3572
4077
|
el.style.removeProperty('transition');
|
|
3573
4078
|
el.style.removeProperty('filter');
|
|
4079
|
+
AmLyrics.clearPreWipeLead(el);
|
|
3574
4080
|
}
|
|
3575
4081
|
}
|
|
3576
4082
|
}
|
|
@@ -3607,19 +4113,26 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3607
4113
|
syllable.style.animation = '';
|
|
3608
4114
|
syllable.style.removeProperty('--pre-wipe-duration');
|
|
3609
4115
|
syllable.style.removeProperty('--pre-wipe-delay');
|
|
4116
|
+
syllable.style.removeProperty('background-color');
|
|
4117
|
+
AmLyrics.applyWipeShape(syllable, AmLyrics.getVisibleCharacterCount(syllable));
|
|
3610
4118
|
const chars = syllable.querySelectorAll('span.char');
|
|
3611
4119
|
for (let ci = 0; ci < chars.length; ci += 1) {
|
|
3612
4120
|
const charEl = chars[ci];
|
|
3613
4121
|
const currentAnim = charEl.style.animation || '';
|
|
3614
4122
|
if (currentAnim.includes('grow-dynamic') ||
|
|
3615
|
-
currentAnim.includes('rise-char')
|
|
4123
|
+
currentAnim.includes('rise-char') ||
|
|
4124
|
+
currentAnim.includes('drag-char')) {
|
|
3616
4125
|
const parts = currentAnim.split(',').map(p => p.trim());
|
|
3617
|
-
const transformAnim = parts.find(p => p.includes('grow-dynamic') ||
|
|
4126
|
+
const transformAnim = parts.find(p => p.includes('grow-dynamic') ||
|
|
4127
|
+
p.includes('rise-char') ||
|
|
4128
|
+
p.includes('drag-char'));
|
|
3618
4129
|
charEl.style.animation = transformAnim || '';
|
|
3619
4130
|
}
|
|
3620
4131
|
else {
|
|
3621
4132
|
charEl.style.animation = '';
|
|
3622
4133
|
}
|
|
4134
|
+
charEl.style.backgroundColor = 'var(--lyplus-text-primary)';
|
|
4135
|
+
AmLyrics.clearPreWipeLead(charEl);
|
|
3623
4136
|
}
|
|
3624
4137
|
}
|
|
3625
4138
|
}
|
|
@@ -3660,14 +4173,16 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3660
4173
|
// Early exit check
|
|
3661
4174
|
if (!(currentTimeMs < startTime - 1000 && !hasActiveState)) {
|
|
3662
4175
|
let preHighlightReset = false;
|
|
3663
|
-
//
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
4176
|
+
// Before the syllable starts, pre-highlight only belongs beside a
|
|
4177
|
+
// previous active word. Once the syllable starts, updateSyllableAnimation
|
|
4178
|
+
// consumes the class so the actual wipe can continue from the pre-wipe
|
|
4179
|
+
// pose instead of restarting from the beginning.
|
|
4180
|
+
if (hasPreHighlight && currentTimeMs < startTime) {
|
|
4181
|
+
const prevSyllable = AmLyrics.getPreviousNonTransliterationSyllable(syllables, i);
|
|
4182
|
+
const previousCarriesHighlight = prevSyllable?.classList.contains('highlight') ||
|
|
4183
|
+
prevSyllable?.classList.contains('finished');
|
|
4184
|
+
if (!previousCarriesHighlight) {
|
|
4185
|
+
AmLyrics.clearPreHighlight(syllable);
|
|
3671
4186
|
preHighlightReset = true;
|
|
3672
4187
|
}
|
|
3673
4188
|
}
|
|
@@ -3695,6 +4210,7 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
3695
4210
|
// Not yet started
|
|
3696
4211
|
AmLyrics.resetSyllable(syllable);
|
|
3697
4212
|
}
|
|
4213
|
+
AmLyrics.maybePreWipeNextWord(syllables, i, currentTimeMs, endTime);
|
|
3698
4214
|
}
|
|
3699
4215
|
}
|
|
3700
4216
|
}
|
|
@@ -4004,6 +4520,9 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4004
4520
|
data-end-time="${endTimeMs}"
|
|
4005
4521
|
data-duration="${durationMs}"
|
|
4006
4522
|
data-syllable-index="${syllableIndex}"
|
|
4523
|
+
data-word-index="${syllableIndex}"
|
|
4524
|
+
data-word-length="${syllable.text.replace(/\s/g, '')
|
|
4525
|
+
.length}"
|
|
4007
4526
|
data-wipe-ratio="1"
|
|
4008
4527
|
>${syllable.text}</span
|
|
4009
4528
|
>${bgRomanizedText}</span
|
|
@@ -4025,13 +4544,13 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4025
4544
|
const groupGrowable = lineData?.groupGrowable ?? [];
|
|
4026
4545
|
const groupGlowing = lineData?.groupGlowing ?? [];
|
|
4027
4546
|
const groupCharRise = lineData?.groupCharRise ?? [];
|
|
4547
|
+
const groupCharDrag = lineData?.groupCharDrag ?? [];
|
|
4028
4548
|
const vwFullText = lineData?.vwFullText ?? [];
|
|
4029
4549
|
const vwFullDuration = lineData?.vwFullDuration ?? [];
|
|
4030
4550
|
const vwCharOffset = lineData?.vwCharOffset ?? [];
|
|
4031
4551
|
const vwStartMs = lineData?.vwStartMs ?? [];
|
|
4032
4552
|
const vwEndMs = lineData?.vwEndMs ?? [];
|
|
4033
4553
|
const lineIsRTL = lineData?.lineIsRTL ?? false;
|
|
4034
|
-
// Create main vocals using YouLyPlus syllable structure
|
|
4035
4554
|
const mainVocalElement = b `<p
|
|
4036
4555
|
class="main-vocal-container ${lineIsRTL ? 'rtl-text' : ''}"
|
|
4037
4556
|
>
|
|
@@ -4039,25 +4558,23 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4039
4558
|
const isGrowable = groupGrowable[groupIdx];
|
|
4040
4559
|
const isGlowing = groupGlowing[groupIdx];
|
|
4041
4560
|
const isCharRise = groupCharRise[groupIdx];
|
|
4042
|
-
const
|
|
4561
|
+
const isCharDrag = groupCharDrag[groupIdx];
|
|
4562
|
+
const isAnimatedByChar = isGrowable || isCharRise || isCharDrag;
|
|
4043
4563
|
const groupLineSynced = group.some(s => s.lineSynced);
|
|
4044
4564
|
const wordText = isAnimatedByChar ? vwFullText[groupIdx] : '';
|
|
4045
4565
|
const wordDuration = isAnimatedByChar
|
|
4046
4566
|
? vwFullDuration[groupIdx]
|
|
4047
4567
|
: 0;
|
|
4048
|
-
const wordNumChars = wordText.length;
|
|
4568
|
+
const wordNumChars = wordText.replace(/\s/g, '').length;
|
|
4049
4569
|
const groupCharOffset = isAnimatedByChar
|
|
4050
4570
|
? vwCharOffset[groupIdx]
|
|
4051
4571
|
: 0;
|
|
4052
|
-
const virtualWordId =
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
const virtualWordStart = isAnimatedByChar
|
|
4056
|
-
? vwStartMs[groupIdx]
|
|
4057
|
-
: '';
|
|
4058
|
-
const virtualWordEnd = isAnimatedByChar ? vwEndMs[groupIdx] : '';
|
|
4572
|
+
const virtualWordId = `${lineIndex}:${vwStartMs[groupIdx]}:${vwEndMs[groupIdx]}`;
|
|
4573
|
+
const virtualWordStart = vwStartMs[groupIdx];
|
|
4574
|
+
const virtualWordEnd = vwEndMs[groupIdx];
|
|
4059
4575
|
let sylCharAccumulator = 0;
|
|
4060
4576
|
const groupText = group.map(s => s.text).join('');
|
|
4577
|
+
const visibleWordLength = groupText.replace(/\s/g, '').length;
|
|
4061
4578
|
const shouldAllowBreak = groupText.trim().length >= 16 ||
|
|
4062
4579
|
/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff\uac00-\ud7af]/.test(groupText);
|
|
4063
4580
|
// Calculate dynamic rise duration based on the audio duration of the word
|
|
@@ -4069,9 +4586,9 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4069
4586
|
return b `<span
|
|
4070
4587
|
class="lyrics-word${isGrowable ? ' growable' : ''}${isCharRise
|
|
4071
4588
|
? ' char-rise'
|
|
4072
|
-
: ''}${
|
|
4073
|
-
? '
|
|
4074
|
-
: ''}"
|
|
4589
|
+
: ''}${isCharDrag ? ' char-drag' : ''}${isGlowing
|
|
4590
|
+
? ' glowing'
|
|
4591
|
+
: ''}${shouldAllowBreak ? ' allow-break' : ''}"
|
|
4075
4592
|
data-virtual-word-id="${virtualWordId}"
|
|
4076
4593
|
data-virtual-word-start="${virtualWordStart}"
|
|
4077
4594
|
data-virtual-word-end="${virtualWordEnd}"
|
|
@@ -4098,13 +4615,26 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4098
4615
|
: '';
|
|
4099
4616
|
let syllableContent = text;
|
|
4100
4617
|
if (isAnimatedByChar) {
|
|
4101
|
-
let charIndexInsideSyllable = 0;
|
|
4102
4618
|
const numCharsInSyllable = text.replace(/\s/g, '').length || 1;
|
|
4619
|
+
const hasVirtualTiming = wordDuration > 0 && Number.isFinite(virtualWordStart);
|
|
4620
|
+
const syllableStartRatio = hasVirtualTiming
|
|
4621
|
+
? AmLyrics.clamp((startTimeMs - virtualWordStart) / wordDuration, 0, 1)
|
|
4622
|
+
: 0;
|
|
4623
|
+
const syllableDurationRatio = hasVirtualTiming
|
|
4624
|
+
? AmLyrics.clamp(durationMs / wordDuration, 0, 1)
|
|
4625
|
+
: 1;
|
|
4626
|
+
let charIndexInsideSyllable = 0;
|
|
4103
4627
|
syllableContent = b `${text.split('').map(char => {
|
|
4104
4628
|
if (char === ' ')
|
|
4105
4629
|
return ' ';
|
|
4106
4630
|
const charIndexInsideWord = groupCharOffset + sylCharAccumulator;
|
|
4107
|
-
const
|
|
4631
|
+
const localCharIndex = charIndexInsideSyllable;
|
|
4632
|
+
const visibleWordChars = Math.max(1, wordNumChars);
|
|
4633
|
+
const charStartPercentVal = AmLyrics.clamp(syllableStartRatio +
|
|
4634
|
+
(localCharIndex / numCharsInSyllable) *
|
|
4635
|
+
syllableDurationRatio, 0, 1);
|
|
4636
|
+
const charDurationPercentVal = syllableDurationRatio / numCharsInSyllable ||
|
|
4637
|
+
1 / visibleWordChars;
|
|
4108
4638
|
sylCharAccumulator += 1;
|
|
4109
4639
|
charIndexInsideSyllable += 1;
|
|
4110
4640
|
const minDuration = 400;
|
|
@@ -4151,21 +4681,37 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4151
4681
|
const normalizedGrowth = (charMaxScale - 1.0) / 0.1;
|
|
4152
4682
|
const effectiveDuration = (wordDuration + durationMs * 2) / 3;
|
|
4153
4683
|
const peakMultiplier = Math.min(1, Math.max(0.3, effectiveDuration / 2000));
|
|
4154
|
-
const
|
|
4684
|
+
const baseTranslateYPeak = -normalizedGrowth * (2 * peakMultiplier); // Further dampened lift peak
|
|
4155
4685
|
const position = (charIndexInsideWord + 0.5) / wordNumChars;
|
|
4156
4686
|
const horizontalOffset = (position - 0.5) * 2 * ((charMaxScale - 1.0) * 25);
|
|
4687
|
+
const isDragMotion = isCharDrag;
|
|
4688
|
+
let charTranslateYPeak = baseTranslateYPeak;
|
|
4689
|
+
if (isCharRise) {
|
|
4690
|
+
charTranslateYPeak = 0;
|
|
4691
|
+
}
|
|
4692
|
+
else if (isDragMotion) {
|
|
4693
|
+
charTranslateYPeak = -0.78;
|
|
4694
|
+
}
|
|
4695
|
+
let motionHorizontalOffset = horizontalOffset;
|
|
4696
|
+
if (isCharRise) {
|
|
4697
|
+
motionHorizontalOffset = 0;
|
|
4698
|
+
}
|
|
4699
|
+
else if (isDragMotion) {
|
|
4700
|
+
motionHorizontalOffset = 0;
|
|
4701
|
+
}
|
|
4157
4702
|
return b `<span
|
|
4158
4703
|
class="char"
|
|
4159
4704
|
data-char-index="${charIndexInsideWord}"
|
|
4160
4705
|
data-syllable-char-index="${charIndexInsideWord}"
|
|
4161
4706
|
data-wipe-start="${charStartPercentVal.toFixed(4)}"
|
|
4162
|
-
data-wipe-duration="${
|
|
4707
|
+
data-wipe-duration="${charDurationPercentVal.toFixed(4)}"
|
|
4163
4708
|
data-horizontal-offset="${horizontalOffset.toFixed(2)}"
|
|
4164
4709
|
data-max-scale="${charMaxScale.toFixed(3)}"
|
|
4165
4710
|
data-matrix-scale="${(charMaxScale * 0.98).toFixed(3)}"
|
|
4166
|
-
data-char-offset-x="${(
|
|
4711
|
+
data-char-offset-x="${(motionHorizontalOffset * 0.98).toFixed(2)}"
|
|
4167
4712
|
data-shadow-intensity="${charShadowIntensity.toFixed(3)}"
|
|
4168
4713
|
data-translate-y-peak="${charTranslateYPeak.toFixed(3)}"
|
|
4714
|
+
style="--word-wipe-width: ${visibleWordChars}ch; --char-wipe-position: -${charIndexInsideWord}ch"
|
|
4169
4715
|
>${char}</span
|
|
4170
4716
|
>`;
|
|
4171
4717
|
})}`;
|
|
@@ -4183,6 +4729,8 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4183
4729
|
data-duration="${durationMs}"
|
|
4184
4730
|
data-word-duration="${wordDuration}"
|
|
4185
4731
|
data-syllable-index="${sylIdx}"
|
|
4732
|
+
data-word-index="${groupIdx}"
|
|
4733
|
+
data-word-length="${visibleWordLength}"
|
|
4186
4734
|
data-wipe-ratio="1"
|
|
4187
4735
|
>${syllableContent}</span
|
|
4188
4736
|
>${romanizedText}</span
|
|
@@ -4415,13 +4963,14 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4415
4963
|
<button
|
|
4416
4964
|
class="download-button source-switch-btn"
|
|
4417
4965
|
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
4966
|
@click=${this.switchSource}
|
|
4420
4967
|
?disabled=${this.isFetchingAlternatives}
|
|
4421
4968
|
>
|
|
4422
4969
|
<svg
|
|
4423
|
-
class="source-switch-svg lucide lucide-arrow-down-up-icon lucide-arrow-down-up
|
|
4424
|
-
|
|
4970
|
+
class="source-switch-svg lucide lucide-arrow-down-up-icon lucide-arrow-down-up ${this
|
|
4971
|
+
.isFetchingAlternatives
|
|
4972
|
+
? 'is-loading'
|
|
4973
|
+
: ''}"
|
|
4425
4974
|
xmlns="http://www.w3.org/2000/svg"
|
|
4426
4975
|
width="12"
|
|
4427
4976
|
height="12"
|
|
@@ -4479,9 +5028,6 @@ let AmLyrics$1 = class AmLyrics extends i {
|
|
|
4479
5028
|
}
|
|
4480
5029
|
};
|
|
4481
5030
|
AmLyrics$1.styles = i$3 `
|
|
4482
|
-
/* ==========================================================================
|
|
4483
|
-
YOULYPLUS-INSPIRED STYLING - Design Tokens & Variables
|
|
4484
|
-
========================================================================== */
|
|
4485
5031
|
:host {
|
|
4486
5032
|
--lyplus-lyrics-palette: var(
|
|
4487
5033
|
--am-lyrics-highlight-color,
|
|
@@ -4510,6 +5056,8 @@ AmLyrics$1.styles = i$3 `
|
|
|
4510
5056
|
--lyplus-blur-amount: 0.07em;
|
|
4511
5057
|
--lyplus-blur-amount-near: 0.035em;
|
|
4512
5058
|
--lyplus-fade-gap-timing-function: ease-out;
|
|
5059
|
+
--wipe-gradient-width: 0.75em;
|
|
5060
|
+
--wipe-gradient-half: 0.375em;
|
|
4513
5061
|
|
|
4514
5062
|
--lyrics-scroll-padding-top: 25%;
|
|
4515
5063
|
|
|
@@ -4537,6 +5085,9 @@ AmLyrics$1.styles = i$3 `
|
|
|
4537
5085
|
max-height: 100vh;
|
|
4538
5086
|
overflow-y: auto;
|
|
4539
5087
|
-webkit-overflow-scrolling: touch;
|
|
5088
|
+
-webkit-touch-callout: none;
|
|
5089
|
+
-webkit-user-select: none;
|
|
5090
|
+
user-select: none;
|
|
4540
5091
|
box-sizing: border-box;
|
|
4541
5092
|
scrollbar-width: none;
|
|
4542
5093
|
overflow-anchor: none;
|
|
@@ -4666,7 +5217,7 @@ AmLyrics$1.styles = i$3 `
|
|
|
4666
5217
|
.lyrics-line.bg-expanded .background-vocal-container {
|
|
4667
5218
|
max-height: 4em;
|
|
4668
5219
|
opacity: 1;
|
|
4669
|
-
will-change:
|
|
5220
|
+
will-change: opacity;
|
|
4670
5221
|
}
|
|
4671
5222
|
|
|
4672
5223
|
.lyrics-line.bg-expanded .background-vocal-wrap {
|
|
@@ -4683,6 +5234,18 @@ AmLyrics$1.styles = i$3 `
|
|
|
4683
5234
|
opacity: 1;
|
|
4684
5235
|
}
|
|
4685
5236
|
|
|
5237
|
+
/* Predictive scrolling begins before the next timestamp. Start dimming
|
|
5238
|
+
the outgoing line at the same moment so it settles with the scroll. */
|
|
5239
|
+
.lyrics-line.scroll-exiting {
|
|
5240
|
+
opacity: 0.8;
|
|
5241
|
+
color: var(--lyplus-text-secondary);
|
|
5242
|
+
transition:
|
|
5243
|
+
opacity var(--scroll-duration, 400ms) cubic-bezier(0.41, 0, 0.12, 0.99),
|
|
5244
|
+
transform var(--scroll-duration, 400ms)
|
|
5245
|
+
cubic-bezier(0.41, 0, 0.12, 0.99) var(--lyrics-line-delay, 0ms),
|
|
5246
|
+
filter var(--scroll-duration, 400ms) ease;
|
|
5247
|
+
}
|
|
5248
|
+
|
|
4686
5249
|
.lyrics-line.persist-highlight {
|
|
4687
5250
|
filter: none !important;
|
|
4688
5251
|
opacity: 1;
|
|
@@ -4818,11 +5381,22 @@ AmLyrics$1.styles = i$3 `
|
|
|
4818
5381
|
white-space: nowrap;
|
|
4819
5382
|
}
|
|
4820
5383
|
|
|
5384
|
+
.lyrics-word.char-drag {
|
|
5385
|
+
display: inline-block;
|
|
5386
|
+
vertical-align: baseline;
|
|
5387
|
+
white-space: nowrap;
|
|
5388
|
+
}
|
|
5389
|
+
|
|
4821
5390
|
.lyrics-word.char-rise.allow-break {
|
|
4822
5391
|
display: inline;
|
|
4823
5392
|
white-space: normal;
|
|
4824
5393
|
}
|
|
4825
5394
|
|
|
5395
|
+
.lyrics-word.char-drag.allow-break {
|
|
5396
|
+
display: inline;
|
|
5397
|
+
white-space: normal;
|
|
5398
|
+
}
|
|
5399
|
+
|
|
4826
5400
|
.lyrics-syllable-wrap {
|
|
4827
5401
|
display: inline;
|
|
4828
5402
|
}
|
|
@@ -4877,44 +5451,30 @@ AmLyrics$1.styles = i$3 `
|
|
|
4877
5451
|
.lyrics-line.active:not(.lyrics-gap)
|
|
4878
5452
|
.lyrics-syllable.pre-highlight.no-chars {
|
|
4879
5453
|
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%;
|
|
5454
|
+
background-image: linear-gradient(
|
|
5455
|
+
90deg,
|
|
5456
|
+
var(--lyplus-text-primary, #fff) 0%,
|
|
5457
|
+
var(--lyplus-text-primary, #fff)
|
|
5458
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5459
|
+
#0000 100%
|
|
5460
|
+
);
|
|
5461
|
+
background-size: 0% 100%;
|
|
5462
|
+
background-position: left;
|
|
4898
5463
|
}
|
|
4899
5464
|
|
|
4900
5465
|
.lyrics-line.active:not(.lyrics-gap) .lyrics-syllable.highlight.rtl-text,
|
|
4901
5466
|
.lyrics-line.active:not(.lyrics-gap)
|
|
4902
5467
|
.lyrics-syllable.pre-highlight.rtl-text {
|
|
4903
5468
|
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%;
|
|
5469
|
+
background-image: linear-gradient(
|
|
5470
|
+
-90deg,
|
|
5471
|
+
var(--lyplus-text-primary) 0%,
|
|
5472
|
+
var(--lyplus-text-primary)
|
|
5473
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5474
|
+
transparent 100%
|
|
5475
|
+
);
|
|
5476
|
+
background-size: 0% 100%;
|
|
5477
|
+
background-position: right 0%;
|
|
4918
5478
|
}
|
|
4919
5479
|
|
|
4920
5480
|
/* Background vocals: muted gray wipe instead of white.
|
|
@@ -4931,18 +5491,13 @@ AmLyrics$1.styles = i$3 `
|
|
|
4931
5491
|
.lyrics-line.pre-active
|
|
4932
5492
|
.background-vocal-container
|
|
4933
5493
|
.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
|
-
);
|
|
5494
|
+
background-image: linear-gradient(
|
|
5495
|
+
90deg,
|
|
5496
|
+
color-mix(in srgb, var(--lyplus-text-primary, #fff) 50%, #888888) 0%,
|
|
5497
|
+
color-mix(in srgb, var(--lyplus-text-primary, #fff) 50%, #888888)
|
|
5498
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5499
|
+
#0000 100%
|
|
5500
|
+
);
|
|
4946
5501
|
}
|
|
4947
5502
|
|
|
4948
5503
|
.lyrics-line.active
|
|
@@ -4957,17 +5512,13 @@ AmLyrics$1.styles = i$3 `
|
|
|
4957
5512
|
.lyrics-line.pre-active
|
|
4958
5513
|
.background-vocal-container
|
|
4959
5514
|
.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
|
-
);
|
|
5515
|
+
background-image: linear-gradient(
|
|
5516
|
+
-90deg,
|
|
5517
|
+
color-mix(in srgb, var(--lyplus-text-primary) 50%, #888888) 0%,
|
|
5518
|
+
color-mix(in srgb, var(--lyplus-text-primary) 50%, #888888)
|
|
5519
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5520
|
+
transparent 100%
|
|
5521
|
+
);
|
|
4971
5522
|
}
|
|
4972
5523
|
|
|
4973
5524
|
/* Non-growable words float up with a gentle curve */
|
|
@@ -4991,6 +5542,10 @@ AmLyrics$1.styles = i$3 `
|
|
|
4991
5542
|
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
4992
5543
|
}
|
|
4993
5544
|
|
|
5545
|
+
.lyrics-word.char-drag .lyrics-syllable.cleanup .char {
|
|
5546
|
+
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
5547
|
+
}
|
|
5548
|
+
|
|
4994
5549
|
.lyrics-line.persist-highlight
|
|
4995
5550
|
.lyrics-word.growable
|
|
4996
5551
|
.lyrics-syllable.finished
|
|
@@ -4998,6 +5553,10 @@ AmLyrics$1.styles = i$3 `
|
|
|
4998
5553
|
.lyrics-line.persist-highlight
|
|
4999
5554
|
.lyrics-word.char-rise
|
|
5000
5555
|
.lyrics-syllable.finished
|
|
5556
|
+
.char,
|
|
5557
|
+
.lyrics-line.persist-highlight
|
|
5558
|
+
.lyrics-word.char-drag
|
|
5559
|
+
.lyrics-syllable.finished
|
|
5001
5560
|
.char {
|
|
5002
5561
|
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
5003
5562
|
}
|
|
@@ -5108,6 +5667,10 @@ AmLyrics$1.styles = i$3 `
|
|
|
5108
5667
|
transform 0.7s ease;
|
|
5109
5668
|
}
|
|
5110
5669
|
|
|
5670
|
+
.lyrics-word.char-drag span.char {
|
|
5671
|
+
transition: color 0.18s;
|
|
5672
|
+
}
|
|
5673
|
+
|
|
5111
5674
|
/* Active char spans: structural only, wipe animation sets gradient */
|
|
5112
5675
|
.lyrics-line.active .lyrics-syllable span.char {
|
|
5113
5676
|
background-clip: text;
|
|
@@ -5126,52 +5689,34 @@ AmLyrics$1.styles = i$3 `
|
|
|
5126
5689
|
#0000 100%
|
|
5127
5690
|
);
|
|
5128
5691
|
background-size:
|
|
5129
|
-
0.
|
|
5692
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
5130
5693
|
0% 100%;
|
|
5131
5694
|
background-position:
|
|
5132
|
-
-0.
|
|
5133
|
-
|
|
5695
|
+
calc(-1 * var(--wipe-gradient-width, 0.75em)) 0%,
|
|
5696
|
+
left;
|
|
5134
5697
|
transition:
|
|
5135
5698
|
transform 0.7s ease,
|
|
5136
5699
|
color 0.18s;
|
|
5137
5700
|
}
|
|
5138
5701
|
|
|
5139
5702
|
.lyrics-line.active .lyrics-syllable span.char.highlight {
|
|
5140
|
-
background-image:
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
#0000 100%
|
|
5150
|
-
);
|
|
5151
|
-
background-position:
|
|
5152
|
-
calc(100% + 0.5em) 0%,
|
|
5153
|
-
calc(100% + 0.25em) 0%;
|
|
5703
|
+
background-image: linear-gradient(
|
|
5704
|
+
-90deg,
|
|
5705
|
+
var(--lyplus-text-primary, #fff) 0%,
|
|
5706
|
+
var(--lyplus-text-primary, #fff)
|
|
5707
|
+
calc(100% - var(--wipe-gradient-width, 0.75em)),
|
|
5708
|
+
#0000 100%
|
|
5709
|
+
);
|
|
5710
|
+
background-size: 0% 100%;
|
|
5711
|
+
background-position: right 0%;
|
|
5154
5712
|
}
|
|
5155
5713
|
|
|
5156
|
-
.lyrics-line.active .lyrics-syllable
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
#0000 100%
|
|
5163
|
-
),
|
|
5164
|
-
linear-gradient(
|
|
5165
|
-
90deg,
|
|
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%;
|
|
5714
|
+
.lyrics-line.active .lyrics-syllable span.char.pre-wipe-lead {
|
|
5715
|
+
animation-name: char-pre-wipe;
|
|
5716
|
+
animation-duration: var(--pre-wipe-duration);
|
|
5717
|
+
animation-delay: var(--pre-wipe-delay);
|
|
5718
|
+
animation-timing-function: linear;
|
|
5719
|
+
animation-fill-mode: forwards;
|
|
5175
5720
|
}
|
|
5176
5721
|
|
|
5177
5722
|
/* ==========================================================================
|
|
@@ -5248,13 +5793,7 @@ AmLyrics$1.styles = i$3 `
|
|
|
5248
5793
|
color: var(--lyplus-text-primary) !important;
|
|
5249
5794
|
}
|
|
5250
5795
|
|
|
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 {
|
|
5796
|
+
.lyrics-line.active .lyrics-syllable.line-synced span.char {
|
|
5258
5797
|
background-image: none !important;
|
|
5259
5798
|
background-color: var(--lyplus-text-primary) !important;
|
|
5260
5799
|
transition: background-color 120ms ease-out !important;
|
|
@@ -5427,6 +5966,49 @@ AmLyrics$1.styles = i$3 `
|
|
|
5427
5966
|
gap: 4px;
|
|
5428
5967
|
}
|
|
5429
5968
|
|
|
5969
|
+
.source-switch-btn {
|
|
5970
|
+
position: relative;
|
|
5971
|
+
display: inline-flex;
|
|
5972
|
+
align-items: center;
|
|
5973
|
+
padding: 2px 8px;
|
|
5974
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
5975
|
+
min-height: 28px;
|
|
5976
|
+
background: transparent;
|
|
5977
|
+
border-radius: 6px;
|
|
5978
|
+
color: #aaa;
|
|
5979
|
+
cursor: pointer;
|
|
5980
|
+
font-family: inherit;
|
|
5981
|
+
font-size: 11px;
|
|
5982
|
+
transition:
|
|
5983
|
+
color 0.2s ease,
|
|
5984
|
+
border-color 0.2s ease,
|
|
5985
|
+
background-color 0.2s ease,
|
|
5986
|
+
transform 0.12s ease;
|
|
5987
|
+
}
|
|
5988
|
+
|
|
5989
|
+
.source-switch-btn::before {
|
|
5990
|
+
content: '';
|
|
5991
|
+
position: absolute;
|
|
5992
|
+
inset: -6px;
|
|
5993
|
+
}
|
|
5994
|
+
|
|
5995
|
+
.source-switch-btn:active:not(:disabled) {
|
|
5996
|
+
transform: scale(0.96);
|
|
5997
|
+
}
|
|
5998
|
+
|
|
5999
|
+
.source-switch-btn:disabled {
|
|
6000
|
+
cursor: default;
|
|
6001
|
+
opacity: 0.7;
|
|
6002
|
+
}
|
|
6003
|
+
|
|
6004
|
+
.source-switch-svg {
|
|
6005
|
+
margin-right: 4px;
|
|
6006
|
+
}
|
|
6007
|
+
|
|
6008
|
+
.source-switch-svg.is-loading {
|
|
6009
|
+
animation: source-switch-spin 1s linear infinite;
|
|
6010
|
+
}
|
|
6011
|
+
|
|
5430
6012
|
.control-button {
|
|
5431
6013
|
background: transparent;
|
|
5432
6014
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
@@ -5435,7 +6017,10 @@ AmLyrics$1.styles = i$3 `
|
|
|
5435
6017
|
font-size: 0.8em;
|
|
5436
6018
|
color: rgba(255, 255, 255, 0.6);
|
|
5437
6019
|
cursor: pointer;
|
|
5438
|
-
transition:
|
|
6020
|
+
transition:
|
|
6021
|
+
color 0.2s,
|
|
6022
|
+
border-color 0.2s,
|
|
6023
|
+
background-color 0.2s;
|
|
5439
6024
|
font-weight: normal;
|
|
5440
6025
|
}
|
|
5441
6026
|
|
|
@@ -5573,136 +6158,157 @@ AmLyrics$1.styles = i$3 `
|
|
|
5573
6158
|
KEYFRAME ANIMATIONS
|
|
5574
6159
|
========================================================================== */
|
|
5575
6160
|
|
|
6161
|
+
@keyframes source-switch-spin {
|
|
6162
|
+
to {
|
|
6163
|
+
transform: rotate(360deg);
|
|
6164
|
+
}
|
|
6165
|
+
}
|
|
6166
|
+
|
|
5576
6167
|
/* Wipe animation for syllables */
|
|
5577
6168
|
@keyframes wipe {
|
|
5578
6169
|
from {
|
|
5579
|
-
background-size:
|
|
5580
|
-
|
|
5581
|
-
0% 100%;
|
|
5582
|
-
background-position:
|
|
5583
|
-
-0.375em 0%,
|
|
5584
|
-
left;
|
|
6170
|
+
background-size: 0% 100%;
|
|
6171
|
+
background-position: left;
|
|
5585
6172
|
}
|
|
5586
6173
|
to {
|
|
5587
|
-
background-size:
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
6174
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6175
|
+
background-position: left;
|
|
6176
|
+
}
|
|
6177
|
+
}
|
|
6178
|
+
|
|
6179
|
+
@keyframes wipe-from-pre {
|
|
6180
|
+
from {
|
|
6181
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6182
|
+
background-position: left;
|
|
6183
|
+
}
|
|
6184
|
+
to {
|
|
6185
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6186
|
+
background-position: left;
|
|
5593
6187
|
}
|
|
5594
6188
|
}
|
|
5595
6189
|
|
|
5596
6190
|
@keyframes start-wipe {
|
|
5597
6191
|
0% {
|
|
5598
|
-
background-size:
|
|
5599
|
-
|
|
5600
|
-
0% 100%;
|
|
5601
|
-
background-position:
|
|
5602
|
-
-0.75em 0%,
|
|
5603
|
-
-0.375em 0%;
|
|
6192
|
+
background-size: 0% 100%;
|
|
6193
|
+
background-position: left;
|
|
5604
6194
|
}
|
|
5605
6195
|
100% {
|
|
5606
|
-
background-size:
|
|
5607
|
-
|
|
5608
|
-
100% 100%;
|
|
5609
|
-
background-position:
|
|
5610
|
-
calc(100% + 0.375em) 0%,
|
|
5611
|
-
left;
|
|
6196
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6197
|
+
background-position: left;
|
|
5612
6198
|
}
|
|
5613
6199
|
}
|
|
5614
6200
|
|
|
5615
6201
|
@keyframes wipe-rtl {
|
|
5616
6202
|
from {
|
|
5617
|
-
background-size:
|
|
5618
|
-
|
|
5619
|
-
0% 100%;
|
|
5620
|
-
background-position:
|
|
5621
|
-
calc(100% + 0.375em) 0%,
|
|
5622
|
-
calc(100% + 0.36em) 0%;
|
|
6203
|
+
background-size: 0% 100%;
|
|
6204
|
+
background-position: right 0%;
|
|
5623
6205
|
}
|
|
5624
6206
|
to {
|
|
5625
|
-
background-size:
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
6207
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6208
|
+
background-position: right 0%;
|
|
6209
|
+
}
|
|
6210
|
+
}
|
|
6211
|
+
|
|
6212
|
+
@keyframes wipe-from-pre-rtl {
|
|
6213
|
+
from {
|
|
6214
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6215
|
+
background-position: right 0%;
|
|
6216
|
+
}
|
|
6217
|
+
to {
|
|
6218
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6219
|
+
background-position: right 0%;
|
|
5631
6220
|
}
|
|
5632
6221
|
}
|
|
5633
6222
|
|
|
5634
6223
|
@keyframes start-wipe-rtl {
|
|
5635
6224
|
0% {
|
|
5636
|
-
background-size:
|
|
5637
|
-
|
|
5638
|
-
0% 100%;
|
|
5639
|
-
background-position:
|
|
5640
|
-
calc(100% + 0.75em) 0%,
|
|
5641
|
-
calc(100% + 0.5em) 0%;
|
|
6225
|
+
background-size: 0% 100%;
|
|
6226
|
+
background-position: right 0%;
|
|
5642
6227
|
}
|
|
5643
6228
|
100% {
|
|
5644
|
-
background-size:
|
|
5645
|
-
|
|
5646
|
-
100% 100%;
|
|
5647
|
-
background-position:
|
|
5648
|
-
-0.75em 0%,
|
|
5649
|
-
right 0%;
|
|
6229
|
+
background-size: calc(100% + var(--wipe-gradient-width, 0.75em)) 100%;
|
|
6230
|
+
background-position: right 0%;
|
|
5650
6231
|
}
|
|
5651
6232
|
}
|
|
5652
6233
|
|
|
5653
6234
|
@keyframes pre-wipe-universal {
|
|
6235
|
+
from {
|
|
6236
|
+
background-size: 0% 100%;
|
|
6237
|
+
background-position: left;
|
|
6238
|
+
}
|
|
6239
|
+
to {
|
|
6240
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6241
|
+
background-position: left;
|
|
6242
|
+
}
|
|
6243
|
+
}
|
|
6244
|
+
|
|
6245
|
+
@keyframes pre-wipe-universal-rtl {
|
|
6246
|
+
from {
|
|
6247
|
+
background-size: 0% 100%;
|
|
6248
|
+
background-position: right 0%;
|
|
6249
|
+
}
|
|
6250
|
+
to {
|
|
6251
|
+
background-size: var(--wipe-gradient-width, 0.75em) 100%;
|
|
6252
|
+
background-position: right 0%;
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6255
|
+
|
|
6256
|
+
/* Character-rendered words use a separate moving gradient in front of
|
|
6257
|
+
their solid fill. This makes the individual glyph wipes read as one
|
|
6258
|
+
continuous word-level wipe. */
|
|
6259
|
+
@keyframes char-pre-wipe {
|
|
5654
6260
|
from {
|
|
5655
6261
|
background-size:
|
|
5656
|
-
0.75em 100%,
|
|
6262
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
5657
6263
|
0% 100%;
|
|
5658
6264
|
background-position:
|
|
5659
|
-
-0.75em 0%,
|
|
6265
|
+
calc(-1 * var(--wipe-gradient-width, 0.75em)) 0%,
|
|
5660
6266
|
left;
|
|
5661
6267
|
}
|
|
5662
6268
|
to {
|
|
5663
6269
|
background-size:
|
|
5664
|
-
0.75em 100%,
|
|
6270
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
5665
6271
|
0% 100%;
|
|
5666
6272
|
background-position:
|
|
5667
|
-
-0.375em 0%,
|
|
6273
|
+
calc(-1 * var(--wipe-gradient-half, 0.375em)) 0%,
|
|
5668
6274
|
left;
|
|
5669
6275
|
}
|
|
5670
6276
|
}
|
|
5671
6277
|
|
|
5672
|
-
@keyframes
|
|
6278
|
+
@keyframes char-start-wipe {
|
|
5673
6279
|
from {
|
|
5674
6280
|
background-size:
|
|
5675
|
-
0.75em 100%,
|
|
6281
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
5676
6282
|
0% 100%;
|
|
5677
6283
|
background-position:
|
|
5678
|
-
calc(
|
|
5679
|
-
|
|
6284
|
+
calc(-1 * var(--wipe-gradient-width, 0.75em)) 0%,
|
|
6285
|
+
left;
|
|
5680
6286
|
}
|
|
5681
6287
|
to {
|
|
5682
6288
|
background-size:
|
|
5683
|
-
0.75em 100%,
|
|
5684
|
-
|
|
6289
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
6290
|
+
100% 100%;
|
|
5685
6291
|
background-position:
|
|
5686
|
-
calc(100% + 0.375em) 0%,
|
|
5687
|
-
|
|
6292
|
+
calc(100% + var(--wipe-gradient-half, 0.375em)) 0%,
|
|
6293
|
+
left;
|
|
5688
6294
|
}
|
|
5689
6295
|
}
|
|
5690
6296
|
|
|
5691
|
-
@keyframes
|
|
6297
|
+
@keyframes char-wipe {
|
|
5692
6298
|
from {
|
|
5693
6299
|
background-size:
|
|
5694
|
-
0.75em 100%,
|
|
6300
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
5695
6301
|
0% 100%;
|
|
5696
6302
|
background-position:
|
|
5697
|
-
-0.
|
|
6303
|
+
calc(-1 * var(--wipe-gradient-half, 0.375em)) 0%,
|
|
5698
6304
|
left;
|
|
5699
6305
|
}
|
|
5700
6306
|
to {
|
|
5701
6307
|
background-size:
|
|
5702
|
-
0.75em 100%,
|
|
5703
|
-
|
|
6308
|
+
var(--wipe-gradient-width, 0.75em) 100%,
|
|
6309
|
+
100% 100%;
|
|
5704
6310
|
background-position:
|
|
5705
|
-
-0.375em 0%,
|
|
6311
|
+
calc(100% + var(--wipe-gradient-half, 0.375em)) 0%,
|
|
5706
6312
|
left;
|
|
5707
6313
|
}
|
|
5708
6314
|
}
|
|
@@ -5797,6 +6403,15 @@ AmLyrics$1.styles = i$3 `
|
|
|
5797
6403
|
}
|
|
5798
6404
|
}
|
|
5799
6405
|
|
|
6406
|
+
@keyframes drag-char {
|
|
6407
|
+
0% {
|
|
6408
|
+
transform: translate3d(0, 0, 0);
|
|
6409
|
+
}
|
|
6410
|
+
100% {
|
|
6411
|
+
transform: translate3d(0, var(--char-rise-y, -1.12px), 0);
|
|
6412
|
+
}
|
|
6413
|
+
}
|
|
6414
|
+
|
|
5800
6415
|
@keyframes grow-static {
|
|
5801
6416
|
0%,
|
|
5802
6417
|
100% {
|