LRC 滾動器 + Vue.js

LRC 滾動展現VueJS

cnblogs @ Orcim   


 

 

 


 近一直在學習尤大大的這個前端框架。Vue 無疑是一款極易上手的前端框架,由於官方的文檔就是中文的,十分「本土化」,中文文檔會最早更新。除此以外,官方網站上的 Vue 教學很是適合像我這樣的新手,教學文檔很詳盡,在這裏給 Vue 的維護團隊點個贊。javascript

本身這幾天邊看文檔,邊動手跟着練習,而後今天花了一些時間,模仿手機音樂播放器實現了一個狀態欄歌詞滾動器,又在此之上添加了一個切換歌詞語言的功能:點擊左邊的音符圖標便可切換到歌詞翻譯,點擊這裏,在個人 CodePen 中查看這個 demo。css

仿狀態欄歌詞滾動器 demo

demo 大概就長這樣,點擊左側 icon 便可切換翻譯。html

思路 & 邏輯

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(毫秒)的位數多是一位、兩位、三位。vue

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 中註冊這些定時器:java

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 連接。json

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

相關文章
相關標籤/搜索