其他分享
首页 > 其他分享> > js歌词逐字滚动效果

js歌词逐字滚动效果

作者:互联网

js歌词逐字滚动效果

@Null - 滨

先上效果图
在这里插入图片描述

目录结构:
在这里插入图片描述

歌词文本music.txt
将酷狗歌词KRC用“酷狗歌词(krc)加解密工具”进行解密后可以得到这种歌词文本

在这里插入图片描述
HTML代码

<!DOCTYPE html>
<html lang="en">
<head>
    <!-- 网页图标 -->
    <link rel="icon" href="data:image/png;base64,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">
    <meta charset="UTF-8">
    <title>Music</title>
    <!-- 引入css文件 -->
    <link rel="stylesheet" href="./code.css">
</head>
<body>
<canvas id="canvas">浏览器不支持Canvas,请升级浏览器!</canvas>
<audio controls id="myAudio" preload src="./music/music.mp3">当前浏览器不支持播放音频</audio>
<div class="music">
    <div class="songInfo">
        <p id="songName"></p>
        <p id="singerName"></p>
    </div>
    <div class="player">
        <img class="animation" id="img" src="./images/cover.jpg">
        <a class="btn" href="javascript:" id="btn" onclick="play_pause()"></a>
    </div>
    <div class="lrc_box">
        <div class="lrc" id="lrc">
            <p class="blank"></p>
        </div>
    </div>
</div>
<div class="control">
    <p><a class="btn" href="javascript:" id="btn2" onclick="play_pause()"></a></p>
    <div class="time_progressBar">
        <div class="time">
            <p class="currentTime" id="currentTime">00:00</p>
            <p>/</p>
            <p class="duration" id="duration">00:00</p>
        </div>
        <div class="progressBar" id="progressBar">
            <p class="progress" id="progress"></p>
        </div>
    </div>
    <div class="volume" id="volume">
        <div class="volumeBox" id="volumeBox">
            <div class="volumeBar" id="volumeBar">
                <p class="currentVolume" id="currentVolume"></p>
            </div>
            <div class="volumeText">
                <span id="currentVolumeText">0</span><span>%</span>
            </div>
        </div>
        <div id="volumeBtn">
            <!-- 音量按钮svg图标 -->
            <svg class="svg" height="26" viewBox="0 0 1080 1080" width="26">
                <path class="path"
                      d="M541.2 132.3c-7.2-3.3-15.2-5-23.3-5-13.9 0-26.8 5.1-36.5 14.4L280.6 312.4h-158c-11.5 0-23.9 3.9-33.6 10.4h-4l-8.7 9.2c-7.5 7.9-12.2 20.8-12.2 33.8v290.7c0 15 6.7 31.4 16.9 41.6 10.2 10.3 26.6 16.9 41.6 16.9h158.2l200.7 165.6c8.4 7.9 23.3 16 35.6 16 5.6 0 16.5 0 27.1-8 10-4.2 17.8-10.4 23.1-18.6 5.7-8.6 8.4-19.1 8.4-32V184c0-12.9-2.8-23.4-8.4-32-5.8-9-14.6-15.6-26.1-19.7z m-24.8 57.4v642.9L310.2 662.5l-8.2-6.8v0.1H123.3V371.7h179l214.1-182z"
                      id="volumeIcon"></path>
                <path class="path"
                      d="M752.7 376.7c-23.8-27.4-48.4-40.2-53.7-42.1h-1.6l-1.9-1.3c-3.2-2.2-7.4-3.3-12-3.3-11.9 0-24.9 7.6-27.8 16.4l-0.3 1-0.6 0.9c-3.8 5.8-4.3 14.2-1.4 22.7 2.9 8.4 8.6 15.1 14.6 17.1l0.6 0.2 0.6 0.3c0.7 0.4 17.2 9.5 33.6 29.7 15.1 18.5 33.1 50.3 33.1 96.7 0 96.6-54.4 128.5-60.7 131.9-13.8 9.4-23 27.9-14.6 40.4l0.3 0.4 0.2 0.5c4.3 8.7 18.9 18.6 27.3 18.6 5.5 0 8.4-0.1 11.7-3.8l2.2-2.2H704.7c6.2-2.3 28.7-15.6 50.6-44.2 20.6-26.9 45.1-74.4 45.1-147.4 0-64.2-25.9-107.5-47.7-132.5z"
                      id="volume_1_icon"
                      style="display:inline-block"></path>
                <path class="path"
                      d="M899.3 300c-20.9-32.7-46.5-61.1-75.9-84.2l-0.1-0.1c-1.3-1.1-2.2-1.8-2.9-2.4-4.5-2.8-9.6-4.3-15.1-4.3-10.3 0-21.6 5.3-31.1 14.5l-0.1 0.1c-8.4 13.5-3.9 35.9 9 45.2l0.1 0.1c6.1 2.5 14.7 9.7 20.8 15.4 9.4 8.7 23.6 23.7 38.1 45.6 24.3 36.8 53.3 99.7 53.3 190.8 0 91-27.4 153.9-50.3 190.6-13.7 22-27.2 36.9-36.1 45.6-8.6 8.3-15.3 13.5-20.1 15.5l-0.1 0.1c-13.9 9.8-17.2 28.6-8.1 46.7 3.8 7.7 17.2 12.9 27.3 12.9 3.8 0 13.9 0 17.4-3.5 0.7-0.7 1.5-1.4 3.3-2.8 28.1-22.9 52.6-51 72.7-83.8 38.8-63.2 58.5-137.6 58.5-221.4 0-83.4-20.4-157.6-60.6-220.6z"
                      id="volume_2_icon"
                      style="display:none"></path>
                <path class="path"
                      d="m670.797714 328.2c10.532571 0 20.918857 3.876571 28.891429 11.776l119.149714 117.467429 119.222857-117.394286a40.96 40.96 0 0 1 57.782857 0 39.789714 39.789714 0 0 1-0.146285 56.978286l-119.003429 117.394285 119.003429 117.394286a39.789714 39.789714 0 0 1 0 56.905143 41.106286 41.106286 0 0 1-57.709715 0l-119.149714-117.321143-119.222857 117.248a41.106286 41.106286 0 0 1-57.636572 0 39.789714 39.789714 0 0 1 0-56.978286l119.003429-117.321142-119.003429-117.394286a39.789714 39.789714 0 0 1 0-56.978286 40.813714 40.813714 0 0 1 28.818286-11.776z"
                      id="volume_mute_icon"
                      style="display: none"></path>
            </svg>
        </div>
    </div>
