zepto+less写QQ音乐播放界面,进度条同步,歌词同步高亮等等(带注释,可参考可直接使用)

这是效果图,小编截下来的是静态图片,真是都是可以动的
在这里插入图片描述
不多说,直接上代码,注释都写在代码里清清楚楚

  1. less
@sizeMan:100%;
//关键帧动画函数
.keyframes(@name:move,@content){
//  @name:动画名,@content:动画内容
  @keyframes @name {@content();}
}


html{
  font-size: 100px;
  height: @sizeMan;
}
body{
  height: @sizeMan;
}








.container{
  height:@sizeMan;
  position: relative;
  .backgroundImg,.bg{
    position: absolute;
    width: @sizeMan;
    height: @sizeMan;
    z-index: -2;
  }
  .backgroundImg{
    background: url("../img/myDream.jpg");
    background-size: cover;
    //滤镜:设置模糊度
    filter: blur(7px);
  }
  .bg{
    background-color: rgba(0,0,0,0.3);
  }
  //头部
  .header{
    width: @sizeMan;
    height: 2rem;
    background-color: rgba(0,0,0,.2);
    padding: .3rem;
    //padding不改变整盒宽高
    box-sizing: border-box;
    display: flex;
    color: white;
    //定义一个关键帧动画
    .keyframes(move,{0%{transform: rotate(0deg)} 100%{transform: rotate(360deg)}});
    img{
      width: 1.2rem;
      height: 1.2rem;
    }
    div{
      margin-left: 5px;
      p:nth-of-type(1){
        font-size: .45rem;
      }
    }
    .musicBtn{
      width: .6rem;
      height: .6rem;
      background: url("../img/music.svg") no-repeat;
      background-size: cover;
      position: relative;
      top: .3rem;
      left: 3.7rem;
    &.select{
      //调用关键帧动画
      animation: move 1s linear 0s infinite;
    }
    }
  }
  //主体
  .main{
    height: 9.1rem;
    padding: .2rem;
    color: rgba(255,255,255,.5);
    font-size: .4rem;
    overflow: hidden;
    box-sizing: border-box;
    position: relative;
    letter-spacing: .04rem;
    .wrapper{
      width: @sizeMan;
      position: absolute;
      top: 0;
      left: 0;
    }
    p{
      width: @sizeMan;
      height: .8rem;
      text-align: center;
      line-height: .8rem;
      &.select{
        color: #31c27c;
      }
    }
  }
  //尾部
  .footer{
    color: white;
    box-sizing: border-box;
    .progress{
      width: @sizeMan;
      //.probg的父元素这里没有设置padding-top,所以后面设置.probg的margin的时候会传递给.progress
      //解决方法就是给一个overflow:hidden(原因就是父元素没有设置padding-top时子元素的margin-top会发生传递)
      overflow: hidden;
      position: relative;
      margin-bottom: .3rem;
      span{
        position: absolute;
        top: 0;
      }
      .current{
        left: .3rem;
      }
      .duration{
        right: .3rem;
      }
      .probg{
        width: 65%;
        margin: .15rem auto;
        background: rgba(2552,255,255,0.5);
        height: .04rem;
        .already{
          height: 100%;
          background-color: #31c27c;
          width: 10%;
        }
      }
    }
    a{
      display: block;
      width: 4.5rem;
      height: 1rem;
      background:url("../img/sprite_play.png") no-repeat 0.2rem -5.84rem #31c27c;
      background-size: .8rem 7rem;
      color: white;
      border-radius: .5rem;
      font-size: .4rem;
      text-align: center;
      line-height: 1rem;
      margin: auto;
    }
  }
}
  1. html
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>Title</title>
   <link rel="stylesheet" href="css/reset.min.css">
   <link rel="stylesheet/less" href="css/index1.less">
   <script>
   //手机屏幕适配
       ~(function (disn) {
               function getComputedFont() {
                   let winW=document.documentElement.clientWidth;
                   document.documentElement.style.fontSize=winW/disn*100+'px';
               }
               getComputedFont();
               window.addEventListener('resize',getComputedFont);
               window.addEventListener('orientationchange',getComputedFont);
           }
       )(750)
   </script>
   <script src="js/less-2.5.3.min.js"></script>
