第十三周之QQ音乐移动端项目实战笔记

QQ音乐移动端项目涉及的技术栈

  • rem的响应式单位;
  • js中的正则;
  • 数据获取;
  • zepto和zepto中的订阅发布;
  • html5+css3;
  • less;

移动开发中的rem设置

  • rem设置
    • 优点:在不同的屏幕下,字体的大小以及宽高的设置不能使用定值px,需要在不同的屏幕下按比例缩放;所以需要设置rem;
    • rem相对于浏览器中的html根标签的fontSize大小成倍数设置;与其他值无关;
  • 实际开发
    • 实际开发中屏幕大小为320px的设备,设计为2倍的屏幕设置,即按640px的设计,缩小后会很清晰;
    • 需要通过JS代码,来设置在不同屏幕下的比例值缩放;
    • 以下代码中:1)设计时屏幕的大小为640px;设置的html根标签fontSize大小为100px;2)设置后的结果是在设计的屏幕中1rem=100px;如果需要设置30px,则设置为0.3rem;
     //获取可视区域的宽度
     var $screenWidth=$(window).width();
     //在实际开发中会规定好在制定屏幕下的字体大小;一般在320px的浏览器屏幕中,会放大一倍,即640px屏幕下制作,然后设置的字体大小为定值,通过比例,让其在不同的屏幕下,也按比例缩放;
     var kfWidth=640;
     var fSize=100;
     //jQuery中获取html根标签
     var $html=$("html");//在原生JS中获取html标签:document.documentElement;
     var $htmlFont=$screenWidth/kfWidth*fSize;
     //jQuery中设置html的字体大小
     $html.css("fontSize",$htmlFont);
    
    • 在调试中将代码放在resize事件中;便于调试;

QQ音乐页面项目实战开发

移动项目开发前的准备

  • 项目开发的前提条件:
    • 都是以屏幕大小为640px的基准下制作的;html的字体大小设置为100px,所有的单位都用rem;其中1rem=100px;
  • html结构
    • meta视口创建:;快捷键meta:vp+tab
    • 引入less文件:包括index.less文件和less.js文件;放在head中;
    • 引入JS文件:包括zepto.js文件和index.js文件;放在body里面的最后面;
    • html代码:
     
     
     
         
         
         QQ音乐实战
         
         
     
     
     
    ...
  • less设置
    • 引入reset.less文件和public.less文件;
    • html和body元素的宽高设置为100%;
    • less代码:
     @import "reset.less";
     @import (reference)"public.less";
     html,body{
       width: 100%;
       height: 100%;
     }
    
  • JS设置
    • 目的:为了保证在所有屏幕下的rem换算的值能够相对于640px屏幕下值进行等比例缩小和放大;
    • 以屏幕宽度为640px和html的fontSize值为100px为基础,进行换算,设置不同屏幕下的fontSize值;
    • 注意:
      • 屏幕尺寸大于640px后,让musicBox容器的宽度设置为640px,并设置居中;
      • 屏幕尺寸大于640px后,设置为html的fontSize值为100px;
      • 保证在屏幕尺寸大于640px后,能够正常显示;
    • JS代码:
     //设置在不同屏幕下的html的fontSize值;
     $(window).on("resize",resizeTo).trigger("resize");
     function resizeTo() {
         var $screenWidth=$(window).width();
         var $sjWidth=640;
         var $sjFont=100;
         var $htmlFont;
         //当屏幕宽度大于640px时,让$screenWidth值为640px,则计算出来的fontSize值一直为100px;
         //保证了屏幕中的rem值稳定,不会再随着屏幕改变而改变;
         if($screenWidth>$sjWidth){
             $screenWidth=$sjWidth;
             $("html").css({
                 width:$sjWidth,
                 margin: "0 auto"
             });
         }
         $htmlFont=$screenWidth/$sjWidth*$sjFont;
         $("html").css("fontSize",$htmlFont);
     }
    

QQ音乐页面制作