</div>
</body>
<!-- 引入js文件 -->
<script type="text/javascript" src="./code.js"></script>
</html>

js代码

let duration = 0;
let percentage = null;
let currentVolume = 0;
let muted = false;
let timer = {startPlay: null, playing: null, warp: null, peachBlossom: null};
onload = function () {
    Window.myAudio = document.getElementById("myAudio");
    myAudio.volume = 0.5;
    currentVolume = myAudio.volume;
    setVolume(currentVolume);
}
let currentTime = document.getElementById("currentTime");
let progress = document.getElementById("progress");
//  音频播放结束后触发的事件
myAudio.addEventListener('ended', function () {
    clearInterval(timer.playing);
    clearInterval(timer.peachBlossom);
    peachBlossom.disappear();
    img.classList.remove("animation");
    btn.style.opacity = "1";
    btn2.classList.remove("btn_change");
    currentTime.innerText = "00:00";
    progress.style.width = "0";
    lrc.style.transform = "translateY(0px)";
    del_backgroundImage(lyric.length - 1);
})
myAudio.addEventListener("timeupdate", function () {
    //监听音频播放的实时时间事件
    let timeDisplay = Math.round(myAudio.currentTime * 100) / 100;
    //  判断是否正在修改播放进度(正在修改进度时,不改变进度条样式)
    if (!changeProgress) {
        progress.style.width = Math.round(timeDisplay / percentage * 100) / 100 + "%";
    }
    //用秒数来显示当前播放进度
    timeDisplay = Math.floor(timeDisplay);
    //分钟
    let minutes = parseInt(timeDisplay / 60);
    if (minutes < 10) {
        minutes = "0" + minutes;
    }
    //秒
    let seconds = Math.round(timeDisplay % 60);
    if (seconds < 10) {
        seconds = "0" + seconds;
    }
    currentTime.innerText = minutes + ":" + seconds;
})
class PeachBlossom {
    constructor(width,height,petalNumber) {
        this.peachBlossom = new Image();
        this.peachBlossom.src = "./images/peachBlossom.png";
        let canvas = document.getElementById("canvas");
        canvas.width = width;
        canvas.height = height;
        this.ctx = canvas.getContext("2d");
        this.width = width;
        this.height = height;
        this.petalNumber = petalNumber;
        let that = this;
        this.peachBlossom.onload = function () {
            that.drawWidth = that.peachBlossom.width / 10; //  50
            that.drawHeight = that.peachBlossom.height / 10;   //  50
            that.initialize();
        }
    }
    //  更新canvas
    update(){
        //  清空画布
        this.ctx.clearRect(0,0,this.width,this.height);
        // for循环更新所有花瓣坐标
        for (let i = 0; i < 100; i++) {
            this.petal[i].showX += this.petal[i].angle;
            this.petal[i].showY +=  this.petal[i].speed;
            //  判断当前花瓣是否超出屏幕高度,如果超出屏幕高度再重新随机选择一个花瓣,显示位置也随机
            if(this.petal[i].showY > this.height+this.drawHeight){
                let index = Math.floor(Math.random()*(this.petalNumber-1)+1);
                this.petal[i].drawX = (index % 10) * this.drawWidth;
                this.petal[i].drawY = Math.floor(index / 10) * this.drawHeight;
                this.petal[i].showX = Math.floor(Math.random()*(this.width-1)+1);
                this.petal[i].showY = -(Math.random() * this.height);
                this.petal[i].speed = (Math.random()*(13-7+1)+7) / 10;
                this.petal[i].angle = Math.random();
                if(Math.round(Math.random()*10)%2){
                    this.petal[i].angle = -this.petal[i].angle;
                }
            }
            //  渲染更新后的花瓣
            this.ctx.drawImage(this.peachBlossom,this.petal[i].drawX,this.petal[i].drawY,this.drawWidth,this.drawHeight,this.petal[i].showX,this.petal[i].showY,this.drawWidth,this.drawHeight)
        }
    }
    //  花瓣渐渐消失
    disappear(){
        let that = this;
        let transparency = 1;
        let interval = setInterval(function () {
            transparency -= 0.01;
            //  这里里的this指向的是Window,所以要把Class类的this指向赋值给that
            that.ctx.clearRect(0,0,that.width,that.height);
            that.ctx.globalAlpha = transparency;
            for (let i = 0; i < that.petal.length; i++) {
                that.petal[i].showX += that.petal[i].angle;
                that.petal[i].showY +=  that.petal[i].speed;
                that.ctx.drawImage(that.peachBlossom,that.petal[i].drawX,that.petal[i].drawY,that.drawWidth,that.drawHeight,that.petal[i].showX,that.petal[i].showY,that.drawWidth,that.drawHeight)
            }
            if (transparency < 0){
                //  已消失,重置canvas的数据
                that.ctx.clearRect(0,0,that.width,that.height);
                that.ctx.globalAlpha = 1;
                //  重置数据
                that.initialize();
                //  清除当前定时器
                clearInterval(interval);
            }
        },18)
    }
    initialize(){
        this.petal = [];
        for (let i = 0; i < 100; i++) {
            let obj = {};
            let index = Math.floor(Math.random()*(this.petalNumber-1)+1);
            //  剪切图片的X坐标
            obj.drawX = (index % 10) * this.drawWidth;
            //  剪切图片的Y坐标
            obj.drawY = Math.floor(index / 10) * this.drawHeight;
            //  显示在屏幕的X坐标
            obj.showX = Math.floor(Math.random()*(this.width-1)+1);
            //  显示在屏幕的Y坐标
            obj.showY = -(Math.random() * this.height);
            //  飘落速度
            obj.speed = (Math.random()*(13-7+1)+7) / 10;
            //  飘落倾斜角度
            obj.angle = Math.random();
            if (obj.angle > 0.5){
                obj.angle = Math.random();
            }
            if(Math.round(Math.random()*10)%2){
                obj.angle = -obj.angle
            }
            this.petal.push(obj)
        }
    }
}
//  初始化对象
let peachBlossom = new PeachBlossom(window.screen.width,window.screen.height,100);
//  获取id为img的元素
let img = document.getElementById("img");
let lrc = document.getElementById("lrc");
function playMusic() {
    if (duration <= 0) {
        //  设置音频总时长
        setDuration(myAudio.duration);
    }
    //  获取当前音频播放时间
    let currentTime = myAudio.currentTime;
    if (currentTime == myAudio.duration) {
        img.classList.add("animation");
        myAudio.currentTime = 0;
        document.getElementById("lrc").style.transform = "translateY(0px)";
    } else if (currentTime > 0) {
        currentTime = currentTime.toFixed(3);
        let arr = currentTime.split(".");
        currentTime = (Number(arr[0]) * 1000) + Number(arr[1]);
    }
    //  定时更新、渲染canvas的数据
    timer.peachBlossom = setInterval(function () {
        peachBlossom.update()
    },18)
    img.style.animationPlayState = "running";
    //  当前是播放状态,隐藏播放按钮
    btn.style.opacity = "0";
    //  给第二个播放按钮添加类名以修改样式
    btn2.classList.add("btn_change");
    if (currentTime < lyric[0][0][0]) {
        //  当前未开始渲染歌词
        document.getElementById("line_0").classList.add("currentLine");
        myAudio.play();
        let start = new Date().getTime()
        //  未需要渲染歌词,(定时器)等待需要渲染歌词再执行playing函数
        timer.startPlay = setTimeout(function () {
            playing(0, 0, 0)
        }, lyric[0][0][0] - currentTime)
    } else if (currentTime > lyric[lyric.length - 1][0][0]) {
        //  渲染最后一行歌词
        let lastLine = lyric.length - 1;
        let translate = -(lastLine * 40 - 80);
        document.getElementById("line_" + lastLine).classList.add("currentLine");
        for (let i = 0; i < lyric[lastLine].length; i++) {
            document.getElementById("word_" + lastLine + "_" + i).style.backgroundImage = "-webkit-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(255,255,255,0) 100%), -webkit-linear-gradient(left, #f00 100%, #000 0%)";
            if (currentTime < lyric[lastLine][i][0] + lyric[lastLine][i][1]) {
                //  调整歌词,等待换行
                adjustLyricProgress(currentTime, lastLine, i, translate);
                break;
            } else if (i === lyric[lastLine].length - 1) {
                //      歌词全部渲染完毕(已唱完)
                document.getElementById("lrc").style.transform = "translateY(" + translate + "px)";
                myAudio.play();
            }
        }
    } else {
        let translate = 0;
        for (let i = 1; i <= lyric.length; i++) {
            if (currentTime < lyric[i][0][0]) {
                if (i > 2) {
                    translate = -((i-3) * 40);
                }
                //  给当前行歌词添加类名以调整当前行歌词样式
                document.getElementById("line_" + (i - 1)).classList.add("currentLine");
                for (let j = 0; j < lyric[i - 1].length; j++) {
                    document.getElementById("word_" + (i - 1) + "_" + j).style.backgroundImage = "-webkit-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(255,255,255,0) 100%), -webkit-linear-gradient(left, #f00 100%, #000 0%)";
                    //  判断当前是否是当前行歌词的最后一个字
                    if (j === lyric[i - 1].length - 1) {
                        //  判断当前行歌词最后一个字是否渲染完成
                        if (currentTime < lyric[i - 1][j][0] + lyric[i - 1][j][1]) {
                            //  当前行最后一字未完成,调整歌词
                            adjustLyricProgress(currentTime, i - 1, j, translate);
                        } else {
                            //  当前行最后一字已完成
                            myAudio.play();
                            lrc.style.transform = "translateY(" + translate + "px)";
                            timer.startPlay = setTimeout(function () {
                                document.getElementById("line_" + (i - 1)).classList.remove("currentLine");
                                del_backgroundImage(i - 1);
                                document.getElementById("line_" + i).classList.add("currentLine");
                                //  当前行已结束,调整歌词
                                translate -= 40;
                                lrc.style.transform = "translateY(" + translate + "px)";
                                playing(i, 0, translate);
                            }, lyric[i][0][0] - currentTime)

                        }
                    } else if (currentTime < lyric[i - 1][j + 1][0]) {
                        adjustLyricProgress(currentTime, i - 1, j, translate);
                        break;
                    }
                }
                break;
            }
        }
    }
}
function pauseMusic() {
    //  暂停,清楚所有定时器
    clearInterval(timer.peachBlossom);
    clearInterval(timer.playing);
    clearTimeout(timer.startPlay);
    clearTimeout(timer.warp);
    //  显示播放按钮
    btn.style.opacity = "1";
    //  删除第二个播放按钮类名以修改样式
    btn2.classList.remove("btn_change");
    //  暂停动画
    img.style.animationPlayState = "paused";
    //  暂停音频
    myAudio.pause();
}
function playing(lineNumber, wordNumber, translate = 0) {
    clearInterval(timer.peachBlossom);
    //  获取当前字的元素
    let element = document.getElementById("word_" + lineNumber + "_" + wordNumber);
    //  定时器间隔时间
    let intervalTime = 18;
    //  当前字的时长
    let duration = lyric[lineNumber][wordNumber][1];
    //  当前字已播放时间
    let pastTime = 0;
    //  获取当前行的元素
    let currentLine = document.getElementById("line_" + lineNumber);
    //  开始时间
    let startTime = new Date().getTime();
    timer.playing = setInterval(function () {
        //  定时执行里面代码
        pastTime += intervalTime;
        //  设置样式
        element.style.backgroundImage = "-webkit-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(255,255,255,0) 100%), -webkit-linear-gradient(left, #f00 " + ((pastTime / duration).toFixed(2) * 100) + "%, #000 0%)";
        //  判断当前字是否已渲染完成
        if (pastTime >= duration) {
            //  当前字已播放完成,下一字
            wordNumber++;
            //  判断当前行是否已播放完成
            if (wordNumber === lyric[lineNumber].length) {
                //  下一行
                lineNumber++;
                if (lineNumber === lyric.length) {
                    //  最后一行播放完成,播放结束
                    timer.peachBlossom = setInterval(function () {
                        peachBlossom.update()
                    },intervalTime)
                    // clearInterval(timer.playing);
                    //  播放结束
                    return;
                } else {
                    //  下一行
                    //  计算换行的时间
                    let warpIntervalTime = lyric[lineNumber][0][0] - (lyric[lineNumber - 1][wordNumber - 1][0] + lyric[lineNumber - 1][wordNumber - 1][1]);
                    wordNumber = 0
                    if (warpIntervalTime > 5) {
                        clearInterval(timer.playing);
                        timer.peachBlossom = setInterval(function () {
                            peachBlossom.update()
                        },intervalTime);
                        timer.warp = setTimeout(() => {
                            if (lineNumber > 2 && lineNumber < lyric.length - 2) {
                                //  歌词元素继续向上移动
                                translate -= 40;
                                //  设置歌词向上移动
                                lrc.style.transform = "translateY(" + translate + "px)";
                            }
                            //  清除当前行的class类名
                            currentLine.classList.remove("currentLine");
                            //  重新获取当前行
                            currentLine = document.getElementById("line_" + lineNumber);
                            //  给当前行设置class类名
                            currentLine.classList.add("currentLine");
                            //  继续渲染歌词
                            clearInterval(timer.peachBlossom);
                            playing(lineNumber, 0, translate);
                            //  清除上一行已渲染的歌词样式
                            del_backgroundImage(lineNumber - 1);
                        }, warpIntervalTime)
                        return;
                    }
                    //  继续下一行
                    if (lineNumber > 2 && lineNumber < lyric.length - 2) {
                        //  歌词元素继续向上移动
                        translate -= 40;
                        //  设置歌词向上移动
                        lrc.style.transform = "translateY(" + translate + "px)";
                    }
                    //  清除当前行的class类名
                    currentLine.classList.remove("currentLine");
                    //  重新获取当前行
                    currentLine = document.getElementById("line_" + lineNumber);
                    //  给当前行设置class类名
                    currentLine.classList.add("currentLine");
                    //  清除上一行已渲染的歌词样式
                    del_backgroundImage(lineNumber - 1);
                }
            }
            //  重新下一个字的元素
            element = document.getElementById("word_" + lineNumber + "_" + wordNumber);
            //  获取时间戳
            let endTime = new Date().getTime();
            //  上一个字执行结束时间减去上一个字开始执行时间,再减去上一个字的时长得出误差时间
            let errorTime = (endTime - startTime) - duration;
            //  将结束时间作为下一个字开始执行的时间
            startTime = endTime;
            //  重新计算当前字的时长(当前字时长减去定时器误差)
            duration = lyric[lineNumber][wordNumber][1] - errorTime;
            //  当前字已播放时间设为0
            pastTime = 0;
        }
        peachBlossom.update();
    }, intervalTime)
}
let progressBar = document.getElementById("progressBar");
let body = document.getElementsByTagName("body")[0];
let changeProgress = false;
//  监听元素鼠标按下事件
progressBar.onmousedown = function (event) {
    //  判断是否为鼠标左键       0为鼠标左键,1为鼠标中键,2为鼠标右键
    if (event.button === 0) {
        changeProgress = true;
        //  修改进度(样式)
        progress.style.width = ((Math.floor(((event.clientX - progressBar.getBoundingClientRect().left) / progressBar.getBoundingClientRect().width) * 10000) / 100)) + "%";
        //  监听鼠标移动事件
        body.onmousemove = function (event) {
            let progressPercentage = Math.floor(((event.clientX - progressBar.getBoundingClientRect().left) / progressBar.getBoundingClientRect().width) * 10000) / 100;
            if (progressPercentage < 0) {
                progressPercentage = 0;
            } else if (progressPercentage > 100) {
                progressPercentage = 100;
            }
            progress.style.width = progressPercentage + "%";
        }
    }
}
//  监听鼠标松开事件
body.onmouseup = function (event) {
    //  判断当前是否在拖动播放进度条
    if (changeProgress) {
        changeProgress = false;
        body.onmousemove = null;
        myAudio.currentTime = myAudio.duration / 100 * parseFloat(progress.style.width);
        let currentLine = document.getElementsByClassName("currentLine")[0];
        if (currentLine) {
            del_backgroundImage(currentLine.getAttribute("data-lineNumber"))
            currentLine.classList.remove("currentLine");
        }
        // 修改了播放进度,先暂停再播放
        pauseMusic();
        playMusic();
    }
}
//  根据id获取元素
let volume = document.getElementById("volume");
let currentVolumeElement = document.getElementById("currentVolume");
let currentVolumeText = document.getElementById("currentVolumeText");
let volumeBox = document.getElementById("volumeBox");
let volumeBar = document.getElementById("volumeBar");
//  监听volumeBar元素的鼠标按下事件
volumeBar.onmousedown = function (event) {
    currentVolume = Math.round((1 - ((event.clientY - volumeBar.getBoundingClientRect().top) / volumeBar.getBoundingClientRect().height)) * 100) / 100
    setVolume(currentVolume);
    if (muted) {
        //  更改静音状态
        muted = false;
        //  更改音频的静音状态
        myAudio.muted = false;
    }
    //  监听volumeBox元素的鼠标移动事件
    volumeBox.onmousemove = function (event) {
        currentVolume = Math.round(100 - (event.clientY - volumeBar.getBoundingClientRect().top)) / 100;
        if (currentVolume < 0) {
            currentVolume = 0;
        } else if (currentVolume > 1) {
            currentVolume = 1
        }
        setVolume(currentVolume);
    }
}
//  监听volume元素的鼠标松开事件
volume.onmouseup = function () {
    //  鼠标松开音量元素的区域,取消监听volumeBox元素的鼠标移动
    volumeBox.onmousemove = null;
}
//  监听volume元素的鼠标移出事件
volume.onmouseleave = function () {
    //  鼠标移出音量元素的区域,取消监听volumeBox元素的鼠标移动
    volumeBox.onmousemove = null;
}
//  监听volume元素的鼠标滚动事件
volume.onmousewheel = function (event) {
    //  参数deltaY为100时,鼠标滚轮向下滚动;参数deltaY为-100时,鼠标滚轮向上滚动
    if (event.deltaY > 0) {
        if (muted) {
            //  当前已是静音,不能减音量。return返回
            return;
        }
        currentVolume -= 0.05;
    } else {
        if (muted) {
            //  当前是静音,所以直接把音量加到5
            currentVolume = 0.05;
            //  更改静音状态
            muted = false;
            //  更改音频的静音状态
            myAudio.muted = muted;
        } else {
            //  当前非静音,所以在原音量的基础上加5
            currentVolume += 0.05;
        }
    }
    setVolume(currentVolume);
}
//  根据id获取元素
let volumeBtn = document.getElementById("volumeBtn");
let volume_1_icon = document.getElementById("volume_1_icon");
let volume_2_icon = document.getElementById("volume_2_icon");
let volume_mute_icon = document.getElementById("volume_mute_icon");
//  监听volumeBtn(音量按钮)元素的鼠标点击事件
volumeBtn.onclick = function (event) {
    muted = !muted;
    myAudio.muted = muted;
    if (muted) {
        //  设置为静音
        setVolume(0, false)
    } else {
        setVolume(myAudio.volume, false)
    }
}
//  设置音量
function setVolume(currentVolume, changeVolume = true) {
    if (currentVolume > 1) {
        currentVolume = 1;
    } else if (currentVolume <= 0) {
        currentVolume = 0;
        volume_1_icon.style.display = "none";
        volume_2_icon.style.display = "none";
        volume_mute_icon.style.display = "inline-block";
    } else {
        currentVolume = Math.round(currentVolume * 100) / 100;
    }
    if (currentVolume > 0.33) {
        volume_1_icon.style.display = "inline-block";
        volume_2_icon.style.display = "inline-block";
        volume_mute_icon.style.display = "none";
    } else if (currentVolume > 0) {
        volume_1_icon.style.display = "inline-block";
        volume_2_icon.style.display = "none";
        volume_mute_icon.style.display = "none";
    }
    if (changeVolume) {
        myAudio.volume = currentVolume;
    }
    currentVolumeElement.style.height = (currentVolume * 100) + "px";
    currentVolumeText.innerText = parseInt(currentVolume * 100);
}
let lyric = getLrc();
let btn = document.getElementById("btn");
let btn2 = document.getElementById("btn2");
//  监听播放、播放的按钮的点击事件
function play_pause() {
    //  根据音频的播放状态执行对应函数
    if (myAudio.paused) {
        playMusic();
    } else {
        pauseMusic();
    }
}
//  设置进度条的时间
function setDuration(time) {
    duration = time;
    percentage = Math.round(duration) / 100;
    let minutes = "00:";
    if (duration >= 60) {
        minutes = parseInt(duration / 60);
        if (minutes < 10) {
            minutes = "0" + minutes + ":"
        }
    }
    let seconds = parseInt(duration % 60);
    if (seconds < 10) {
        seconds = "0" + seconds
    }
    document.getElementById("duration").innerText = minutes + seconds;
}
//  调整歌词进度
function adjustLyricProgress(currentTime, lineNumber, wordNumber, translate) {
    lrc.style.transform = "translateY(" + translate + "px)";
    let pastTime = currentTime - lyric[lineNumber][wordNumber][0];
    //  获取当前字的元素
    let element = document.getElementById("word_" + lineNumber + "_" + wordNumber);
    //  定时器间隔时间
    let intervalTime = 18;
    //  当前字的时长
    let duration = lyric[lineNumber][wordNumber][1];
    if (duration % intervalTime > intervalTime / 2) {
        duration += duration % intervalTime;
    } else {
        duration -= duration % intervalTime;
    }
    element.style.backgroundImage = "-webkit-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(255,255,255,0) 100%), -webkit-linear-gradient(left, #f00 " + ((pastTime / duration).toFixed(2) * 100) + "%, #000 0%)";
    myAudio.play();
    timer.playing = setInterval(function () {
        pastTime += intervalTime;
        element.style.backgroundImage = "-webkit-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(255,255,255,0) 100%), -webkit-linear-gradient(left, #f00 " + ((pastTime / duration).toFixed(2) * 100) + "%, #000 0%)";
        if (pastTime >= duration) {
            //  当前字结束
            clearInterval(timer.playing);
            if (wordNumber < lyric[lineNumber].length - 1) {
                playing(lineNumber, wordNumber + 1, translate);
            } else if (lineNumber < lyric.length - 1) {
                //  判断是否最后一行,如果不是最后一行则继续执行
                translate -= 40;
                lrc.style.transform = "translateY(" + translate + "px)";
                timer.startPlay = setTimeout(function () {
                    document.getElementById("line_" + lineNumber).classList.remove("currentLine");
                    del_backgroundImage(lineNumber);
                    document.getElementById("line_" + (lineNumber + 1)).classList.add("currentLine");
                    playing(lineNumber + 1, 0, translate);
                }, lyric[lineNumber + 1][0][0] - (lyric[lineNumber][wordNumber][0] + lyric[lineNumber][wordNumber][1]))
            }
        }
    }, intervalTime)
}
//  清楚上一行渲染的歌词样式
function del_backgroundImage(lineNumber) {
    for (let i = 0; i < lyric[lineNumber].length; i++) {
        document.getElementById("word_" + lineNumber + "_" + i).style.backgroundImage = "-webkit-linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 100%), -webkit-linear-gradient(left, #000 0%, #000 0%)";
    }
}
//  从txt文件获取歌词数据
function getLrc() {
    let txt = "";
    let ajax = new XMLHttpRequest();
    ajax.open("GET", "./music/music.txt", false);
    ajax.onreadystatechange = function () {
        txt = ajax.responseText;
    };
    ajax.send(null);
    //  将字符串以回车符(\n)分割成每一行文本为一个数组元素
    let arr = txt.split("\n");
    //  歌词内容
    let lyric = [];
    let lrcElement = document.getElementById("lrc")
    //  有些不是歌词,所以lineNumber从0开始,判断是歌词行,再加1。这样动态添加的元素的id和class类名序号能跟数组索引对上
    let lineNumber = 0;
    for (let i = 0; i < arr.length; i++) {
        //  删除每行文本全部的\r符号
        arr[i] = arr[i].replace(/[\r]/g, "");
        //  如果当前行文本存在"<"和">"符号,说明该行文本是歌词
        if (arr[i].indexOf("<") > -1 && arr[i].indexOf(">") > -1) {
            lrcElement.innerHTML += "<p id=line_" + lineNumber + " data-lineNumber=" + lineNumber + "></p>";
            //  每行歌词是一个一维数组元素,每个字是一个二维数组(包含"开始时间","时长","文字")
            //  每一行的开始时间(转为数值类型)
            let startTime = Number(arr[i].slice(1, arr[i].indexOf(",")));
            //  截取改行文本从"]"符号开始,一直到最后的文本
            arr[i] = arr[i].slice(arr[i].indexOf("]") + 1);
            //  每一行的数据
            let line = [];
            let nextTime = 0;
            let lineElement = document.getElementById("line_" + lineNumber)
            while (arr[i]) {
                let str = arr[i].slice(0, arr[i].indexOf(">") + 1);
                let time = Number(str.slice(str.indexOf(",") + 1, str.lastIndexOf(",")));
                nextTime += time;
                arr[i] = arr[i].slice(str.length);
                let word = "";
                let index = arr[i].indexOf(String(nextTime));
                if (index > -1) {
                    word = arr[i].slice(0, index - 1);
                } else {
                    word = arr[i];
                }
                lineElement.innerHTML += "<span id=word_" + lineNumber + "_" + line.length + " class=word_" + lineNumber + "_" + line.length + ">" + word + "</span>";
                arr[i] = arr[i].slice(word.length);
                line.push([startTime, time, word]);
                startTime += time;
            }
            lyric.push(line);
            lineNumber++;
        } else if (arr[i].indexOf("total:") > -1) {
            let total = arr[i].slice(7, arr[i].indexOf("]"))
            if (total) {
                setDuration(total / 1000);
            }
        } else if (arr[i].indexOf("ti:") > -1) {
            //  歌名
            let songName = arr[i].slice(4, arr[i].indexOf("]"));
            document.getElementById('songName').innerText = "歌名:" + songName;
        } else if (arr[i].indexOf("ar:") > -1) {
            //  歌手
            let singerName = arr[i].slice(4, arr[i].indexOf("]"));
            document.getElementById('singerName').innerText = "歌手:" + singerName;
        }
    }
    return lyric;
}

