LRC 滚动器 + Vue.js

LRC 滚动展示VueJS

cnblogs @ Orcim   


 

 

 


 近一直在学习尤大大的这个前端框架。Vue 无疑是一款极易上手的前端框架,因为官方的文档就是中文的,十分“本土化”,中文文档会最先更新。除此之外,官方网站上的 Vue 教学非常适合像我这样的新手,教学文档很详尽,在这里给 Vue 的维护团队点个赞。

自己这几天边看文档,边动手跟着练习,然后今天花了一些时间,模仿手机音乐播放器实现了一个状态栏歌词滚动器,又在此之上添加了一个切换歌词语言的功能:点击左边的音符图标即可切换到歌词翻译,点击这里,在我的 CodePen 中查看这个 demo。

仿状态栏歌词滚动器 demo

demo 大概就长这样,点击左侧 icon 即可切换翻译。

思路 & 逻辑

1. 对LRC文件字符串格式,进行解析。

1 2 3 4 5 6 7
[ti:]
[ar:]
[al:]
[by:]
[00:00.05]
[00:01.09]Never knowing where our own futures lie,
[00:04.39]And easily we start simplifying all our planning,
 

这里要注意的是lrc文件每行,开头的“时间戳”的时间格式,普遍的格式有三种:①[min:sec.ms]②[min:sec]③其他,这里只对前两种情况做了兼容,因为这两种现在见得最多;还有要注意的就是ms(毫秒)的位数可能是一位、两位、三位。

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
/**
 *  @ method   lrcHandler  返回指定 type 的歌词 json 对象
 *  @ param   {  String  }   type  lrc(原) 或 tlrc(译)
 *  @ param   {  Object  }   lns_obj  info&main
  */
function   lrcHandler   ( type )   {
     var   refer   =   {
         " lrc " :   " lyric " ,
         " tlrc " :   " translateLyric "
     } ;
     var   lns_obj   =   {
         " lrc-info " : [],
         " lrc-main " : []
     } ;
     var   lns   =   lrc [ refer [ type ]]. split ( " \n " ) ;  
     // lrc 文件字符串的每行所组成的 JSON
     for ( var   i = 0 ,   len = lns . length ;   i < len ;   i ++ ){
         var   m   =   lns [ i ] ;
         var   info   =   m . replace ( / \[ ( . * ) \] ( . * )/ ,   " $1 " ) ;
         var   lys   =   m . replace ( / \[ ( . * ) \] ( . * )/ ,   " $2 " ) ;
         var   mth   =   info . match ( /([ 0-9 ] + ) \: ([ 0-9 ] + ) \. ([ 0-9 ] + )/ ) ;
         var   pad_0   =   function ( num_str ){ return   ( num_str   +   ( new   Array ( 4 - num_str . length )). join ( " 0 " ))} ;
         if ( mth ){
             var   milis   =   mth [ 1 ] * 1 * 60 * 1000   +   mth [ 2 ] * 1 * 1000   +   pad_0 ( mth [ 3 ]) * 1;
             var   ln   =   { " time " :   milis ,   " lyric " :   lys } ;
             lns_obj [ " lrc-main " ]. push ( ln )
         } else {
             var   inf   =   {} ;
             inf [ info . replace ( /( . * ) \: ( . * )/ " $1 " )]   =   info . replace ( /( . * ) \: ( . * )/ ,   " $2 " ) ;
             lns_obj [ " lrc-info " ]. push ( inf ) ;
         } ;
     } ;
     return   lns_obj ;
} ;
 