1.1大背景的制作

  • 思路:
    • 在musicBox容器中创建
      ;添加定位,宽高均为100%;设置背景图为backgroundSize为cover;
    • 给musicbg添加模糊度-webkit-filter: blur(5px); filter: blur(5px);
    • 创建遮罩层:添加定位,背景颜色用透明度设置,达到遮罩层的效果;
    • 给musicbg和遮罩层设置层级为负数;保证后面的元素覆盖在上面;
  • 知识点:
    • 元素脱离文档流后,即添加定位和浮动后,不会继承父级宽度,需要重新设置宽度;
    • 模糊度代码的设置;
    • 遮罩层使用背景色的透明度;
    • 利用层级进行覆盖;
  • 注意:
    • 在chrome浏览器控制台上调试时,使用屏幕大小为320px的屏幕调试;屏幕中的尺寸为实际设置的尺寸值的一半;
  • 代码:
    • html代码:
     
    • less代码:
     .musicBox{
       width: 100%;
       height: 100%;
       position: relative;
       .musicbg{
         width: 100%;
         height: 100%;
         position: absolute;
         left: 0;
         top: 0;
         z-index: -2;
         background: url("../img/piao.jpg") no-repeat center;
         background-size: cover;
         -webkit-filter: blur(5px);
         filter: blur(5px);
       }
       .musicshadow{
         width: 100%;
         height: 100%;
         position: absolute;
         left: 0;
         right: 0;
         z-index: -1;
         background-color: rgba(1,1,1,.3);
       }
     }
    

1.2头部区域的制作

  • 重点:
    • 右侧播放图标的设计
      • 两个图标的容器添加定位;利用显示隐藏设置来实现切换;
      • 在容器中添加背景图标;通过背景图的定位来控制位置;
      • 背景图片的大小不能使用px,要使用rem值进行换算;换算后是相对于640px尺寸下的值,在调试中用320px尺寸的屏幕时,数值为一半;
  • 知识点:
    • 在less文件中使用.类名 (){}创建函数,括号中可以传入实参;可以设置默认值;
     .spriteFn(@x:0,@y:0){
       background: url("../img/timg.jpg") no-repeat @x @y;
       background-size: 4rem;
     }
    
  • 代码:
    • html代码:
     

    天空
    朴信惠

    • less代码:
     header{
         padding: .3rem;
         color: @color-white;
         background-color: rgba(0,0,0,.3);
         .h-left{
           float: left;
           >img{
             float: left;
             width: 1.2rem;
             height: 1.2rem;
             margin-right: .4rem;
           }
           >p{
             float: left;
             font-size: .35rem;
             >span{
               line-height: .63rem;
             }
           }
         }
         .h-right{
           float: right;
           width: .8rem;
           height: .8rem;
           border-radius: 50%;
           border: 1px solid @color-white;
           box-sizing: border-box;
           margin-top: .2rem;
           margin-right: .1rem;
           position: relative;
           .hr-play,.hr-pause{
             position: absolute;
             width: .4rem;
             height: .4rem;
             left: 50%;
             top:50%;
             margin-top: -.2rem;
             margin-left: -.2rem;
           }
           .hr-play{
             .spriteFn(@x:-.4rem,@y:-1.3rem);
             display: none;
           }
           .hr-pause{
             .spriteFn(@x:-.4rem,@y:-.36rem);
           }
         }
       }
    

1.3歌词区域初制作

  • html结构:
    • 设置main容器,设置上下margin值;高度由JS计算设置;
    • 在main容器中,创建lyc歌词容器,容器添加绝对定位,目的是为了调整top值,来达到歌词的更新;
    • 设置lyc歌词容器中的p元素,通过假数据将样式设置好;
    • main容器的高度为定值,而lyc歌词容器的高度是通过内容撑开的;给main设置overflow:hidden,这样lyc容器多出来的会被隐藏,通过定位不断的显示;