</head>
<body>
<section class="container">
   <audio src="./img/myDream.mp3"  preload="none" id="audio"></audio>
   <!--背景图-->
   <div class="backgroundImg"></div>
   <!--背景图的蒙层-->
   <div class="bg"></div>
   <header class="header" id="header">
       <img src="img/myDream.jpg" alt="">
       <div class="song">
           <p>我的梦</p>
           <p>张靓颖</p>
       </div>
       <a href="javascript:;" class="musicBtn" id="musicBtn"></a>
   </header>
   <main class="main" id="main">
       <div class="wrapper">
<!--            <p>就让光芒折射泪湿的瞳孔</p>-->
<!--            <p>就让光芒折射泪湿的瞳孔</p>-->
<!--            <p>就让光芒折射泪湿的瞳孔</p>-->
<!--            <p>就让光芒折射泪湿的瞳孔</p>-->
<!--            <p>就让光芒折射泪湿的瞳孔</p>-->
<!--            <p>就让光芒折射泪湿的瞳孔</p>-->
<!--            <p>就让光芒折射泪湿的瞳孔</p>-->
<!--            <p>就让光芒折射泪湿的瞳孔</p>-->
       </div>
   </main>
   <footer class="footer" id="footer">
       <div class="progress" id="progress">
           <span class="current">00:00</span>
           <span class="duration">00:00</span>
           <div class="probg" id="probg">
               <div class="already"></div>
           </div>
       </div>
       <a href="javascript:;" class="down" id="down">下载这首音乐</a>
   </footer>
</section>



   <!--
audio 是h5新增的标签,用来播放音频;
mp3
ogg
wav
m4a
// ...
(不支持amr格式的音频,npm上有一个库可以播放amr格式的音频)
如果有一天你接的需求中有音视频,要确认是什么格式,最好是mp3;如果不是mp3,谁来转(app或者服务端来转),如果前端来搞,体验会降级。
-->
   <!--
      <audio src="img/myDream.m4a" controls autoplay preload="metadata"></audio>
      controls 启用audio的控制界面,如果不写,页面中将什么也不展示;
      autoplay 自动播放(iphone中不能用)
      preload 提前加载 auto 自动加载;none 不提前加载,播放时才加载;metadata 只加载音频的基本信息
      loop: 循环播放
      -->
   <!--video的属性和audio基本相似,-->
   <!--
   poster: 视频的封面图片
   <video src="http://www.w3school.com.cn/i/movie.ogg" poster="img/myDream.jpg" controls></video>
   -->
   <!--
   视频流、音频流:不是一个具体的文件

   如果是播放流,一般服务端会提供给你一个 类似 rtmp://xxx.xx.com/dddd/xxx 的地址,把地址赋给audio和video的src属性;

   如果遇到不能用的情况,需要借助第三方的插件:video.js
   -->


   <script src="js/zepto.min.js"></script>
   <script src="js/index1.js"></script>
  1. js
