LRC歌词解析 详细教程 - 风之涯技术博客

标签搜索

LRC歌词解析 详细教程

小峰
2022年09月09日 / 0 评论 / 44 阅读 / 正在检测是否收录...
LRC歌词是什么?

LRC歌词是英文lyric(歌词)的缩写,基于纯文本的歌词专用格式,被用做歌词文件的扩展名。其实还有很多其他的歌词格式,比如酷狗音乐的krc,QQ音乐的qrc之类的。大部分的音乐站都还是使用的LRC。

LRC歌词内容类型标签

一个完整的LRC歌词文件,一般包含标识标签和时间标签。

LRC歌词编写相对比较自由,只要大体上的时间线是对的,基本就可以用。
[00:05.16]有的也写成[0:5.16]这样的格式
有的歌词里面甚至一行有2个甚至3个时间,这都是非常场常见。

新建一个html文件

在文件中创建一个div用于装歌词

<style>
  .fzyplayer-lrc{
    align-items: center;
    justify-content: center;
    text-align: center;
    height: 255px;
    max-height: 255px;
    width: 100%;
    position: relative;
}
.fzyplayer-lyric-area{
    width: 100%;
    position: absolute;
    text-align: center;
    justify-content: center;
    align-items: center;
    height: 255px;
    min-height: 255px;
    max-height: 255px;
    overflow: hidden;
    top: 0;
    -webkit-mask-image: linear-gradient(
            to bottom,
            rgba(255, 255, 255, 0) 0,
            rgba(255, 255, 255, 0.6) 15%,
            rgba(255, 255, 255, 1) 25%,
            rgba(255, 255, 255, 1) 75%,
            rgba(255, 255, 255, 0.6) 85%,
            rgba(255, 255, 255, 0) 100%
    );
}
.fzyplayer-lyric{
    padding: 0!important;
    width: 100%;
    height:100%;
    position: absolute;
    transition: top 1s linear;
}
.fzyplayer-lyric>li{
    width: 100%;
    /*color: rgba(0,0,0,.6);*/
    font-weight: 500;
    line-height: 35px;
    font-size: 16px;
    text-align: center;
    user-select:none;
    -moz-user-select: none;
    -ms-user-select: none;
    -webkit-user-select: none;
    transition: all 0.3s;
    text-shadow: 1px 1px rgba(0, 0, 0, 0.2),
    1px 2px rgba(0, 0, 0, 0.1);
    display: table!important;
}
.fzyplayer-lyric>.active{
    font-size: 18px;
    color: #fff;
    font-weight: 800;
    /*color: #c62f2f;*/
    background-image: linear-gradient(
            to right,
            #c62f2f,
            orange,
            yellow,
            green,
            yellow,
            orange,
            red,
            orange,
            yellow,
            green,
            yellow,
            orange,
            #c62f2f
    );
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-size: 200% 100%;
    animation: bgp 5s infinite linear;
}
@keyframes bgp {
    0% {
        background-position: 0 0;
    }
    100% {
        background-position: -100% 0;
    }
}
</style>
<div class="fzyplayer-lrc">
  <div class="fzyplayer-lyric-area">
      <ul class="fzyplayer-lyric">
          <li class="active">欢迎使用风之涯播放器,精彩由你而写</li>
      </ul>
  </div>
