绝配!周杰伦bgm+阿瑟爱心代码 = 酷炫播放器

前言

最近电视剧《点燃我,温暖你》中的一个桥段,李峋写的爱心代码火了 ❤️‍❤️‍❤️‍,类似的表白神器每年都会在特定的时间(这些日期你都知道吗)出圈几个。

当各路大佬开始晒各种语言各种类型的爱心代码,我一个顶级CV工程师,在这浮躁的环境下自然也想着炫耀一番,于是在这片汪洋大海中经过我的层层筛选,这款爱心代码被我挑中并对他进行了小小的改造。

插件地址

关于插件:

这款插件的作者从代码来看已经无从考究了,感谢大佬栽树。

作为前端实现爱心代码的酷炫效果,尤其是3d效果使用的技术栈基本都是以three.js+canvas为主

这款插件也是使用three.js + canvas 开发,具体的开发过程不做过多的分析,cv工程师不太会three.js,见笑了

重点认识一下
Web Audio API:

Web Audio API 为控制 Web 上的音频提供了一个强大且通用的系统,允许开发人员选择音频源、为音频添加效果、创建音频可视化、应用空间效果(如平移)等等。

声音远近,大小,震动等等这些效果的可视化都需要Web Audio API的帮助

接口:这里简单列举了几个插件中用到的接口,更多接口可前往MDN查看

AudioListener

AudioListener 用一个虚拟的listener表示在场景中所有的位置和非位置相关的音效.
一个three.js程序通常创建一个AudioListener. 它是音频实体构造函数的必须参数,比如 Audio and PositionalAudio.
大多数情况下, listener对象是camera的子对象. Camera的3D变换表示了listener的3D变换.

AudioLoader

用来加载 AudioBuffer的一个类。
内部默认使用FileLoader> > 来加载文件。(项目中用来加载音频文件)

AudioAnalyser

创建AudioAnalyser对象, 使用AnalyserNode 去分析音频数据.

开始

插件动画效果已经完美了,符合装要求。

去掉边框更显大气;

bgm像铃声换成我周董的更能俘获人心;

周董咬字不清加个歌词显示更加人性化(开玩笑的啦,喜欢的就是这个味)。

动画跟随旋律的效果插件已经有了,配上歌词那不就是一个现成的播放器吗

思路一:

实现识别音频显示歌词,类似视频播放显示字幕的效果。

一通百度得到的结果主要分两种

  • 上传语音到服务端,服务端利用解析语音的API,返回文字;
  • 利用原生api或各种库调取浏览器麦克风,识别语音转文字

方法一在我这个捣鼓着玩的项目里明显不适用,方法二延时太久,识别精确度太低

思路二:√

获取歌词,实现歌词与音频的同步,这种方案是可行的

实现

获取歌词

网上找到LRC格式的歌词,一定要是LRC格式,放到一个文本域中,设置为隐藏

js读取歌词

var lrc = document.getElementById("lrc_content").innerHTML;

将LRC歌词解析为JS对象

var oLRC = {
    ti: "", //歌曲名
    ar: "", //演唱者
    al: "", //专辑名
    by: "", //歌词制作人
    offset: 0, //时间补偿值,单位毫秒,用于调整歌词整体位置
    ms: [] //歌词数组{t:时间,c:歌词}
};

function createLrcObj(lrc) {
    if(lrc.length==0) return;
    var lrcs = lrc.split('\n');//用回车拆分成数组
    for(var i in lrcs) {//遍历歌词数组
        lrcs[i] = lrcs[i].replace(/(^\s*)|(\s*$)/g, ""); //去除前后空格
        var t = lrcs[i].substring(lrcs[i].indexOf("[") + 1, lrcs[i].indexOf("]"));//取[]间的内容
        var s = t.split(":");//分离:前后文字
        if(isNaN(parseInt(s[0]))) { //不是数值
            for (var i in oLRC) {
                if (i != "ms" && i == s[0].toLowerCase()) {
                    oLRC[i] = s[1];
                }
            }
        }else { //是数值
            var arr = lrcs[i].match(/[(\d+:.+?)]/g);//提取时间字段,可能有多个
            var start = 0;
            for(var k in arr){
                start += arr[k].length; //计算歌词位置
            }
            var content = lrcs[i].substring(start);//获取歌词内容
            for (var k in arr){
                var t = arr[k].substring(1, arr[k].length-1);//取[]间的内容
                var s = t.split(":");//分离:前后文字
                oLRC.ms.push({//对象{t:时间,c:歌词}加入ms数组
                    t: (parseFloat(s[0])*60+parseFloat(s[1])).toFixed(3),
                    c: content
                });
            }
        }
    }
    oLRC.ms.sort(function (a, b) {//按时间顺序排序
        return a.t-b.t;
    });
}
createLrcObj(lrc);

