LRC播放器

LRC播放器

lrc_player.jsview raw
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
function toSecond(time) {
return new Date(`1970-01-01T${time}Z`).getTime() / 1000;
}

/**
* 播放歌词
*
* @param {
* lyrics, 歌词,格式为二维数组。e.g. [ ['00:00:00.40', '周杰伦 - 等你下课 (with 杨瑞代)'], ['00:00:03.94', '词:周杰伦'], ['00:00:05.21', '曲:周杰伦'] ]
* seek = '00:00:00', 开始时间
* print = (lyric) => console.log(lyric), 歌词回调
* interval = 50 刷新检测间隔 ms
* }
* @returns
*/
function play({ lyrics, seek = '00:00:00', print = (lyric) => console.log(lyric), interval = 50 }) {
seek = toSecond(seek);
lyrics = lyrics.map(lyric => [toSecond(lyric[0]), lyric[1]]).sort((lyric1, lyric2) => lyric1[0] - lyric2[0]).filter(lyric => lyric[0] >= seek);

// setInterval和setTimeout在浏览器窗口非激活的状态下会停止工作或者以极慢的速度工作
// 可以使用 Web Worker 解决或者 requestAnimationFrame 解决[更新:经过测试窗口处于非激活状态下 requestAnimationFrame 也会停止工作]
// [RAF replacements for setTimeout and setInterval](https://bl.ocks.org/joyrexus/7304146)
// @see https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame
const timer = setInterval(() => {
if (lyrics.length === 0) return clearInterval(timer);

// seek 之前的全部显示
while (lyrics.length > 0 && lyrics[0][0] <= seek) print(lyrics.shift()[1]);

seek += (interval / 1000);
}, interval);

return timer;
}

TODO

  • 使用RAF模拟setInterval
  • 封装为对象,支持play、pause、resume、seek、reset等功能
    lrc_player_class.jsview raw
    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
    class LRCPlayer {
    constructor({ lyrics, print = (lyric) => console.log(lyric), done = () => { }, interval = 50 }) {
    this.lyrics = lyrics;
    this.print = print;
    this.done = done;
    this.interval = interval;
    this.seek = 0;
    this._timer = null;
    this.state = 'INIT';
    }

    // 播放(支持seek)
    play(seek = '00:00:00') {
    if (this.state === 'PLAY') return;

    if (this.state !== 'PAUSE') {
    this.seek = toSecond(seek);
    }
    this.state = 'PLAY';

    const lyrics = this.lyrics.map(lyric => [toSecond(lyric[0]), lyric[1]]).sort((lyric1, lyric2) => lyric1[0] - lyric2[0]).filter(lyric => lyric[0] >= this.seek);
    this._timer = setInterval(() => {
    if (lyrics.length === 0) {
    clearInterval(this._timer);
    this.done();
    return;
    }

    // seek 之前的全部显示
    while (lyrics.length > 0 && lyrics[0][0] <= this.seek) this.print(lyrics.shift()[1]);

    this.seek += (this.interval / 1000);
    }, this.interval);
    }

    // 暂停
    pause() {
    if (this.state === 'PAUSE') return;
    this.state = 'PAUSE';
    clearInterval(this._timer);
    }

    // 继续播放
    resume() {
    if (this.state === 'PAUSE') this.play();
    }

    // 重新开始
    reset() {
    this.pause();
    this.state = 'INIT';
    this.play();
    }
    }

参考

本站采用「署名 4.0 国际」进行许可。