</div>
JS部分
if(typeof binlyric != 'object') { binlyric = {};}
binlyric = {
    edition:"1.1",
    obj:"",
    lyricCSS:new Object(),
    txt:"",
    index:0,
    time:new Array(),
    lyric:new Array(),
    sort:function(){ // 冒泡排序(从小到大)
        var third;
        for(var j=0;j<this.index-1;j++)
        {
            for(var i=0;i<this.index-1;i++)
            {
                if(this.time[i]>this.time[i+1])
                {
                    third = this.time[i];
                    this.time[i] = this.time[i+1];
                    this.time[i+1] = third;
                    third = this.lyric[i];
                    this.lyric[i] = this.lyric[i+1];
                    this.lyric[i+1] = third;
                }
            }
        }
    },
    createPanel:function(){ // 创建歌词面板
        var i=0;
        $(this.obj).html("").css({'top':'95px'});
        for(i=0;i<this.index;i++)
        {
            $(this.obj).append("<li>"+this.lyric[i]+"</li>");
        }
    },
    findTags:function(index,strArray,number){ // 查找标签(包括任何扩展的标签)
        // 此方法能匹配所有格式的标签
        // 因为此方法是在后面写的,所以时间标签并没有使用此方法
        number = number || this.txt.length;
        number = (number>this.txt.length) ? this.txt.length:number;
        var i,j,complete=0,value;
        var obj = new Object();
        obj.booble = false;
        obj.value = "[";
        for(i=index;i<number;i++)
        {
            if(this.txt.substr(i,1)==strArray[complete].s)
            {
                complete+=1;
                if(complete>1)
                {
                    if(complete<strArray.length)
                    {
                        obj.value += '{value:"'+this.txt.substr(value+1,i-value-1)+'"},';
                    }
                    else
                    {
                        obj.value += '{value:"'+this.txt.substr(value+1,i-value-1)+'"}]';
                    }
                }
                if(complete==strArray.length)
                {
                    obj.txt = this.txt.substr(index,i-index+1);
                    obj.value = eval('('+obj.value+')');
                    obj.index = i+1;
                    obj.booble = true;
                    break
                }
                value = i;
            }
            else if(this.txt.substr(i,1)=="\n")
            {
                obj.booble = false;
                return obj;
            }
            else if(this.txt.substr(i,1)==strArray[0].s && complete>0) // 遇到2次开始标志就退出
            {
                obj.booble = false;
                return obj;
            }
        }
        return obj;
    },
    findlyric:function(index){ // 查找歌词: 有则返回 歌词、继续查找的位置, 否则只返回继续查找的位置
        var obj = {};
        var str = this.txt;
        var i;
        for(i=index;i<str.length;i++)
        {
            if(str.charAt(i)=="[")
            {
                var _obj = this.findTags(i,[{s:"["},{s:":"},{s:"]"}]);
                if(_obj.booble)
                {
                    obj.index = i;//i + _obj.txt.length;
                    obj.lyric = str.substr(index,i-index);
                    return obj;
                }
            }
            else if(str.charAt(i)=="\n")
            {
                obj.index = i+1;
                obj.lyric = str.substr(index,i-index);
                return obj
            }
        }
        if(i==str.length) // 专处理最后一句歌词(最后一句歌词比较特殊)
        {
            obj.index = i+1;
            obj.lyric = str.substr(index,i-index);
            return obj;
        }
        obj.index = i;
        return obj;
    },
    findTime:function(index){ // 查找时间 : 有则返回 时间、继续查找的位置, 否则只返回继续查找的位置
        // 此功能可以用 findTags 方法实现,更简单、更强大、代码更少
        // findTags方法 是在后面写的,所以这里就不改了,具体可参考 findID方法里的使用实例
        var obj = {};
        var thisobj = this;
        var str = this.txt;
        obj.index = index;
        function recursion()
        {
            var _obj = thisobj.findTime(obj.index);
            if(_obj.time)
            {
                obj.time += _obj.time;
                obj.index = _obj.index;
            }
        }
        // --------------- 可以在这里 扩展 其它功能 ---------------
        // lrc歌词只能精确到每句歌词,可以通过扩展lrc 精确 到 每个字
        if(/\[\d{1,2}\:\d{1,2}\.\d{1,2}\]/.test(str.substr(index,10))) // [mm:ss.ff]
        {
            obj.time = str.substr(index+1,8) + "|";
            obj.index = index+9+1;
            recursion();
        }
        else if(/\[\d{1,2}\:\d{1,2}\.\d{3}\]/.test(str.substr(index,11))) //[mm:ss.fff]
        {
            obj.time = str.substr(index+1,9) + "|";
            obj.index = index+10+1;
            recursion();
        }
        else if(/\[\d{1,2}\:\d{1,2}\]/.test(str.substr(index,7))) // [mm:ss]
        {
            obj.time = str.substr(index+1,5) + ".00" + "|";
            obj.index = index+6+1;
            recursion();
        }
        // 以下标签均属于合法标签,但很少被使用,请根据需要进行扩展
        // [mm:ss.f] [mm:s.ff] [mm:s.f] [m:ss.ff] [m:s.ff] [m:s.f]
        // [mm:s] [m:ss] [s:s]
        return obj;
    },
    findID:function(index){ // 查找预定义标识
        //[ar:艺人名]
        //[ti:曲名]
        //[al:专辑名]
        //[by:编者(指编辑LRC歌词的人)]
        //[offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。(很少被使用)
        // 注:本程序也不支持 offset 功能(但是能取值),如需要 请自行在 sort 方法添加此功能
        // 此处功能 使用 findTags方法 实现
        var obj;
        obj = this.findTags(index,[{s:"["},{s:":"},{s:"]"}]);
        if(obj.booble)
        {
            if(obj.value[0].value=="ar")
            {
                this.ar = obj.value[1].value;
            }
            else if(obj.value[0].value=="ti")
            {
                this.ti = obj.value[1].value;
            }
            else if(obj.value[0].value=="al")
            {
                this.al = obj.value[1].value;
            }
            else if(obj.value[0].value=="by")
            {
                this.by = obj.value[1].value;
            }
            else if(obj.value[0].value=="offset") // 这里是 offset 的值
            {
                this.offset = obj.value[1].value;
            }
        }
    },
    analysis:function(){ // 解析
        if(this.txt=="") return false;
        var str = this.txt;
        this.index = 0;
        for(var i=0;i<str.length;i++)
        {
            if(str.charAt(i)=="[")
            {
                var time = this.findTime(i);
                if(time.time) // 时间标签
                {
                    var lyric = this.findlyric(time.index);
                    if(lyric.lyric!="\n" && lyric.lyric!="") // 去掉无意义歌词
                    {
                        var timeArray = time.time.split("|");
                        for(var j=0;j<timeArray.length;j++)
                        {
                            if(timeArray[j])
                            {
                                this.time[this.index] = timeArray[j];
                                this.lyric[this.index] = lyric.lyric;
                                this.index+=1;
                            }
                        }
                    }
                    i = time.index;
                }
                else // 预定义标签
                {
                    this.findID(i);
                }
            }
        }
        if(this.time.length<1){ //歌词出错
            this.time = ['00:01.000'];
            this.lyric = ['没有歌词展示'];
            this.index = 1;
        }
        this.sort();
        this.createPanel();
    },
    play:function(position,CSS){ // 定位指定时间的歌词
        var time;
        var obj = this;
        function set(index)
        {
            let num = (-35*index+95)+'px';
            $(obj.obj).css("top",num);
            $(obj.obj).find('li').eq(index).addClass('active').siblings().removeClass('active');
        }
        for(var i=0;i<this.index;i++)
        {
            if(position==this.time[i])
            {
                let set = new Set();
                set.add(i);
                return;
            }
            else if(position>this.time[i])
            {
                time = i;
            }
        }
        set(time);// 没找到匹配时间 则就近最小选择
    }
};

相对都比较简单

调用JS
//装入歌词
binlyric.txt = '完整歌词内容';
binlyric.obj = '.fzyplayer-lyric';
binlyric.analysis();//执行方法

//播放歌词 此段写入播放器timeupdate事件内
//例如播放器 
let player=document.getElementById('audio');
player.addEventListener('timeupdate',function(){
  let curTime = player.currentTime;
  //转换为00:00.102格式
  let mm = parseInt(curTime/60);
  mm = mm<10?('0'+mm):mm;
  let ff =(curTime%60).toFixed(2);
  ff = ff<10 ? ('0'+ff) :ff;
  binlyric.play(mm+':'+ff); //执行歌词就靠这一句
});
演示
  • 欢迎使用风之涯播放器,精彩由你而写
4

评论 (0)

QQ
昵称
邮箱
取消