~(function () {
   //获取元素
   let $header = $('.header'),
       $main = $('.main'),
       $footer = $('.footer'),
       $musicBtn = $('.musicBtn'),
       $wrapper = $('.wrapper'),
       $current = $('.current'),
       $duration = $('.duration'),
       $already = $('.already'),
       audio = document.getElementById('audio');
       progress=document.getElementsByClassName('progress')[0];
       //step标识当前播放哪条歌词,top标识屏幕显示的部分,autoPlayTime定时器返回值用来控制播放以及暂停时的歌词高亮和时间的及时更新
       let step=0, top=0, autoPlayTime,autoPlay;
   init();
   //主入口函数
   function init() {
       //起始运行一次computendMain方法适配用户的手机屏幕,同时绑定resize事件适屏幕
       computendMain();
       window.addEventListener('resize',computendMain);
       //给音符按钮绑定事件
       btnEvent();
       ajax();
   }
   //动态设置main部分的高度()
   //浏览器可视区域的高-header的hight-footer的hight
   function computendMain() {
       let winH=document.documentElement.clientHeight,//可视窗口的高度
           heiderH=$header[0].offsetHeight,//header的高度
           footerH=$footer[0].offsetHeight;//footer的高度
       //计算出main的高度(单位是px,我们要设置rem长度,所以还需要进行一次转换)
       let curH=(winH-heiderH-footerH)/parseFloat(document.documentElement.style.fontSize)-0.5;
       //设置main的height
       $main.css('height',curH+'rem');
   }
   //发送请求获取数据以及数据绑定
   function ajax() {
       //发送请求
       $.ajax({
           url:"./json/lyric.json",
           type:'GET',
           error(err){
               console.log(err);
           },
           success({lyric}){
               //绑定数据
               bindHTML(lyric);
           }
       });
       //绑定数据
       function bindHTML(lyric) {
           //替换歌词中的空格,(,),-等特殊符号
           lyric=lyric.replace(/&#(\d+);/g,(quan,num)=>{
               //为什么*1?上面捕获的是字符串类型的数字,*1将其转为数字类型的数字
               switch (num*1) {
                   case 32:
                       return ' ';
                   case 40:
                       return '(';
                   case 41:
                       return ')';
                   case 45:
                       return '-';
                   default:
                       return quan;
               }

           })
           //拼接字符串模板
           let str=``,ary=[];
           // 这个字符串不能使用split(';')来进行分割,那么怎么办?可以借用字符串的replace替换来巧妙遍历
           // [00&#58;16&#46;68]迎着痛把眼中所有梦&#10;这是一句歌词,按这个格式来捕获遍历
           lyric.replace(/\[(\d+)&#58;(\d+)&#46;(?:\d+)\]([^&#]+)&#10;/g,(quan,start,end,lyric)=>{
               ary.push({
                   start:start,end:end,lyric:lyric
               })
           });
           //遍历歌词数组动态添加歌词
           ary.forEach(item=>{
               str+=`<p data-min="${item['start']}" data-sec="${item['end']}">${item['lyric']}</p>`
           })
           $wrapper.html(str);

           //歌词加载完毕开启播放和按钮旋转
           audio.play();
           $musicBtn.addClass('select');
       }
       //实时检测播放进度
       autoPlay=function () {
           //每过一秒检测一次
           autoPlayTime=setInterval(changeTime,1000)
       };
       autoPlay();
       //检测播放进度更改歌词高亮以及播放时间的方法
       function changeTime() {
           //解构出audio元素的currentTime当前时间和duration结束时间
           let{currentTime, duration}=audio;
           if(currentTime>=duration){
               //播放完毕
               clearInterval(autoPlayTime);
               return;
           }

           //设置总时长
           $duration.text(changeTime(duration));
           //设置当前时长
           $current.text(changeTime(currentTime));
           //更改进度条
           $already.css('width',currentTime/duration*100+'%');
           //高亮歌词
           let $ps=$('.wrapper>p'),
               [min,sec]=changeTime(currentTime).split(':');
           $ps.each((idx,item)=>{
               if(item.getAttribute('data-min')===min&&item.getAttribute('data-sec')===sec){
                   // $('.wrapper > p').filter(`[data-min="${min}"]`).filter(`[data-sec="${sec}"]`);
                   // 	这个filter方法是zepto中的方法,实现起来会更加的简单,上面的意思是先过滤data-min符合条件的出来
                   //  然后再过滤出data-sec符合条件的出来
                   $(item).addClass('select').siblings().removeClass('select');
                   step++;
                   if(step>4){
                       top+=.8;
                       $wrapper.css('top',-top+'rem');
                   }
               }
           })

           //转换时间
           function changeTime(time) {
               //获取分钟以及秒
               let min = Math.floor(time / 60);
               let sec = Math.round(time - min * 60);
               //补零
               min < 10 && (min = '0' + min);
               sec < 10 && (sec = '0' + sec);
               return `${min}:${sec}`;
           }
       }
   }
   //给音符绑定事件。top是zepto中提供的手机触摸方法
   function btnEvent() {
     let deg;//用以记录暂停时按钮的旋转角度
     $musicBtn.tap(function () {
         //判断当前是播放还是停止状态,audio自带的paused属性检测是否处于暂停状态,暂停状态返回true不是返回fasle
         if(audio.paused){
             //暂停状态,play继续播放
             audio.play();
             //按钮添加旋转
             $musicBtn.addClass('select');
             //添加上次暂停角度
             $musicBtn.css('transform',deg);
             //运行时间函数检测已放时间方法
             autoPlay();
         }else{
             //播放状态,pause方法暂停播放
             audio.pause();
             //清除检测方法
             clearInterval(autoPlayTime);
             //保存旋转的角度
             deg=getComputedStyle($musicBtn[0])['transform'];
             //移除按钮旋转的动画
             $musicBtn.removeClass('select');

         }
     })
 }

})();

全部文件已上传百度云盘:https://pan.baidu.com/s/1We8dQfGrBQJIwiSkj_STgQ,提取码:pq8t

  • 18
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值