audioSyncLyric.ts
3.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import RabbitLyrics from 'rabbit-lyrics';
import parseLyrics from 'rabbit-lyrics/src/parseLyrics';
// @ts-ignore
export default class AudioSyncLyric extends RabbitLyrics {
public startTime = 0;
public setStartTime(time: number) {
this.startTime = time || 0;
this.render();
this.mediaElement.addEventListener('timeupdate', this.synchronize);
}
private render(): void {
// Add class names
this.lyricsElement.classList.add('rabbit-lyrics');
this.lyricsElement.classList.add(`rabbit-lyrics--${this.viewMode}`);
this.lyricsElement.classList.add(`rabbit-lyrics--${this.alignment}`);
this.lyricsElement.textContent = null;
// Render lyrics lines
this.lyricsLines = parseLyrics(this.lyrics).map((line) => {
const lineElement = document.createElement('div');
lineElement.className = 'rabbit-lyrics__line';
lineElement.addEventListener('click', () => {
this.mediaElement.currentTime = line.startsAt - this.startTime;
this.synchronize();
});
const lineContent = line.content.map((inline) => {
const inlineElement = document.createElement('span');
inlineElement.className = 'rabbit-lyrics__inline';
inlineElement.textContent = inline.content;
lineElement.append(inlineElement);
return { ...inline, element: inlineElement };
});
this.lyricsElement.append(lineElement);
return { ...line, content: lineContent, element: lineElement };
});
this.synchronize();
}
private synchronize = () => {
const time = this.startTime + this.mediaElement.currentTime;
let changed = false; // If here are active lines changed
const activeLines = this.lyricsLines.filter((line) => {
if (time >= line.startsAt && time < line.endsAt) {
// If line should be active
if (!line.element.classList.contains('rabbit-lyrics__line--active')) {
// If it hasn't been activated
changed = true;
line.element.classList.add('rabbit-lyrics__line--active');
}
line.content.forEach((inline) => {
if (time >= inline.startsAt) {
inline.element.classList.add('rabbit-lyrics__inline--active');
} else {
inline.element.classList.remove('rabbit-lyrics__inline--active');
}
});
return true;
}
// If line should be inactive
if (line.element.classList.contains('rabbit-lyrics__line--active')) {
// If it hasn't been deactivated
changed = true;
line.element.classList.remove('rabbit-lyrics__line--active');
line.content.forEach((inline) => {
inline.element.classList.remove('rabbit-lyrics__inline--active');
});
}
return false;
});
if (changed && activeLines.length > 0) {
// Calculate scroll top. Vertically align active lines in middle
const activeLinesOffsetTop =
(activeLines[0].element.offsetTop +
activeLines[activeLines.length - 1].element.offsetTop +
activeLines[activeLines.length - 1].element.offsetHeight) /
2;
this.lyricsElement.scrollTop = activeLinesOffsetTop - this.lyricsElement.clientHeight / 2;
}
};
}