LRC 滚动展示VueJS

cnblogs @ Orcim   


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

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

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

思路 & 逻辑

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

 
***.lrc
COPY

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,

COPY

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

 
lrcHandler.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
/**
 *  @ 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 中注册这些定时器:

 
setTimers.js

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 组件,这个部分就不多说了,直接贴代码,看看吧。

 
main.html

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 如下:

 
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 " )) ;

 
main.css

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

LRC 滚动器 + Vue.js相关推荐

  1. 【Vue.JS】纯 Vue.js 制作甘特图

    效果图 在线预览 GitHub链接(包含 knockoutJS 版本与 Vue 版本) 推荐组合效果 推荐与双表头固定效果组合,实现如上例中横表头(日期)纵向固定,纵表头(类型)横向固定效果. 参照连 ...

  2. vue 音乐盒app_基于Vue.js的音乐播放器(Webapp)

    概述 项目是基于Vue.js,成品是一个移动端的音乐播放器,来源于imooc的实战课程.自己动手实践并加以修改拓展. 项目的大致流程是Vue-cli构建开发环境,分析需求,设计构思,规划目录结构,开始 ...

  3. 基于 Vue.js 的移动端组件库mint-ui实现无限滚动加载更多

    通过多次爬坑,发现了这些监听滚动来加载更多的组件的共同点, 因为这些加载更多的方法是绑定在需要加载更多的内容的元素上的, 所以是进入页面则直接触发一次,当监听到滚动事件之后,继续加载更多, 所以对于无 ...

  4. 组件用.vue还是.js_如何使用Vue.js 2.0构建灵活的图像上传器组件

    组件用.vue还是.js by Cathy Ha 由凯茜·哈(Cathy Ha) 如何使用Vue.js 2.0构建灵活的图像上传器组件 (How to build a flexible image u ...

  5. 从 JavaScript 属性描述器剖析 Vue.js 响应式视图

    学习每一门语言,一般都是从其数据结构开始,JavaScript也是一样,而JavaScript的数据结构中对象(Object)是最基础也是使用最频繁的概念和语法,坊间有言,JavaScript中,一切 ...

  6. animate inater插件_基于animate.css动画库的全屏滚动小插件,适用于vue.js(移动端、pc)项目...

    功能简介 基于animate.css动画库的全屏滚动,适用于vue.js(移动端.pc)项目. 安装 npm install vue-animate-fullpage --save 使用 main.j ...

  7. zurb是什么网站_Zurb的Tribute库的Vue.js包装器,用于本机@mentions

    zurb是什么网站 致敬 (vue-tribute) A Vue.js wrapper for Zurb's Tribute library for native @mentions. Zurb的Tr ...

  8. js:Vue.js自定义指令实现scroll下滑滚动翻页

    Vue.js自定义指令实现scroll下滑滚动翻页 核心代码 import Vue from 'vue'// v-scroll Vue.directive('scroll', {bind(el, bi ...

  9. 使用Vue.js的简单拾色器

    选色器 (vue-colorpicker) A Simple Color Picker With Vue.js 使用Vue.js的简单拾色器 View demo 查看演示 Download Sourc ...

最新文章

  1. /proc/mtd 各参数的含义 -- linux内核
  2. Microsoft .NET Pet Shop 4 架构与技术分析(转)
  3. JSON Assertion(JSON断言)
  4. MPLS 配置静态LSP
  5. ORACLE ORA-01000: 超出打开游标的最大数(解决及原因)
  6. 【路径生成--绘制的方法】矢量地图巡线式路径探索
  7. 【面试笔试-c/c++】人民搜索2012校园招聘试题
  8. android qq 退出帐号,大家手机上的QQ平常用完退不退出帐号
  9. laravel whereDoesntHave 查询不等于条件的数据
  10. 十年MFC经历认识的Microsoft技术
  11. Python中的图像处理(第六章)Python图像量化及采样处理(2)
  12. linux查看共享内存文件,linux 共享内存
  13. 基于PHP的餐饮行业管理系统
  14. Socket hang up
  15. 黑马程序员————集合2(day17)
  16. Party All the Time HDU - 4355(三分)
  17. css 边框 不连续,css 不规则边框怎么设置
  18. 特征学习笔记Chapter1-Chapter4
  19. [4G5G专题-131]:流程 - LTE的功率控制
  20. 虚拟机ping不通本地_虚拟机ping不通本机是怎么回事

热门文章

  1. 2020牛客暑期多校训练营(第五场) F、DPS(签到题)
  2. java初学者之青铜篇一(初始java)
  3. Python中的replace方法
  4. STM32作品设计:蓝牙彩灯V1.4(WS2812全彩、声控、光控、人体红外、小科语音控制、手机APP、蓝牙无线升级)
  5. Alliance Advisors任命新的大中华区投资者信息副总裁,旨在扩张亚洲业务
  6. 数字孪生智慧校园三维可视化管理系统解决方案
  7. OI常用的数学知识大全(持续更新)
  8. Android手机如何提取系统内核(boot.img镜像文件提取)
  9. 代理商丨thinkcell是一款功能强大的图表创建工具
  10. php大文件下载502,php+nginx上传大文件502BadGateway