2. 对 ***.lrc 文件利用如上方法进行解析后,返回一个对象,对象包括歌词的附属信息(lrc-info):by、ti、ar等头信息,歌词的主要部分(lrc-main)数组类型,其每项包含时间戳转换的毫秒总数,以及对应时间戳点(time)的歌词字符串(lyric)。之后要做的就是批量注册定时器,在 Vue 生命周期函数的 created 中注册这些定时器:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
" created " :   function (){
     for ( var   j = 0 ,   len = $lrc [ " lrc-main " ]. length ;   j < len ;   j ++ ){
         var   ti   =   that . displayLrc [ " lrc-main " ][ j ][ " time " ] ;
         var   c   =   0;
         that . timers [ j ]   =   setTimeout ( function (){
             that . scroll_fn () ;
             clearTimeout ( that . transition_timer ) ;
             that . transition_timer   =   setTimeout ( function (){
                 $lrc [ " lrc-main " ][ c - 1 ]   &&   that . setLnLrc ( c - 1 ,   function (){
                     that . recover_fn () ;
                     console . log ( that . displayLrc [ " lrc-main " ][ c - 1 ][ " time " ],   that . displayLrc [ " lrc-main " ][ c - 1 ][ " lyric " ]) ;
                 }) ;
                 clearTimeout ( that . transition_timer ) ;
             },   200 )
             nowLine   =   c ++ ;
             clearTimeout ( that . timers [ j ]) ;
         },   ti ) ;
     }
}
 

部分代码逻辑如上,完整代码可见文章开头处的 CodePen 链接。

3. 注册和创建 Vue 组件,这个部分就不多说了,直接贴代码,看看吧。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
< html >
     < head >
         < meta   charset =" utf-8 ">
         < link   rel =" stylesheet "   href =" main.css ">
        < script   src =" ../../vuejs_2.6.10.js "> < / script >
     </ head >
     < body >
         < div   id =" main ">
             < lrc-scroller
             :offset-class =" sim "
             :lrc_top =" tlrc "
             :lrc_bt =" blrc "
             :swl =" switchLang "
             ></ lrc-scroller >
         </ div >
     </ body >
    < script   src =" 498286345 "   type =" text/javascript "> < / script >
     <!--  引入 lrc 文件,为基于网易云音乐的歌词文件 @param { JSON } lrc  -->
    < script   src =" main.js "   type =" text/javascript "> < / script >
</ html >
 

核心逻辑代码 main.js 如下:

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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
Vue . component ( " lrc-scroller " ,   {
     " template " :  
         " <div class='container'>\
            <div class='box'>\
                <div class='box-outer' v-bind:class='offsetClass'>\
                    <div class='box1'><span>{{ lrc_top }}</span></div>\
                    <div class='box2'><span>{{ lrc_bt }}</span></div>\
                </div>\
                <div class='switchBtn' title='switch language' v-on:click='swl'></div>\
            </div>\
        </div> " ,
     " props " :[ " offsetClass " " lrc_top " " lrc_bt " " swl " ]
}) ;
var   that   =   {} ;
var   $lrc   =   lrcHandler ( " lrc " ) ;
var   $tlrc   =   lrcHandler ( " tlrc " ) ;
var   nowLine   =   0;
var   main   =   new   Vue ({
     " el " :   " #main " ,
     " data " :   {
         " sim " :   {
             " offset " :   false ,
             " transition " :   true
         },
         " tlrc " :   "" ,
         " blrc " :   "" ,
         " timers " : [],
         " transition_timer " :   null ,
         " displayLrc " :   window [ " $lrc " ]
     },
     " methods " :   {
         setLnLrc :   function ( line ,   fn ){
             that [ " tlrc " ]   =   that . displayLrc [ " lrc-main " ][ line ][ " lyric " ] ;
             $lrc [ " lrc-main " ][ line + 1 ]   &&   ( that [ " blrc " ]   =   that . displayLrc [ " lrc-main " ][ line + 1 ][ " lyric " ]) ;
             fn   &&   fn () ;
         },
         recover_fn :   function (){
             that . sim [ " transition-none " ]   =   true ;
             that . sim [ " transition " ]   =   false ;
             that . sim [ " offset " ]   =   false ;
         },
         scroll_fn :   function (){
             that . sim [ " offset " ]   =   true ;
             that . sim [ " transition " ]   =   true ;
             that . sim [ " transition-none " ]   =   false ;
         },
         switchLang :   function (){
             that . displayLrc   =   ( that . displayLrc   ===   window [ " $tlrc " ]   ?   window [ " $lrc " ]   :   window [ " $tlrc " ]) ;
         }
     },
     " watch " :   {
         " displayLrc " :   function (){
             that . setLnLrc ( nowLine ) ;
         }
     },
     " beforeCreate " :   function (){
         that   =   this ;
     },
     " created " :   function (){
         for ( var   j = 0 ,   len = $lrc [ " lrc-main " ]. length ;   j < len ;   j ++ ){
             var   ti   =   that . displayLrc [ " lrc-main " ][ j ][ " time " ] ;
             var   c   =   0;
             that . timers [ j ]   =   setTimeout ( function (){
                 that . scroll_fn () ;
                 clearTimeout ( that . transition_timer ) ;
                 that . transition_timer   =   setTimeout ( function (){
                     $lrc [ " lrc-main " ][ c - 1 ]   &&   that . setLnLrc ( c - 1 ,   function (){
                         that . recover_fn () ;
                         console . log ( that . displayLrc [ " lrc-main " ][ c - 1 ][ " time " ],   that . displayLrc [ " lrc-main " ][ c - 1 ][ " lyric " ]) ;
                     }) ;
                     clearTimeout ( that . transition_timer ) ;
                 },   200 )
                 nowLine   =   c ++ ;
                 clearTimeout ( that . timers [ j ]) ;
             },   ti ) ;
         }
     }
}) ;
/**
 *  @ method   lrcHandler  返回指定 type 的歌词 json 对象
 *  @ param   {  String  }   type  lrc(原) 或 tlrc(译)
 *  @ param   {  Object  }   lns_obj  info&main
  */