将解析后的歌词呈现在页面上

创建一个容器展示歌词

    

用JS把歌词写到标签里面

注:如果歌词显示效果是全部展示,高亮当前歌词需要这一步操作;如果只显示当前歌词不展示其他歌词,可省略这一步

function showLRC() {
    var s="";
    for(var i in oLRC.ms){//遍历ms数组,把歌词加入列表
        s+='
  • '+oLRC.ms[i].c+'
  • '; } document.getElementById("lyric").innerHTML = s; console.log(s) } showLRC();

    JS实现歌词与播放音乐同步

    歌词滚动效果

    CSS

    .lyric_area{/*歌词显示区域*/
        height: 300px; /*歌词区域高度*/
        overflow: hidden; /*隐藏超出部分*/
        margin-top: 15px;
    }
    #lyric{/*歌词列表*/
        line-height: 20px;/*行高,这个值要用在歌词滚动距离上*/
        transition-duration: 600ms;/*滚动速度*/
     }
    #lyric .lineHigh{/*高亮行*/
        color: red;
    }
    

    JS

    var lineNo = 0; //当前行
    var C_pos = 6; //C位
    var offset = -20; //滚动距离(应等于行高)
    var audio = document.getElementById("audio");//播放器
    var ul = document.getElementById("lyric"); //歌词容器列表
    
    //高亮显示歌词当前行及文字滚动控制,行号为lineNo
    function lineHigh() {
        var lis = ul.getElementsByTagName("li");//歌词数组
        if(lineNo>0){
             lis[lineNo-1].removeAttribute("class");//去掉上一行的高亮样式
        }
        lis[lineNo].className = "lineHigh";//高亮显示当前行
    
        //文字滚动
        if(lineNo>C_pos){
            ul.style.transform = "translateY("+(lineNo-C_pos)*offset+"px)"; //整体向上滚动一行高度
        }
    }
    
    //滚回到开头,用于播放结束时
    function goback() {
        document.querySelector("#lyric .lineHigh").removeAttribute("class");
        ul.style.transform = "translateY(0)";
        lineNo = 0;
    }
    
    //监听播放器的timeupdate事件,实现文字与音频播放同步
    audio.ontimeupdate = function () {
        if(lineNo==oLRC.ms.length)
            return;
        var curTime = audio.currentTime; //播放器时间
        if(parseFloat(oLRC.ms[lineNo].t)<=curTime){
            lineHigh();//高亮当前行
            lineNo++;
        }
    };
    
    //监听播放器的ended事件,播放结束时回滚歌词
    audio.onended = function () {
        goback(); //回滚歌词
    };
    
    单句歌词切换效果

    var lineNo = 0; //当前行
    var audio = document.getElementById("auo");//播放器
    
    //滚回到开头,用于播放结束时
    function goback() {
        // document.getElementById('lyric').innerText = '花海'
        lineNo = 0;
    }
    
    //监听播放器的timeupdate事件,实现文字与音频播放同步
    audio.ontimeupdate = function () {
        if(lineNo==oLRC.ms.length)
            return;
        var curTime = audio.currentTime; //播放器时间
        if(parseFloat(oLRC.ms[lineNo].t)<=curTime){
            //歌词切换效果
            document.getElementById('lyric').innerText = oLRC.ms[lineNo].c
            lineNo++;
        }
    };
    
    //监听播放器的ended事件,播放结束时回滚歌词
    audio.onended = function () {
        goback(); //回滚歌词
    };

    问题

    插件本身用web audio api加载的音频,而歌词跟随音频显示是读取的audio标签,这里两者有冲突,利用web audio api中的属性是可以实现文字与音频的播放同步的,这里我没有学习并进行统一。
    而是利用audio标签加载,避免冲突点击播放之后将audio绑定的音频音量静音

    有需要代码的大家可自行下载预览我在这里

    你可能感兴趣的:(绝配!周杰伦bgm+阿瑟爱心代码 = 酷炫播放器)