← 返回首页
Javascript实现歌词滚动效果
发表时间:2023-04-01 18:52:08
Javascript实现歌词滚动效果

本文案例为歌词滚动,随着音乐播放的进度同步滚动更新。

案例目录结构:

实现源码:

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <!-- 设置favicon -->
    <link rel="shortcut icon" href="./assets/author_favicon.ico" type="image/x-icon">
    <!-- 引入css -->
    <link rel="stylesheet" href="./css/index.css">
    <title>歌词滚动效果</title>
</head>

<body>
<!--
<audio controls src="./assets/海阔天空.mp3"></audio>
-->
<audio controls src="http://media.simoniu.com/海阔天空.mp3"></audio>

<div class="container">
    <ul class="lrc-list"></ul>
</div>

<!-- 引入js -->
<script src="./js/mock.js"></script>
<script src="./js/index.js"></script>
</body>

</html>

index.css

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

li {
    list-style: none;
}


body {
    width: 100vw;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    background-color: #010409;
    padding: 50px;
    color: #666;
    text-align: center;
}


audio {
    width: 600px;
}


.container {
    width: 100%;
    height: 600px;
    display: flex;
    flex-direction: column;
    margin-top: 30px;
    overflow: hidden;
}


.container ul {
    transition: all 0.3s ease-in;
}


.container li {
    transition: all 0.3s ease;
    height: 30px;
    line-height: 30px;
}


.container li.active {
    transform: scale(1.4);
    color: #fff;
}

歌词数据 mock.js

const lrc = `[00:00.000] 作词 : 黄家驹\n[00:01.000] 作曲 : 黄家驹\n[00:02.000] 编曲 : Beyond/梁邦彦\n[00:03.000] 制作人 : Beyond/梁邦彦\n[00:18.466]今天我 寒夜里看雪飘过\n[00:25.110]怀着冷却了的心窝漂远方\n[00:30.950]风雨里追赶 雾里分不清影踪\n[00:37.229]天空海阔你与我\n[00:40.291]可会变 (谁没在变)\n[00:43.440]多少次 迎着冷眼与嘲笑\n[00:50.050]从没有放弃过心中的理想\n[00:55.907]一刹那恍惚 若有所失的感觉\n[01:02.133]不知不觉已变淡\n[01:05.243]心里爱 (谁明白我)\n[01:08.801]原谅我这一生不羁放纵爱自由\n[01:15.799]也会怕有一天会跌倒\n[01:22.008]背弃了理想 谁人都可以\n[01:28.276]哪会怕有一天只你共我\n[01:34.102]\n[01:42.695]今天我 寒夜里看雪飘过\n[01:49.284]怀着冷却了的心窝漂远方\n[01:55.189]风雨里追赶 雾里分不清影踪\n[02:01.405]天空海阔你与我\n[02:04.535]可会变 (谁没在变)\n[02:08.014]原谅我这一生不羁放纵爱自由\n[02:15.040]也会怕有一天会跌倒\n[02:21.279]背弃了理想 谁人都可以\n[02:27.531]哪会怕有一天只你共我\n[02:33.633]\n[03:08.454]仍然自由自我 永远高唱我歌\n[03:15.064]走遍千里\n[03:19.739]原谅我这一生不羁放纵爱自由\n[03:26.734]也会怕有一天会跌倒\n[03:33.005]背弃了理想 谁人都可以\n[03:39.257]哪会怕有一天只你共我\n[03:45.496]背弃了理想 谁人都可以\n[03:51.756]哪会怕有一天只你共我\n[03:57.201]原谅我这一生不羁放纵爱自由\n[04:04.204]也会怕有一天会跌倒\n[04:10.456]背弃了理想 谁人都可以\n[04:16.647]哪会怕有一天只你共我\n[04:22.828]\n[04:31.852] 录音 : Shunichi Yokoi\n[04:40.876] 混音 : Shunichi Yokoi\n[04:49.900] 录音室 : Greenbird St./Tokyu Fun St./West Side St.(Tokyo/From Jan/to Apr./1993)\n[04:58.924] 母带工程师 : Setsu Hisai at Tokyu Fun St.\n[05:07.948] 弦乐 : 桑野圣乐团 (Kuwano Strings)\n[05:16.972] OP : Amuse Inc. & Fun House Inc.\n[05:25.996] SP : Amuse H.K. Ltd.`

index.js

const lrcData = formatLrc(lrc)
const audio = document.querySelector('audio')
const container = document.querySelector('.container')
const ul = document.querySelector('.lrc-list')
const containerHeight = container.clientHeight

function formatLrc(data) {
    const lrcs = []
    const lrc = data.split('\n').forEach(item => {
        lrcs.push({
            time: formatTime(item),
            lrc: item.split(']')[1]
        })
    })
    return lrcs
}

function formatTime(str) {
    const res = str.split(']')[0].substring(1)
    return res.split(':')[0] * 60 + res.split(':')[1] * 1
}

function monitorAudio() {
    const currentAudioTime = audio.currentTime
    const index =
        lrcData.findIndex(item => {
            return item.time > currentAudioTime
        }) - 1
    return index < 0 ? lrcData.length - 1 : index
}

function createLrcElement() {
    const frag = document.createDocumentFragment()
    lrcData.forEach(item => {
        const li = document.createElement('li')
        li.innerText = item.lrc
        frag.appendChild(li)
    })
    ul.appendChild(frag)
}

createLrcElement()

const liHeight = ul.children[0].clientHeight
const maxUpLength = lrcData.length * liHeight - containerHeight

function setUpLength() {
    const index = monitorAudio()
    let upLength = liHeight * index + liHeight / 2 - containerHeight / 2
    if (upLength < 0) {
        upLength = 0
    }
    if (upLength > maxUpLength) {
        upLength = maxUpLength
    }
    ul.style.transform = `translateY(-${upLength}px)`
    const lis = [...ul.children]
    lis.forEach(item => {
        item.classList.remove('active')
    })
    const li = ul.children[index]
    if (li) {
        li.classList.add('active')
    }
}

audio.addEventListener('timeupdate', function () {
    setUpLength()
})

运行效果: