@braccato/core 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/engine.js +32 -35
- package/dist/inject.d.ts +1 -0
- package/dist/inject.js +58 -53
- package/dist/styles/lyrics.css +19 -43
- package/package.json +1 -1
package/dist/constants.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export declare const BACKGROUND_LINE_CLASS: "blyrics-background-line";
|
|
|
19
19
|
export declare const WORD_GROUP_CLASS: "blyrics-word-group";
|
|
20
20
|
export declare const LONG_WORD_GROUP_CLASS: "blyrics-word-group-long";
|
|
21
21
|
export declare const WORD_HIGHLIGHT_CLASS: "blyrics-word-highlight";
|
|
22
|
+
export declare const HIGHLIGHT_RUN_CLASS: "blyrics-highlight-run";
|
|
22
23
|
export declare const LINE_SYNCED_WORD_CLASS: "blyrics-line-synced-word";
|
|
23
24
|
export declare const BIDI_RUN_CLASS: "blyrics-bidi-run";
|
|
24
25
|
export declare const BIDI_SENSITIVE_CLASS: "blyrics-bidi-sensitive";
|
package/dist/constants.js
CHANGED
|
@@ -25,6 +25,7 @@ export const BACKGROUND_LINE_CLASS = "blyrics-background-line";
|
|
|
25
25
|
export const WORD_GROUP_CLASS = "blyrics-word-group";
|
|
26
26
|
export const LONG_WORD_GROUP_CLASS = "blyrics-word-group-long";
|
|
27
27
|
export const WORD_HIGHLIGHT_CLASS = "blyrics-word-highlight";
|
|
28
|
+
export const HIGHLIGHT_RUN_CLASS = "blyrics-highlight-run";
|
|
28
29
|
export const LINE_SYNCED_WORD_CLASS = "blyrics-line-synced-word";
|
|
29
30
|
export const BIDI_RUN_CLASS = "blyrics-bidi-run";
|
|
30
31
|
export const BIDI_SENSITIVE_CLASS = "blyrics-bidi-sensitive";
|
package/dist/engine.js
CHANGED
|
@@ -302,10 +302,14 @@ function resetLineAnimationState(lineData) {
|
|
|
302
302
|
resetLineAnimations(lineData);
|
|
303
303
|
markLineAnimationsStopped(lineData);
|
|
304
304
|
}
|
|
305
|
+
function togglePartClass(part, className, force) {
|
|
306
|
+
part.lyricElement.classList.toggle(className, force);
|
|
307
|
+
part.highlightElement?.classList.toggle(className, force);
|
|
308
|
+
}
|
|
305
309
|
function setAnimationsPlayState(lineData, isPlaying) {
|
|
306
310
|
const children = [lineData, ...lineData.parts];
|
|
307
311
|
for (const part of children) {
|
|
308
|
-
part
|
|
312
|
+
togglePartClass(part, PAUSED_CLASS, !isPlaying);
|
|
309
313
|
for (const animation of part.animations) {
|
|
310
314
|
if (isPlaying) {
|
|
311
315
|
animation.play();
|
|
@@ -319,11 +323,10 @@ function setAnimationsPlayState(lineData, isPlaying) {
|
|
|
319
323
|
function clearLineStateClasses(lineData) {
|
|
320
324
|
lineData.lyricElement.classList.remove(ANIMATING_CLASS);
|
|
321
325
|
for (const part of [lineData, ...lineData.parts]) {
|
|
322
|
-
part
|
|
326
|
+
togglePartClass(part, PAUSED_CLASS, false);
|
|
323
327
|
}
|
|
324
328
|
}
|
|
325
329
|
const LINE_SYNCED_WORD_CLASS = "blyrics-line-synced-word";
|
|
326
|
-
const WORD_HIGHLIGHT_SELECTOR = ".blyrics-word-highlight";
|
|
327
330
|
const INSTRUMENTAL_FILL_SELECTOR = ".blyrics--instrumental-fill";
|
|
328
331
|
const INSTRUMENTAL_WAVE_CLIP_SELECTOR = ".blyrics--wave-clip";
|
|
329
332
|
const INSTRUMENTAL_WAVE_PATH_SELECTOR = ".blyrics--wave-path";
|
|
@@ -610,13 +613,6 @@ function activeTextInstantKeyframes(config) {
|
|
|
610
613
|
},
|
|
611
614
|
];
|
|
612
615
|
}
|
|
613
|
-
function highlightTarget(part) {
|
|
614
|
-
const highlight = part.lyricElement.querySelector(WORD_HIGHLIGHT_SELECTOR);
|
|
615
|
-
if (highlight) {
|
|
616
|
-
return { element: highlight, options: {} };
|
|
617
|
-
}
|
|
618
|
-
return { element: part.lyricElement, options: { pseudoElement: "::after" } };
|
|
619
|
-
}
|
|
620
616
|
function lineSyncedTextKeyframes(config) {
|
|
621
617
|
return [
|
|
622
618
|
{
|
|
@@ -649,33 +645,30 @@ function fadeOutTextKeyframes(config) {
|
|
|
649
645
|
}
|
|
650
646
|
function startRichSyncedHighlightAnimations(engine, part, config, swipeTimeMs, wordTimeMs, swipeDurationMs, glowDurationMs, appliedTimingOffsetMs) {
|
|
651
647
|
const animations = [];
|
|
652
|
-
const
|
|
648
|
+
const highlight = part.highlightElement;
|
|
653
649
|
let swipeAnimation;
|
|
654
650
|
if (config.enabled.highlightSwipe) {
|
|
655
|
-
swipeAnimation = trackLyricAnimationTiming(engine,
|
|
651
|
+
swipeAnimation = trackLyricAnimationTiming(engine, highlight.animate(activeTextGradientKeyframes(config), {
|
|
656
652
|
duration: swipeDurationMs,
|
|
657
653
|
easing: config.highlight.swipeEasing,
|
|
658
654
|
fill: "forwards",
|
|
659
|
-
...target.options,
|
|
660
655
|
}), { appliedTimingOffsetMs, offsetMs: swipeTimeMs - wordTimeMs });
|
|
661
656
|
swipeAnimation.currentTime = correctedAnimationTimeMs(swipeTimeMs, appliedTimingOffsetMs, swipeDurationMs);
|
|
662
657
|
animations.push(swipeAnimation);
|
|
663
658
|
}
|
|
664
|
-
const opacityAnimation = trackLyricAnimationTiming(engine,
|
|
659
|
+
const opacityAnimation = trackLyricAnimationTiming(engine, highlight.animate(config.enabled.highlightSwipe ? activeTextVisibleKeyframes() : activeTextInstantKeyframes(config), {
|
|
665
660
|
duration: 1,
|
|
666
661
|
easing: "linear",
|
|
667
662
|
fill: "forwards",
|
|
668
|
-
...target.options,
|
|
669
663
|
}), { appliedTimingOffsetMs, offsetMs: 0 });
|
|
670
664
|
opacityAnimation.currentTime = correctedAnimationTimeMs(wordTimeMs, appliedTimingOffsetMs, 1);
|
|
671
665
|
animations.push(opacityAnimation);
|
|
672
666
|
let glowAnimation;
|
|
673
667
|
if (config.enabled.highlightGlow) {
|
|
674
|
-
glowAnimation = trackLyricAnimationTiming(engine,
|
|
668
|
+
glowAnimation = trackLyricAnimationTiming(engine, highlight.animate(activeTextGlowKeyframes(config), {
|
|
675
669
|
duration: glowDurationMs,
|
|
676
670
|
easing: config.highlight.glowEasing,
|
|
677
671
|
fill: "forwards",
|
|
678
|
-
...target.options,
|
|
679
672
|
}), { appliedTimingOffsetMs, offsetMs: 0 });
|
|
680
673
|
glowAnimation.currentTime = correctedAnimationTimeMs(wordTimeMs, appliedTimingOffsetMs, glowDurationMs);
|
|
681
674
|
animations.push(glowAnimation);
|
|
@@ -685,22 +678,20 @@ function startRichSyncedHighlightAnimations(engine, part, config, swipeTimeMs, w
|
|
|
685
678
|
function startLineSyncedHighlightAnimations(engine, part, config, wordTimeMs, glowDurationMs, appliedTimingOffsetMs) {
|
|
686
679
|
const animations = [];
|
|
687
680
|
const fadeInDuration = config.enabled.highlightFade ? config.highlight.fadeInDurationMs : 1;
|
|
688
|
-
const
|
|
689
|
-
const opacityAnimation = trackLyricAnimationTiming(engine,
|
|
681
|
+
const highlight = part.highlightElement;
|
|
682
|
+
const opacityAnimation = trackLyricAnimationTiming(engine, highlight.animate(lineSyncedTextKeyframes(config), {
|
|
690
683
|
duration: fadeInDuration,
|
|
691
684
|
easing: config.enabled.highlightFade ? config.highlight.fadeInEasing : "linear",
|
|
692
685
|
fill: "forwards",
|
|
693
|
-
...target.options,
|
|
694
686
|
}), { appliedTimingOffsetMs, offsetMs: 0 });
|
|
695
687
|
opacityAnimation.currentTime = correctedAnimationTimeMs(wordTimeMs, appliedTimingOffsetMs, fadeInDuration);
|
|
696
688
|
animations.push(opacityAnimation);
|
|
697
689
|
let glowAnimation;
|
|
698
690
|
if (config.enabled.highlightGlow) {
|
|
699
|
-
glowAnimation = trackLyricAnimationTiming(engine,
|
|
691
|
+
glowAnimation = trackLyricAnimationTiming(engine, highlight.animate(activeTextGlowKeyframes(config), {
|
|
700
692
|
duration: glowDurationMs,
|
|
701
693
|
easing: config.highlight.glowEasing,
|
|
702
694
|
fill: "forwards",
|
|
703
|
-
...target.options,
|
|
704
695
|
}), { appliedTimingOffsetMs, offsetMs: 0 });
|
|
705
696
|
glowAnimation.currentTime = correctedAnimationTimeMs(wordTimeMs, appliedTimingOffsetMs, glowDurationMs);
|
|
706
697
|
animations.push(glowAnimation);
|
|
@@ -753,8 +744,9 @@ function startWordAnimations(engine, part, config, currentTime, appliedTimingOff
|
|
|
753
744
|
const highlightAnimations = isLineSyncedWord
|
|
754
745
|
? startLineSyncedHighlightAnimations(engine, part, config, wordTimeMs, config.highlight.glowMinDurationMs, appliedTimingOffsetMs)
|
|
755
746
|
: startRichSyncedHighlightAnimations(engine, part, config, swipeTimeMs, wordTimeMs, swipeDurationMs, glowDurationMs, appliedTimingOffsetMs);
|
|
756
|
-
const
|
|
757
|
-
|
|
747
|
+
const wobbleAnimations = [];
|
|
748
|
+
if (config.enabled.wordWobble) {
|
|
749
|
+
const wobbleKeyframes = [
|
|
758
750
|
{ transform: config.word.wobbleFrom },
|
|
759
751
|
{
|
|
760
752
|
transform: config.word.wobblePeak,
|
|
@@ -769,18 +761,25 @@ function startWordAnimations(engine, part, config, currentTime, appliedTimingOff
|
|
|
769
761
|
offset: Math.max(config.word.wobblePeakOffset, config.word.wobbleSettleOffset),
|
|
770
762
|
},
|
|
771
763
|
{ transform: config.word.wobbleTo, easing: config.word.wobbleEndEasing },
|
|
772
|
-
]
|
|
764
|
+
];
|
|
765
|
+
const wobbleOptions = {
|
|
773
766
|
duration: config.word.wobbleDurationMs,
|
|
774
767
|
easing: config.word.wobbleEasing,
|
|
775
768
|
fill: "forwards",
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
769
|
+
};
|
|
770
|
+
const wobbleStartMs = correctedAnimationTimeMs(wordTimeMs, appliedTimingOffsetMs, config.word.wobbleDurationMs);
|
|
771
|
+
// The wobble is a paint transform, so the highlight copy must carry it too or the active
|
|
772
|
+
// sweep drifts off the word.
|
|
773
|
+
for (const wordElement of [part.lyricElement, part.highlightElement]) {
|
|
774
|
+
const animation = trackLyricAnimationTiming(engine, wordElement.animate(wobbleKeyframes, wobbleOptions), {
|
|
775
|
+
appliedTimingOffsetMs,
|
|
776
|
+
offsetMs: 0,
|
|
777
|
+
});
|
|
778
|
+
animation.currentTime = wobbleStartMs;
|
|
779
|
+
wobbleAnimations.push(animation);
|
|
780
|
+
}
|
|
780
781
|
}
|
|
781
|
-
part.animations =
|
|
782
|
-
? [...highlightAnimations.animations, wobbleAnimation]
|
|
783
|
-
: highlightAnimations.animations;
|
|
782
|
+
part.animations = [...highlightAnimations.animations, ...wobbleAnimations];
|
|
784
783
|
}
|
|
785
784
|
function startLineAnimations(engine, lineData, config, currentTime) {
|
|
786
785
|
const appliedTimingOffsetMs = engine.learnedAnimationTimingOffsetMs;
|
|
@@ -796,12 +795,10 @@ function startLineAnimations(engine, lineData, config, currentTime) {
|
|
|
796
795
|
function startWordExitAnimation(part, config) {
|
|
797
796
|
resetPartAnimations(part);
|
|
798
797
|
const fadeDuration = config.enabled.highlightFade ? config.highlight.fadeOutDurationMs : 1;
|
|
799
|
-
const
|
|
800
|
-
const animation = target.element.animate(fadeOutTextKeyframes(config), {
|
|
798
|
+
const animation = part.highlightElement.animate(fadeOutTextKeyframes(config), {
|
|
801
799
|
duration: fadeDuration,
|
|
802
800
|
easing: config.enabled.highlightFade ? config.highlight.fadeOutEasing : "linear",
|
|
803
801
|
fill: "none",
|
|
804
|
-
...target.options,
|
|
805
802
|
});
|
|
806
803
|
part.animations = [animation];
|
|
807
804
|
animation.addEventListener("finish", () => {
|
package/dist/inject.d.ts
CHANGED
package/dist/inject.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// animates is one word, so every part is split on whitespace with its timing pro-rated across the
|
|
12
12
|
// split by character count. A line that arrives with no timed parts at all is rebuilt the same way
|
|
13
13
|
// into zero duration words, so line synced lyrics reach the DOM the sweep already knows.
|
|
14
|
-
import { BACKGROUND_LINE_CLASS, BACKGROUND_LYRIC_CLASS, BIDI_RUN_CLASS, BIDI_SENSITIVE_CLASS, CONTENT_LINE_CLASS, EXPLICIT_WORD_CLASS, LINE_MAIN_CLASS, LINE_SYNCED_WORD_CLASS, LONG_WORD_GROUP_CLASS, ROMANIZED_LYRICS_CLASS, RTL_CLASS, TRANSLATED_LYRICS_CLASS, WORD_CLASS, WORD_GROUP_CLASS, WORD_HIGHLIGHT_CLASS, ZERO_DURATION_ANIMATION_CLASS, } from "./constants.js";
|
|
14
|
+
import { BACKGROUND_LINE_CLASS, BACKGROUND_LYRIC_CLASS, BIDI_RUN_CLASS, BIDI_SENSITIVE_CLASS, CONTENT_LINE_CLASS, EXPLICIT_WORD_CLASS, HIGHLIGHT_RUN_CLASS, LINE_MAIN_CLASS, LINE_SYNCED_WORD_CLASS, LONG_WORD_GROUP_CLASS, ROMANIZED_LYRICS_CLASS, RTL_CLASS, TRANSLATED_LYRICS_CLASS, WORD_CLASS, WORD_GROUP_CLASS, WORD_HIGHLIGHT_CLASS, ZERO_DURATION_ANIMATION_CLASS, } from "./constants.js";
|
|
15
15
|
import { getSeekTimeFromClick } from "./seek.js";
|
|
16
16
|
import { testRtl } from "./text.js";
|
|
17
17
|
import { registerThemeSetting } from "./themeSettings.js";
|
|
@@ -60,11 +60,12 @@ export function deriveSyncType(lyrics) {
|
|
|
60
60
|
return "richsync";
|
|
61
61
|
return lyrics.every(item => item.startTimeMs === 0) ? "none" : "synced";
|
|
62
62
|
}
|
|
63
|
-
function newPartData(part, span) {
|
|
63
|
+
function newPartData(part, span, highlight) {
|
|
64
64
|
return {
|
|
65
65
|
time: part.startTimeMs / 1000,
|
|
66
66
|
duration: part.durationMs / 1000,
|
|
67
67
|
lyricElement: span,
|
|
68
|
+
highlightElement: highlight,
|
|
68
69
|
animations: [],
|
|
69
70
|
};
|
|
70
71
|
}
|
|
@@ -193,67 +194,57 @@ function appendLongWordBreaks(doc, span, text, threshold) {
|
|
|
193
194
|
}
|
|
194
195
|
return true;
|
|
195
196
|
}
|
|
196
|
-
function cloneTextWithBreaks(doc, source) {
|
|
197
|
-
const fragment = doc.createDocumentFragment();
|
|
198
|
-
for (const node of source.childNodes) {
|
|
199
|
-
fragment.appendChild(node.cloneNode(true));
|
|
200
|
-
}
|
|
201
|
-
return fragment;
|
|
202
|
-
}
|
|
203
197
|
function createTimedWordSpan(doc, part, wrapThreshold) {
|
|
204
198
|
const span = doc.createElement("span");
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
highlight.setAttribute("aria-hidden", "true");
|
|
228
|
-
highlight.appendChild(cloneTextWithBreaks(doc, span));
|
|
229
|
-
span.appendChild(highlight);
|
|
199
|
+
const highlight = doc.createElement("span");
|
|
200
|
+
highlight.classList.add(WORD_HIGHLIGHT_CLASS);
|
|
201
|
+
for (const wordElement of [span, highlight]) {
|
|
202
|
+
wordElement.classList.add(WORD_CLASS);
|
|
203
|
+
wordElement.dir = "auto";
|
|
204
|
+
if (part.durationMs === 0) {
|
|
205
|
+
wordElement.classList.add(ZERO_DURATION_ANIMATION_CLASS);
|
|
206
|
+
wordElement.classList.add(LINE_SYNCED_WORD_CLASS);
|
|
207
|
+
}
|
|
208
|
+
if (testRtl(part.words))
|
|
209
|
+
wordElement.classList.add(RTL_CLASS);
|
|
210
|
+
if (part.durationMs > longWordThreshold.getNumberValue())
|
|
211
|
+
wordElement.dataset.longWord = "true";
|
|
212
|
+
if (part.isBackground)
|
|
213
|
+
wordElement.classList.add(BACKGROUND_LYRIC_CLASS);
|
|
214
|
+
if (part.explicit)
|
|
215
|
+
wordElement.classList.add(EXPLICIT_WORD_CLASS);
|
|
216
|
+
appendLongWordBreaks(doc, wordElement, part.words, wrapThreshold);
|
|
217
|
+
wordElement.dataset.time = String(part.startTimeMs / 1000);
|
|
218
|
+
wordElement.dataset.duration = String(part.durationMs / 1000);
|
|
219
|
+
wordElement.dataset.content = part.words;
|
|
220
|
+
wordElement.style.setProperty("--blyrics-duration", part.durationMs + "ms");
|
|
230
221
|
}
|
|
231
|
-
|
|
232
|
-
span.dataset.duration = String(part.durationMs / 1000);
|
|
233
|
-
span.dataset.content = part.words;
|
|
234
|
-
span.style.setProperty("--blyrics-duration", part.durationMs + "ms");
|
|
235
|
-
return span;
|
|
222
|
+
return { span, highlight };
|
|
236
223
|
}
|
|
237
224
|
function createWordGroup(doc, group, lineData) {
|
|
238
225
|
const wrapThreshold = Math.max(1, longWordWrapThreshold.getNumberValue());
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
groupElement
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
groupElement.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
226
|
+
const lyricGroup = doc.createElement("span");
|
|
227
|
+
const highlightGroup = doc.createElement("span");
|
|
228
|
+
for (const groupElement of [lyricGroup, highlightGroup]) {
|
|
229
|
+
groupElement.classList.add(WORD_GROUP_CLASS);
|
|
230
|
+
groupElement.dir = "auto";
|
|
231
|
+
groupElement.dataset.content = group.text;
|
|
232
|
+
if (group.text.length > wrapThreshold * 2) {
|
|
233
|
+
groupElement.classList.add(LONG_WORD_GROUP_CLASS);
|
|
234
|
+
}
|
|
235
|
+
if (group.isBackground) {
|
|
236
|
+
groupElement.classList.add(BACKGROUND_LYRIC_CLASS);
|
|
237
|
+
}
|
|
248
238
|
}
|
|
249
239
|
for (const token of group.tokens) {
|
|
250
240
|
if (token.kind === "space")
|
|
251
241
|
continue;
|
|
252
|
-
const span = createTimedWordSpan(doc, token.part, wrapThreshold);
|
|
253
|
-
lineData.parts.push(newPartData(token.part, span));
|
|
254
|
-
|
|
242
|
+
const { span, highlight } = createTimedWordSpan(doc, token.part, wrapThreshold);
|
|
243
|
+
lineData.parts.push(newPartData(token.part, span, highlight));
|
|
244
|
+
lyricGroup.appendChild(span);
|
|
245
|
+
highlightGroup.appendChild(highlight);
|
|
255
246
|
}
|
|
256
|
-
return
|
|
247
|
+
return { lyricGroup, highlightGroup };
|
|
257
248
|
}
|
|
258
249
|
function createContentLine(doc, className, text) {
|
|
259
250
|
const line = doc.createElement("div");
|
|
@@ -268,6 +259,12 @@ function createBidiRun(doc, text) {
|
|
|
268
259
|
applyDirection(run, text);
|
|
269
260
|
return run;
|
|
270
261
|
}
|
|
262
|
+
function createHighlightRun(doc, text) {
|
|
263
|
+
const run = createBidiRun(doc, text);
|
|
264
|
+
run.classList.add(HIGHLIGHT_RUN_CLASS);
|
|
265
|
+
run.setAttribute("aria-hidden", "true");
|
|
266
|
+
return run;
|
|
267
|
+
}
|
|
271
268
|
export function createLyricsLine(doc, parts, line, lyricElement, options = { splitBackgroundLine: true }) {
|
|
272
269
|
const lineText = parts.map(part => part.words).join("");
|
|
273
270
|
const mainText = options.splitBackgroundLine
|
|
@@ -282,13 +279,17 @@ export function createLyricsLine(doc, parts, line, lyricElement, options = { spl
|
|
|
282
279
|
.join("");
|
|
283
280
|
const main = createContentLine(doc, LINE_MAIN_CLASS, mainText);
|
|
284
281
|
const mainRun = createBidiRun(doc, mainText);
|
|
282
|
+
const mainHighlightRun = createHighlightRun(doc, mainText);
|
|
285
283
|
const groupedTokens = groupTokensByWord(normalizeParts(parts));
|
|
286
284
|
const backgroundLine = createContentLine(doc, BACKGROUND_LINE_CLASS, backgroundText);
|
|
287
285
|
const backgroundRun = createBidiRun(doc, backgroundText);
|
|
286
|
+
const backgroundHighlightRun = createHighlightRun(doc, backgroundText);
|
|
288
287
|
let hasBackground = false;
|
|
289
288
|
let pendingForegroundSpace = "";
|
|
290
289
|
let pendingBackgroundSpace = "";
|
|
290
|
+
main.appendChild(mainHighlightRun);
|
|
291
291
|
main.appendChild(mainRun);
|
|
292
|
+
backgroundLine.appendChild(backgroundHighlightRun);
|
|
292
293
|
backgroundLine.appendChild(backgroundRun);
|
|
293
294
|
for (const item of groupedTokens) {
|
|
294
295
|
if ("kind" in item) {
|
|
@@ -299,11 +300,15 @@ export function createLyricsLine(doc, parts, line, lyricElement, options = { spl
|
|
|
299
300
|
else {
|
|
300
301
|
const shouldUseBackgroundLine = options.splitBackgroundLine && item.isBackground;
|
|
301
302
|
const target = shouldUseBackgroundLine ? backgroundRun : mainRun;
|
|
303
|
+
const highlightRun = shouldUseBackgroundLine ? backgroundHighlightRun : mainHighlightRun;
|
|
302
304
|
const pendingSpace = shouldUseBackgroundLine ? pendingBackgroundSpace : pendingForegroundSpace;
|
|
303
305
|
if (target.childNodes.length > 0 && pendingSpace.length > 0) {
|
|
304
306
|
target.appendChild(doc.createTextNode(pendingSpace));
|
|
307
|
+
highlightRun.appendChild(doc.createTextNode(pendingSpace));
|
|
305
308
|
}
|
|
306
|
-
|
|
309
|
+
const { lyricGroup, highlightGroup } = createWordGroup(doc, item, line);
|
|
310
|
+
target.appendChild(lyricGroup);
|
|
311
|
+
highlightRun.appendChild(highlightGroup);
|
|
307
312
|
if (shouldUseBackgroundLine) {
|
|
308
313
|
hasBackground = true;
|
|
309
314
|
pendingBackgroundSpace = "";
|
package/dist/styles/lyrics.css
CHANGED
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
display: block;
|
|
39
39
|
max-width: 100%;
|
|
40
40
|
overflow-wrap: normal;
|
|
41
|
+
position: relative;
|
|
41
42
|
text-align: inherit;
|
|
42
43
|
unicode-bidi: plaintext;
|
|
43
44
|
white-space: normal;
|
|
@@ -47,18 +48,28 @@
|
|
|
47
48
|
display: block;
|
|
48
49
|
margin-top: 0.1em;
|
|
49
50
|
max-width: 100%;
|
|
51
|
+
position: relative;
|
|
50
52
|
text-align: inherit;
|
|
51
53
|
unicode-bidi: plaintext;
|
|
52
54
|
white-space: normal;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
.blyrics-bidi-run {
|
|
56
|
-
display:
|
|
58
|
+
display: block;
|
|
57
59
|
max-width: 100%;
|
|
58
60
|
unicode-bidi: normal;
|
|
59
61
|
white-space: inherit;
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
.blyrics-highlight-run {
|
|
65
|
+
inset: 0 0 auto;
|
|
66
|
+
pointer-events: none;
|
|
67
|
+
position: absolute;
|
|
68
|
+
user-select: none;
|
|
69
|
+
width: auto;
|
|
70
|
+
z-index: 1;
|
|
71
|
+
}
|
|
72
|
+
|
|
62
73
|
.blyrics-word-group {
|
|
63
74
|
display: inline-block;
|
|
64
75
|
max-width: 100%;
|
|
@@ -116,68 +127,33 @@
|
|
|
116
127
|
initial-value: -0.1;
|
|
117
128
|
}
|
|
118
129
|
|
|
119
|
-
.blyrics--word
|
|
120
|
-
.blyrics-word-highlight {
|
|
130
|
+
.blyrics--word.blyrics-word-highlight {
|
|
121
131
|
color: transparent;
|
|
122
132
|
background-image: linear-gradient(
|
|
123
133
|
90deg,
|
|
124
134
|
var(--blyrics-lyric-active-color)
|
|
125
|
-
calc(
|
|
126
|
-
100% * var(--lyric-transition-amount-start) - 4rem *
|
|
127
|
-
var(--lyric-transition-amount-start) + 2rem
|
|
128
|
-
),
|
|
135
|
+
calc(100% * var(--lyric-transition-amount-start)),
|
|
129
136
|
#00000000
|
|
130
|
-
calc(
|
|
131
|
-
100% * var(--lyric-transition-amount-end) - 4rem *
|
|
132
|
-
var(--lyric-transition-amount-end) + 2rem + 1px
|
|
133
|
-
)
|
|
137
|
+
calc(100% * var(--lyric-transition-amount-end) + 1px)
|
|
134
138
|
);
|
|
135
139
|
background-clip: text;
|
|
136
|
-
box-sizing: content-box;
|
|
137
140
|
filter: drop-shadow(0 0 0 var(--blyrics-glow-color));
|
|
138
|
-
left: -2rem;
|
|
139
141
|
opacity: 0;
|
|
140
|
-
padding: 2rem;
|
|
141
142
|
pointer-events: none;
|
|
142
|
-
position: absolute;
|
|
143
|
-
top: -2rem;
|
|
144
|
-
white-space: inherit;
|
|
145
|
-
width: 100%;
|
|
146
143
|
--lyric-transition-amount-start: var(
|
|
147
144
|
--blyrics-highlight-swipe-start-from,
|
|
148
145
|
-0.2
|
|
149
146
|
);
|
|
150
147
|
--lyric-transition-amount-end: var(--blyrics-highlight-swipe-end-from, -0.1);
|
|
151
148
|
}
|
|
152
|
-
|
|
153
|
-
.blyrics
|
|
154
|
-
content: attr(data-content);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
.blyrics-word-highlight {
|
|
158
|
-
user-select: none;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
.blyrics--word:has(.blyrics-word-highlight)::after {
|
|
162
|
-
content: none;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
.blyrics-rtl.blyrics--word::after,
|
|
166
|
-
.blyrics--word.blyrics-rtl::after,
|
|
167
|
-
.blyrics-rtl .blyrics-word-highlight,
|
|
168
|
-
.blyrics--word.blyrics-rtl .blyrics-word-highlight {
|
|
149
|
+
.blyrics-word-highlight.blyrics-rtl,
|
|
150
|
+
.blyrics-rtl .blyrics-word-highlight {
|
|
169
151
|
background-image: linear-gradient(
|
|
170
152
|
270deg,
|
|
171
153
|
var(--blyrics-lyric-active-color)
|
|
172
|
-
calc(
|
|
173
|
-
100% * var(--lyric-transition-amount-start) - 4rem *
|
|
174
|
-
var(--lyric-transition-amount-start) + 2rem
|
|
175
|
-
),
|
|
154
|
+
calc(100% * var(--lyric-transition-amount-start)),
|
|
176
155
|
#00000000
|
|
177
|
-
calc(
|
|
178
|
-
100% * var(--lyric-transition-amount-end) - 4rem *
|
|
179
|
-
var(--lyric-transition-amount-end) + 2rem + 1px
|
|
180
|
-
)
|
|
156
|
+
calc(100% * var(--lyric-transition-amount-end) + 1px)
|
|
181
157
|
);
|
|
182
158
|
}
|
|
183
159
|
|