css代码

body {
    user-select: none;
    overflow: hidden;
}
#canvas {
    position: absolute;
}
audio {
    display: none;
}
.music {
    margin: 20px auto 0;
    text-align: center;
    font-family: "楷体", "楷体_GB2312";
    cursor: default;
}
.music .songInfo p {
    display: inline-block;
    margin: 0 50px;
}
.player {
    position: relative;
    margin: 50px auto;
    width: 200px;
    height: 200px;
    background-color: #333;
    border-radius: 50%;
    text-align: center;
}
.player img {
    margin: 30px;
    width: 140px;
    height: 140px;
    border-radius: 50%;
}
.player .animation {
    animation: rotate 8s linear infinite;
    animation-play-state: paused;
}
@keyframes rotate {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg)
    }
}
.btn {
    display: inline-block;
    width: 40px;
    height: 40px;
    background-color: rgba(0, 0, 0, 0.2);
    border: 1px solid #fff;
    border-radius: 50%;
    margin: -20px;
    position: absolute;
    top: 50%;
    left: 50%;
}
.btn:after {
    content: "";
    display: inline-block;
    border: 12px solid transparent;
    border-left-color: #fff;
    border-radius: 15%;
    position: absolute;
    top: 8px;
    left: 16px;
}
.music .songInfo {
    font-size: 28px;
}
.music .lrc_box {
    height: 362px;
    overflow: hidden;
}
.lrc_box .lrc {
    /* 歌词向上移动的过渡动画 */
    transition: transform 200ms;
}
.lrc_box .lrc .blank {
    height: 80px;
}
.lrc_box .lrc p {
    line-height: 40px;
    font-size: 26px;
    padding: 0;
    margin: 0;
    /* 文字放大和缩小的过渡动画 */
    transition: font-size 500ms;
    position: relative;
}
.lrc_box .lrc p > span {
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0) 100%), -webkit-linear-gradient(left, #000 0%, #000 0%);
}
.lrc_box .lrc .currentLine {
    font-size: 30px;
}
.control {
    width: 102%;
    height: 50px;
    background-image: linear-gradient(#f0f0f0, #5c8bfc, #005cec, #051959);
    position: fixed;
    left: -2%;
    bottom: 2px;
    display: flex;
    justify-content: space-evenly;
    cursor: default;
    padding: 20px;
}
.control>p{
    position: relative;
    top: -10px;
}
.control .btn {
    width: 30px;
    height: 30px;
    left: 20px;
    top: 24px;
}
.control .btn:after {
    border: 10px solid transparent;
    border-left-color: #fff;
    border-radius: 15%;
    top: 5px;
    left: 12px;
}
.control .btn:before {
    content: "";
    width: 14px;
    height: 14px;
    border-style: double;
    border-width: 0 0 0 12px;
    border-color: #fff;
    position: absolute;
    top: 8px;
    left: 9px;
    display: none;
}
.control .btn_change:after {
    display: none;
}
.control .btn_change:before {
    display: inline-block;
}
.control .time_progressBar {
    width: 50%;
    display: flex;
    align-items: center;
}
.control .time {
    display: flex;
    margin-right: 8px;
}
.control .time p {
    margin-right: 3px;
}
.control .progressBar {
    background-color: #fff;
    width: 100%;
    height: 6px;
    border-radius: 30px;
    cursor: pointer;
}
.control .progressBar .progress {
    background-color: #0010ce;
    width: 0;
    height: 6px;
    border-radius: 30px;
    position: relative;
    margin: 0;
}
.control .progressBar .progress:after {
    content: "";
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background-color: #fff;
    position: absolute;
    right: -8px;
    top: 50%;
    margin: -6px 0;
}
.progressBar:hover .progress:after {
    opacity: 1 !important;
}
.progressBar:active .progress:after {
    opacity: 1 !important;
}
.volume {
    width: 66px;
    position: relative;
    top: 12px;
    cursor: pointer;
    text-align: center;
}
.path {
    fill: #fff;
}
.volume:hover .path {
    fill: #00f;
}
.volume:hover .volumeBox {
    display: flex;
}
.volumeBox {
    width: 66px;
    background-color: #f0f0f0;
    position: absolute;
    bottom: 66px;
    left: 0;
    border-radius: 6px;
    /* 改这里为display:none隐藏 */
    display: none;
    flex-direction: column;
    align-items: center;
    padding: 22px 0;
}
.volumeBox:after {
    z-index: 9999;
    content: "";
    display: block;
    width: 0;
    height: 0;
    border: 12px solid transparent;
    border-top-color: #f0f0f0;
    position: absolute;
    left: 17px;
    bottom: -24px;
}
.volumeBar {
    width: 4px;
    height: 100px;
    border-radius: 30px;
    margin: 0;
    background-color: #ccc;
    cursor: pointer;
    transform: rotateZ(180deg);
}
.currentVolume {
    background-color: #2182e8;
    width: 4px;
    height: 0%;
    border-radius: 30px;
    position: relative;
    margin: 0;
}
.currentVolume:after {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background-color: #2182e8;
    position: absolute;
    bottom: 0;
    left: 50%;
    margin-left: -4px;
}
.volumeText {
    margin: 10px 0 0 0;
    color: #999;
}

注意:由于浏览器默认是不允许本地“跨域”请求,所以直接打开html文件,无法加载txt歌词文件,所以需要用到IDE编辑器打开(大部分IDE会默认创建本地服务器)或者通过网络请求加载txt文件!

标签:style,逐字,歌词,js,getElementById,let,lineNumber,lyric,document
来源: https://blog.csdn.net/qq_38432429/article/details/121374475