1.4footer区域的制作

  • 思路:分为三个区域
    • 收藏区域: 通过右浮动来做storage;
    • 进度条区域:分为左右两个时间区域,和中间的进度条区域;
      • 左右两个时间区域利用浮动制作,必须设置宽度才能浮动;
      • 中间的进度条区域:
        • 设置为行内块inline-block类型;利用容器的text-align: center来居中显示;
        • 利用vertical-align: middle/number来设置垂直方向上的位置;
        • 在progress容器中添加一个div,相对于容器绝对定位,高度相同,宽度可以自由设置,背景色为绿色,二者叠加在一起;通过控制div的宽度,来呈现出进度条的进程;
    • 底部下载区域:通过伪类元素设置背景图,完成QQ图标的设置;
    • footer区域设置定值高度,让底部下载区域距离底部有一段空隙;

1.5歌词区域的静态制作

  • 1)计算设置不同屏幕下歌词区域的高度
    • 在拉伸事件中设置JS代码;
    • 获取屏幕的视口高度-header区域的高度-footer区域的高度-歌词区域设置的上下margin值;
    • 注意:设置的margin值为rem值,需要乘上不同屏幕下的html的fontSize值;在jQuery中height()方法获取的高度不包含padding值,所以需要减去padding值;或使用innerHeight()或outerHeight();
    • JS代码:
     //1 计算不用屏幕下的main歌词区域的高度
     //注意:在jQuery中$().height()拿到的高度不包含padding和边框,而zpeto中包含;
     //在jQuery中用$().outerHeight()可以拿到包含padding和边框的高度,但是zpeto中不支持此属性;
     var $screenH=$(window).height();
     var $mainH=$screenH-$("header").height()-$("footer").height()-0.8*$htmlFont-0.6*$htmlFont;//jQuery引入后,会覆盖zpeto;
     $(".main").css("height",$mainH);
    
  • 2)从后台获取歌词数据,转化为需要的格式;
    • 通过$.ajax()来过去后台数据,此时获取到的时字符串;建立一个自执行函数的格式
    • 将字符串分割为数组,forEach遍历数组,通过正则和replace方法配合使用,匹配出满足要求的内容,通过小分组,拿到分,秒,文字;并给每一项加一个不同的id值;
    • 最终获取的数据为一个数组,数组中的每一项时包含分,秒,文字的对象;
    • 通过订阅式发布fire函数来传出数据;
    • JS代码:
     //2 获取后台歌词数据,转换成需要的格式通过订阅式发布输出
     var data=[],
         $id=0;
     //ajax获取数据,经过转化后,获取数组,然后通过jQuery中的订阅发布传入数据;
     var musicRender=(function () {
         return {
             init:function () {
                 $.ajax({
                     url:"data/lyc.txt",
                     dataType:"text",
                     type: "get",
                     success: function (result) {
                         var ary=result.split("\\n");//\n需要转义符,将获取的字符串数据,分割为数组;
                         var reg=/\[(\d{2})\:(\d{2})\.(?:\d{2})\](\D+)/g;//通过小分组拿到分,秒,文字;
                         //forEach方法,遍历数组,第一项为数组元素内容,第二项为数组元素的索引值
                         ary.forEach(function (item,index) {
                             //item为每一项的字符串,通过replace方法配合正则,进行逐一匹配操作;
                             item.replace(reg,function () {
                                 data.push({
                                     minute:arguments[1],
                                     seconds: arguments[2],
                                     lyc:arguments[3],
                                     id:$id
                                 })
                             });
                             $id++;
                         });
                         callbacks.fire(data);//向订阅发布传入数据
                     }
                 })
             }
         }
     })();
     musicRender.init();
    
  • 3)订阅式绑定函数,函数中获取数据,通过字符串拼接插入到DOM结构中;
    • 通过订阅式绑定函数获取数据;获取的数据为原生数组,必须用$.each()方法来遍历数组;
    • 通过字符串拼接插入DOM结构;
    • 将data1数组中的分,秒,通过data自定义属性绑定到每个歌词的p元素上;
    • JS代码:
     //订阅式绑定方法;获取传入的数据,绑定在页面中;
     callbacks.add(function (data1) {
         //此时获得的数据为原生数组
         var str="";
         $.each(data1,function (index, item) {
             //字符串拼接,将数据放在自定义属性上;
             str+=`

    ${item.lyc}

    ` }); $lyc.html(str); });

