第十三周之QQ音乐移动端项目实战代码
2018-11-18 本文已影响0人
果木山
QQ音乐移动端项目实战代码:
- html代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>QQ音乐实战</title> <link rel="stylesheet" type="text/less" href="css/index.less"> <script src="js/less.min.js"></script> </head> <body> <section class="musicBox"> <div class="musicbg"></div> <div class="musicshadow"></div> <header class="clearfix"> <div class="h-left"> <img src="img/piao.jpg" alt=""> <p> <span>天空</span><br/> <span>朴信惠</span> </p> </div> <div class="h-right"> <div class="hr-play"></div> <div class="hr-pause"></div> </div> </header> <section class="main"> <div class="lyc"> <!--<p>天空 - 朴信惠</p> <p>词:周耀辉</p> <p>曲:李荣浩</p> <p>穿华丽的服装 为原始的渴望而站着</p>--> </div> </section> <footer> <div class="storage"> <p></p> </div> <audio src="music/cc.mp3"></audio> <div class="progressWrap clearfix"> <div class="current">00:00</div> <div class="progress"> <div class="time-line"></div> </div> <div class="duration">00:00</div> </div> <div class="download"><a href="#">下载这首歌</a></div> </footer> </section> <script src="js/jquery.js"></script> <script src="js/zepto.js"></script> <script src="js/index.js"></script> </body> </html>
- less代码:
@import "reset.less"; @import (reference)"public.less"; html,body{ width: 100%; height: 100%; } .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); } } //header 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; cursor: pointer; outline: none; .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); } .hr-pause{ .spriteFn(@x:-.4rem,@y:-.36rem); display: none; } } } //main .main{ margin: .4rem .2rem; overflow: hidden; position: relative; .lyc{ position: absolute; transition: all 1s; >p{ text-align: center; font-size: .32rem; line-height: .84rem; color: darken(@color-white,20%); } >p.active{ color:@color-green; } } } //footer footer{ margin: 0 .3rem; height: 3rem; color: darken(@color-white,10%); .storage{ height: .8rem; >p{ width: .6rem; height: .6rem; .spriteFn(@x:-2.185rem,@y:-.285rem); border-radius: 50%; border: 1px solid red; box-sizing: border-box; float: right; margin-top: .1rem; } } >audio{ position: absolute; } .progressWrap{ height: .8rem; font-size: .24rem; line-height: .8rem; text-align: center; .current{ width: 12%; float: left; } .progress{ width: 70%; height: .1rem; display: inline-block; vertical-align: .03rem; border-radius: .05rem; background-color: darken(@color-white,10%); position: relative; .time-line{ position: absolute; height: 100%; border-radius: .05rem; background-color: @color-green; } } .duration{ width: 12%; float: right; } } .download{ width: 4.9rem; height: 1rem; line-height: 1rem; font-size: .36rem; margin: 0 auto; border-radius: .5rem; background-color: darken(@color-green,2%); text-align: center; position: relative; &::after{ position: absolute; width: .74rem; height: .74rem; top: 50%; left: .16rem; margin-top: -.37rem; content: ""; .spriteFn2(); } >a{ color: @color-white; font-weight: 700; } } }
- JS代码:
//添加拉伸事件 $(window).on("resize",resizeTo).trigger("resize"); var $htmlFont; function resizeTo() { //计算不同屏幕下html的fontSize值 var $screenWidth=$(window).width(); var $sjWidth=640; var $sjFont=100; //当屏幕宽度大于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); //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 获取后台歌词数据,转换成需要的格式通过订阅式发布输出; 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 jQuery中的订阅发布,获取歌词数据,插入页面 var callbacks=$.Callbacks(), $lyc=$(".main .lyc"), $current=$("footer .progressWrap .current"), $duration=$("footer .progressWrap .duration"); //获取媒体变量 var oAudio=$("audio")[0], $btn=$(".h-right"), $hr_play=$(".hr-play"), $hr_pause=$(".hr-pause"); //获取定时器变量 var timer=null; //获取进度条元素 var $time_line=$(".progress .time-line"); //订阅式绑定方法;获取传入的数据,绑定在页面中; callbacks.add(function (data1) { //此时获得的数据为原生数组 var str=""; $.each(data1,function (index, item) { //字符串拼接,将数据放在自定义属性上; str+=`<p data-minute="${item.minute}" data-seconds="${item.seconds}">${item.lyc}</p>` }); $lyc.html(str); }); //创建函数,用于转化时间格式 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)); /*oAudio.addEventListener("timeupdate",fn1)//每隔0.3s触发一次,可以替换定时器;*/ 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(); } } //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(); } }) });