function   lrcHandler   ( type )   {
     var   refer   =   {
         " lrc " :   " lyric " ,
         " tlrc " :   " translateLyric "
     } ;
     var   lns_obj   =   {
         " lrc-info " : [],
         " lrc-main " : []
     } ;
     var   lns   =   lrc [ refer [ type ]]. split ( " \n " ) ;  
     // lrc 文件字符串的每行所组成的 JSON
     for ( var   i = 0 ,   len = lns . length ;   i < len ;   i ++ ){
         var   m   =   lns [ i ] ;
         var   info   =   m . replace ( / \[ ( . * ) \] ( . * )/ ,   " $1 " ) ;
         var   lys   =   m . replace ( / \[ ( . * ) \] ( . * )/ ,   " $2 " ) ;
         var   mth   =   info . match ( /([ 0-9 ] + ) \: ([ 0-9 ] + ) \. ([ 0-9 ] + )/ ) ;
         var   pad_0   =   function ( num_str ){ return   ( num_str   +   ( new   Array ( 4 - num_str . length )). join ( " 0 " ))} ;
         if ( mth ){
             var   milis   =   mth [ 1 ] * 1 * 60 * 1000   +   mth [ 2 ] * 1 * 1000   +   pad_0 ( mth [ 3 ]) * 1;
             var   ln   =   { " time " :   milis ,   " lyric " :   lys } ;
             lns_obj [ " lrc-main " ]. push ( ln )
         } else {
             var   inf   =   {} ;
             inf [ info . replace ( /( . * ) \: ( . * )/ " $1 " )]   =   info . replace ( /( . * ) \: ( . * )/ ,   " $2 " ) ;
             lns_obj [ " lrc-info " ]. push ( inf ) ;
         } ;
     } ;
     return   lns_obj ;
} ;
console . log ( lrcHandler ( " tlrc " )) ;
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14
body { padding :   0 ;   margin :   0 ;   font-family :  -apple-system ,  BlinkMacSystemFont ,   ' Segoe UI ' ,  Roboto ,  Oxygen ,  Ubuntu ,  Cantarell ,   ' Open Sans ' ,   ' Helvetica Neue ' ,  sans-serif ;   letter-spacing :   .5 px ;   font-size :   18 px ;}
. container { position :  relative ;   width :   100 % ;   height :   36 px ;   background-color :   # eee ;}
. box { height :   32 px ;   width :   calc ( 100 %   -   36 px );   position :  absolute ;   top :   0 ;   left :   0 ;   padding :   2 px   0   2 px   36 px ;   overflow :  hidden ;}
. box :: before { display :  block ;   content :   "" ;   width :   36 px ;   height :   36 px ;   position :  absolute ;   top :   0 ;   left :   0 ;   background :   url ( data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzNiIgaGVpZ2h0PSIzNiIgdmlld0JveD0iMCAwIDEyOTYgMTI5NiI+PHBhdGggZD0iTTExNTIgNjMuOTNMNDI0LjIwNyAyMDEuMjM4djYxMi44NjVhMjE0LjM0NCAyMTQuMzQ0IDAgMCAwLTY1Ljk3Mi0xMC43MjNBMjE0LjM0NCAyMTQuMzQ0IDAgMCAwIDE0NCAxMDE3Ljg0N2EyMTQuMzQ0IDIxNC4zNDQgMCAwIDAgMjE0LjIzNSAyMTQuMjM1IDIxNC4zNDQgMjE0LjM0NCAwIDAgMCAyMTQuNDY4LTIxNC4yMzV2LTYyOS42NWw0MzAuNTY4LTgxLjM1OHY0MjIuMTc2YTIxNC4zNDQgMjE0LjM0NCAwIDAgMC02NS43NC0xMC43MjRBMjE0LjM0NCAyMTQuMzQ0IDAgMCAwIDcyMy4yOTggOTMyLjc2YTIxNC4zNDQgMjE0LjM0NCAwIDAgMCAyMTQuMjM1IDIxNC4yMzVBMjE0LjM0NCAyMTQuMzQ0IDAgMCAwIDExNTIgOTMyLjc2VjI3OC42MzJ6TTkzNy43NjUgODY2Ljc4OGE2NS43NzYgNjUuNzc2IDAgMCAxIDY1LjczOSA2NS43MzkgNjUuNzc2IDY1Ljc3NiAwIDAgMS02NS43NCA2NS45NzIgNjUuNzc2IDY1Ljc3NiAwIDAgMS02NS45NzEtNjUuOTcyIDY1Ljc3NiA2NS43NzYgMCAwIDEgNjUuOTcyLTY1LjczOXptLTU3OS4yOTcgODUuMDg4YTY1Ljc3NiA2NS43NzYgMCAwIDEgNjUuNzQgNjUuNzM5IDY1Ljc3NiA2NS43NzYgMCAwIDEtNjUuNzQgNjUuOTcyIDY1Ljc3NiA2NS43NzYgMCAwIDEtNjUuOTcyLTY1Ljk3MiA2NS43NzYgNjUuNzc2IDAgMCAxIDY1Ljk3Mi02NS43NHoiLz48L3N2Zz4= )  no-repeat center ;   background-size :   24 px ;}
. switchBtn { position :  absolute ;   top :   0 ;   left :   0 ;   width :   36 px ;   height :   36 px ;   cursor :  pointer ;}
. switchBtn :: before { display :  block ;   content :   "" ;   position :  absolute ;   left :   0 ;   top :   0 ;   width :   100 % ;   height :   100 % ;   background-color :  grey ;   border-radius :   50 % ;   opacity :   0 ;   transform :   scale ( 0 );   transition :  all  200 ms  linear }
. switchBtn : hover :: before { transform :   scale ( 1.5 );   opacity :   .25 ;}
 
. box-outer { height :   64 px ;   display :  felx ;   flex-direction :  column ;   transition :  all  200 ms ;}
. box-outer . offset { transform :   translateY ( -32 px );}
. box-outer . transition { transition :  transform  200 ms  ease-in-out ;}
. box-outer . transition-none { transition-duration :   0 ms ;}
. box1 { height :   32 px ;   display :  flex ;   justify-content :  flex-start ;   align-items :  center ;   background-color :  none ;   white-space :  nowrap ;}
. box2 { height :   32 px ;   display :  flex ;   justify-content :  flex-start ;   align-items :  center ;   background-color :  none ;   white-space :  nowrap ;}
 

结束语

Vue确实方便学习和使用,如果接触过微信小程序的话,基本上看看官网上的文档几乎就可以直接拿来用了,框架设计十分友好和人性化,除此外 Vue 式的组件化和模块化让代码变得简洁和明了易于维护,数据绑定等降低 DOM 渲染成本也恰到好处,是个很不错的前端框架。

其他

VueJS 版本:2.6.10,2019/07

更新

今天又花了一些时间完善了这个歌词滚动器,点击这里来查看demo,在我的CodePen中查看源代码。主要是修正了一些错误,适配了所有格式的LRC文件,添加了窄屏下歌词可以左右以较为合理的速度滚动的功能,优化了细节。2019/07/17

转载于:https://www.cnblogs.com/Orcim/p/11197162.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值