1.6audio音频区域的制作

  • 1)创建audio标签,放入到footer中;

    • 代码:
    • 注意:audio自带高度,所以给其添加定位,让其脱离文档流;不占据位置;
  • 2)加载页面后音频播放

    • 订阅绑定,绑定的函数依次执行;
    • 函数中设置oAudio.play(),音频播放;
    • oAudio绑定canplay事件,当音频播放时,进入函数执行;
    • 通过oAudio.duration获取音频的总时间长度,转化为分,秒的固定格式,插入到$duration中;
    • 创建定时器,每隔一秒执行f1()函数;
    • f1()函数中设置:
      • $current中的数组更新:
        • 通过oAudio.currentTime获取音频的当前播放时间,转化为固定格式,插入到$current中;
      • 歌词区域的制作:
        • 通过获取的当前音频时间值,进行筛选p元素身上的自定义属性;进而控制相对应的文字变色显示;
        • 利用filter过滤器,通过属性判断,过滤选择;
        • 将获取的p添加active类名;其他的兄弟元素删除此类名;
        • 此时文字会跟随音乐的播放而对应变色显示;
        • 设置lyc歌词容器的top值,来进行歌词的显示和隐藏,通过p的索引值,来控制上移的距离;
      • 进度条的进度更新:
        • 通过控制time-line的宽度值,来显示进度;通过设置百分数;
        • 利用获取的当前播放时间与总时间的比值获得比例,然后设置宽度为此百分比;
      • 比较当前播放时间和总时间:当二者相等时,证明歌曲播放完;
        • 关闭定时器;
        • 设置歌曲暂停,设置暂停按钮;
    • 秒转化为固定格式的分,秒的函数知识点:
      • 分的获取用除号,然后向下取整;秒的获取用%号,取余;然后向上取整;这样会让歌词比音乐块一些;
      • 秒里面由于是向上取整,会出现60;所以需要进行条件判断:当秒为60时,秒赋值为0;分自加1;
      • 分和秒中的数字,会出现小于10的情况,所以需要进行重新判断赋值,通过字符串拼接,将1转化为"01";
      • 返回一个固定格式;
      • 函数代码:
       //创建函数,用于转化时间格式
       function timeFormat(time){
           var min=Math.floor(time/60);//分钟用向下取整;
           var sec=Math.ceil(time%60);//秒用向上取整;这样会让歌词比音乐快一些;
           //判断当秒数为60时,变为0,分钟加等1;
           if(sec===60){
               sec=0;
               min+=1;
           }
           min=min<10?"0"+min:""+min;//字符串拼接;得到的是字符串;
           sec=sec<10?"0"+sec:""+sec;
           return min+":"+sec;
       }
      
    • JS代码:
     //创建函数,用于转化时间格式
     function timeFormat(time){
         var min=Math.floor(time/60);//分钟用向下取整;
         var sec=Math.ceil(time%60);//秒用向上取整;这样会让歌词比音乐快一些;
         //判断当秒数为60时,变为0,分钟加等1;
         if(sec===60){
             sec=0;
             min+=1;
         }
         min=min<10?"0"+min:""+min;//字符串拼接;得到的是字符串;
         sec=sec<10?"0"+sec:""+sec;
         return min+":"+sec;
     }
     //4 进入页面歌曲播放;获取音频的当前时间,控制歌词更新
     callbacks.add(function () {
         oAudio.play();//加载页面后,歌词立刻播放;
         //拿到当前音频对象的总时间,为总秒数;转化格式后赋给$duratoin
         oAudio.addEventListener("canplay",function () {
             $duration.html(timeFormat(oAudio.duration));
             timer=setInterval(fn1,1000);//开启定时器,不断获取新的currentTime
         })
     });
     function fn1() {
         //获取当前的音乐时间,赋值在$current中;
         var currentTime=timeFormat(oAudio.currentTime);
         $current.html(currentTime);
         //通过获取的当前时间值,进行筛选p元素身上的自定义属性;进而控制相对应的文字变色显示;
         var minute=currentTime.split(":")[0];
         var seconds=currentTime.split(":")[1];
         //利用filter过滤器,通过属性判断,过滤选择;
         var targetP=$lyc.children("p").filter(`[data-minute="${minute}"]`).filter(`[data-seconds="${seconds}"]`);
         //将获取的p添加active类名;其他的兄弟元素删除类名;
         targetP.addClass("active").siblings().removeClass("active");
         //歌词的移动,通过给lyc添加定位,控制其top值的变化
         var indexP=targetP.index();//通过索引值获取哪个p的时候,开始运动;
         if(indexP>=2){
             $lyc.css("top",-(indexP-2)*0.84*$htmlFont);
         }
         //进度条的设置:设置time-line的宽度占progress的宽度百分比
         var tlW=Number(oAudio.currentTime/oAudio.duration*100)+"%";
         //$time_line.css("width",tlW);
         $time_line.animate({
             width: tlW
         });
         //当前时间等于总时间时,停止定时器,按钮变为pause;
         if(oAudio.currentTime===oAudio.duration){
             clearInterval(timer);
             oAudio.pause();
             $hr_pause.show();
             $hr_play.hide();
         }
     }
    
  • 3)播放和暂停的制作

    • 订阅绑定函数,依次执行;
    • 在移动端点击事件,不设置click,设置tap;
    • 在点击事件中,不管进行何种操作,都要先关闭定时器;
    • 通过判断oAudio.paused的布尔值,来进行播放和暂停的操作;
    • 在进行播放操作中,最好先执行一下fn1()函数,因为定时器在1s后才会运行,存在异步;
    • 设置不同的按钮显示;
    • JS代码:
     //5 播放和暂停的制作;
     callbacks.add(function () {
         $btn.on("click",function () {//在移动端不添加click时间,添加tap事件
             clearInterval(timer);
             //在音频play播放时,oAudio.paused返回false值;
             if(oAudio.paused){
                 fn1();
                 timer=setInterval(fn1,1000);
                 oAudio.play();
                 $hr_play.show();
                 $hr_pause.hide();
             }else{
                 oAudio.pause();
                 $hr_pause.show();
                 $hr_play.hide();
             }
         })
     });
    
  • 项目知识点:

    • jQuery和zpeto二者区别:
      • jQuery中$().width()获取的是不包含padding和边框的;而$().innerWidth()包含padding;$().outerWidth()包含padding和边框;
      • zpeto中只有$().width()$().height(),代表包含padding和边框的宽高;innerWidth()outerWidth()不支持;
      • jQuery中的存在订阅式发布$.Callbacks();而zpeto中不存在此方法;
    • jQuery中的订阅式发布
      • 创建:var callbacks=$.Callbacks();
      • 订阅绑定:callbacks.add(function(data1){});data1为接收fire传入的数据;
      • 发布执行:callbacks.fire(data);其中data为传入匿名函数的数据
      • 其中add可以绑定多个函数,当fire执行时,绑定的函数依次执行,并且每一个函数中都会接收fire传入的数据;
    • jQuery中filter属性值筛选
      • 代码:
       var targetP=$lyc.children("p").filter(`[data-minute="${minute}"]`).filter(`[data-seconds="${seconds}"]`);
      
    • 歌词和进度条的过渡效果:
      • 在css样式中过渡元素的身上设置transition属性;
      • 在JS代码中给过渡元素设置animate()方法;
    • audio的知识
      • audio的属性和方法,全都是原生JS获取的元素;
      • oAudio.play():音频播放;
      • oAudio.pause():音频暂停;
      • oAudio.paused:获取暂停的状态,返回布尔值;当音频播放时,获取的值为false,当音频暂停时,获取的值为true;
      • oAudio.addEventListener("canplay",function(){}):指的是给oAudio绑定canplay事件,即当音频播放时,才会运行函数;
      • oAudio.duration:获取音频的总时间长度;单位为秒;
      • oAudio.currentTime:获取音频播放中的当前时间;单位为秒;
      • oAudio.addEventListener("timeupdate",function(){}):指的是不断的更新时间,当播放时,每隔0.3s触发一次事件;跟定时器一样;

你可能感兴趣的:(第十三周之QQ音乐移动端项目实战笔记)