图片 首先运用canvas的方式 将图片通过drawimage的方法画到canvas上,然后运用canvas的一个方法 getimagedata 获取图片每一个像素点的 rgba的像素 就是
获取data里面rgba的数据 在网上查到高斯模糊的算法 就是好像对data里的rgba的数据进行数学方式的处理 处理之后将data输出 canvas的 putImageData()
将图像数据放回画布 重新放进canvas里 然后利用canvas的todataurl方法转化成base的方式 就是这个编码的格式 来当作他背景图片的src
html文件里用了meta标签
<meta name="viewport" content="width=device-width initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
背景图片考虑到不同手机的分辨率 不定宽 用 "background-size:cover; "尽可能把容器覆盖上
就是分成了两个模块,采用模块的方式写的,页面主要分为两个大的部分 一个是音频管理对象 用来管理播放器本身的一些功能就比如 播放 暂停 上一首 下一首点赞
另一个就是控制对象 controlmanager 控制对象主要是针对用户来控制播放器的一些功能和方法
我们将歌曲的信息,歌曲的图片,歌曲的播放时长,作者,专辑信息之类的利用json的形式封装到后台 进行模拟后台的数据
用了ajax的方式进行获取 利用回调函数来处理以及渲染页面
首先我们定义一个构造函数,在构造函数里面定义一些方法以及事件,方法:
音频播放,音频暂停,加载音频资源,修改音频当前播放时间;
渲染当前歌曲总时长,当前时长以及进度条的位置,进度条停止和开始,
歌单的渲染,歌单的隐藏和显示,
渲染当前歌曲信息,背景图片,播放按钮样式
事件:ended,当当前音频播放结束触发ended事件,触发点击下一首事件
绑定了一些点击事件以及拖拽事件,
点击事件:点击上一首后下一首按钮时,修改当前歌曲索引值,加载歌单资源,修改当前页面样式;
点击播放/暂停按钮,触发音频播放/暂停,渲染播放按钮样式;
点击显示歌单按钮,渲染歌单,点击关闭,将歌单隐藏;
点击歌单中某一首歌曲的时候,重新渲染歌单,加载歌曲资源,歌曲播放
拖拽事件:
在body上面绑定touchstart,touchmove,touchend事件,在touchstart事件触发的时候,进度条的动画停止,记录停止的时间;
touchmove,计算拖拽的百分比 更新我们的当前时间和进度条;
touchend,修改音频当前播放时间继续播放,开启进度条动画
由两个div实现,通过定位,将后一个div放在前一个div上,最开始上面的div设置为translate(-100%),然后根据当前时长,歌曲总时长,计算百分比,修改translate的值,实现进度条动画,动画效果是通过requestAnimitionFrame实现的,判断进度条的百分比是否达到1,没有则继续渲染,有则触发点击下一首事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link rel="stylesheet" href="../css/index.css"> </head> <body> <div class="wrapper"> <div class="song-img"> <div class="img-wrapper"> <img src="" alt=""> </div> </div> <div class="song-info"> </div> <div class="pro"> <div class="cur-time">00:00</div> <div class="pro-wrapper"> <div class="pro-bottom"></div> <div class="pro-top"> <div class="slider-point"></div> </div> </div> <div class="all-time">04:10</div> </div> <div class="control"> <div class="btn-wrapper like-btn"></div> <div class="btn-wrapper prev-btn"></div> <div class="btn-wrapper play-btn"></div> <div class="btn-wrapper next-btn"></div> <div class="btn-wrapper list-btn"></div> </div> </div> <script src = "../js/zepto.min.js"></script> <script src = "../js/gaussBlur.js"></script> <script src = "../js/render.js"></script> <script src = "../js/controlManager.js"></script> <script src = "../js/audioManager.js"></script> <script src = "../js/processor.js"></script> <script src = "../js/playlist.js"></script> <script src = "../js/index.js"></script> </body> </html>
var gulp = require("gulp"); var imagemin = require("gulp-imagemin") //图片压缩 var htmlclean = require("gulp-htmlclean"); //去掉html空格,进行压缩 var uglify = require("gulp-uglify"); //js压缩 var stripDebug = require("gulp-strip-debug"); //去掉调试语句 var concat = require("gulp-concat"); //没使用 var deporder = require("gulp-deporder"); //没使用 var less = require("gulp-less"); //将less转成css var postcss = require("gulp-postcss"); //cssnano,autoprefixer的依赖 var autoprefixer = require("autoprefixer"); //兼容css3属性,添加前缀 var cssnano = require("cssnano"); //css代码压缩 var connect = require("gulp-connect"); var folder = { src : "src/", dist : "dist/" } var devMode = process.env.NODE_ENV !== "production"; //流操作 task running gulp.task("html",function(){ var page = gulp.src(folder.src + "html/index.html") .pipe(connect.reload()); if(!devMode){ page.pipe(htmlclean()); } page.pipe(gulp.dest(folder.dist + "html/")) }) gulp.task("images",function(){ gulp.src(folder.src + "images/*") .pipe(imagemin()) .pipe(gulp.dest(folder.dist+"images/")) }) gulp.task("js",function(){ var js = gulp.src(folder.src+"js/*") .pipe(connect.reload()); if(!devMode){ //开发模式不压缩,生产模式压缩 js.pipe(uglify()) //代码压缩 .pipe(stripDebug()) //去掉调试语句 } js.pipe(gulp.dest(folder.dist+"js/")) }) gulp.task("css",function(){ var css = gulp.src(folder.src+"css/*") .pipe(connect.reload()) .pipe(less()); //将less转成css var options = [autoprefixer()]; //添加前缀,css3一些属性需要兼容,添加前缀 if(!devMode){ options.push(cssnano()) //css代码压缩 } css.pipe(postcss(options)) .pipe(gulp.dest(folder.dist + "css/")) }) gulp.task("watch",function(){ gulp.watch(folder.src + "html/*",["html"]); gulp.watch(folder.src + "images/*",["images"]); gulp.watch(folder.src + "js/*",["js"]); gulp.watch(folder.src + "css/*",["css"]); }) gulp.task("server",function(){ connect.server({ port : "8081", livereload : true }); }) gulp.task("default",["html","images","js","css","watch","server"]);
获取数据,绑定点击事件,绑定touch事件
var $ = window.Zepto; var root = window.player; var $scope = $(document.body); var songList; //存获取来的data var controlmanager; //创建的改变索引值的实例 var audio = new root.audioManager(); //创建一个音频实例 function bindClick(){ $scope.on("play:change",function(event,index,flag){ audio.setAudioSource(songList[index].audio); if(audio.status == "play"||flag){ //如果当前的状态是播放的话,加载完新的音频资源,播放 audio.play(); root.processor.start(); // } root.processor.renderAllTime(songList[index].duration) //渲染歌曲时间 root.render(songList[index]); //渲染页面 root.processor.updata(0); //渲染curtime,以及进度条的位置 }) //移动端click有300ms延迟 $scope.on("click",".prev-btn",function(){ var index = controlmanager.prev(); $scope.trigger("play:change",index); //渲染整个页面 }) $scope.on("click",".next-btn",function(){ var index = controlmanager.next(); $scope.trigger("play:change",index); }) $scope.on("click",".play-btn",function(){ if(audio.status == "play"){ audio.pause(); root.processor.stop(); }else{ root.processor.start(); audio.play(); } $(this).toggleClass("playing"); //改变play-btn的样式 }) $scope.on("click",".list-btn",function(){ root.playList.show(controlmanager); }) } function bindTouch(){ var $slidePoint = $scope.find(".slider-point"); var offset = $scope.find(".pro-wrapper").offset(); var left = offset.left; var width = offset.width; //绑定拖拽事件 开始拖拽 : 取消进度条渲染 $slidePoint.on("touchstart",function(){ root.processor.stop(); }).on("touchmove",function(e){ //计算拖拽的百分比 更新我们的当前时间和进度条 var x = e.changedTouches[0].clientX; var percent = (x - left) / width; if(percent > 1 || percent < 0){ percent = 0; } root.processor.updata(percent) }).on("touchend",function(e){ //计算百分百 跳转播放 重新开始进度条渲染 var x = e.changedTouches[0].clientX; var percent = (x - left) / width; if(percent > 1 || percent < 0){ percent = 0; } var curDuration = songList[controlmanager.index].duration; var curTime = curDuration * percent; audio.jumpToplay(curTime); root.processor.start(percent); $scope.find(".play-btn").addClass("playing"); }) } function getData(url){ //请求数据,请求到数据之后,进行img渲染,imgInfo,背景图片,时间 $.ajax({ type : "GET", url : url, success : function(data){ bindClick(); bindTouch(); root.playList.renderList(data); controlmanager = new root.controlManager(data.length); songList = data; $scope.trigger("play:change",0); }, error : function(){ console.log("error") } }) } getData("../mock/data.json")
渲染背景图皮
/* requires: zepto.min.js */ (function ($, root) { 'use strict'; function gaussBlur(imgData) { var pixes = imgData.data; var width = imgData.width; var height = imgData.height; var gaussMatrix = [], gaussSum = 0, x, y, r, g, b, a, i, j, k, len; var radius = 10; var sigma = 5; a = 1 / (Math.sqrt(2 * Math.PI) * sigma); b = -1 / (2 * sigma * sigma); //生成高斯矩阵 for (i = 0, x = -radius; x <= radius; x++, i++) { g = a * Math.exp(b * x * x); gaussMatrix[i] = g; gaussSum += g; } //归一化, 保证高斯矩阵的值在[0,1]之间 for (i = 0, len = gaussMatrix.length; i < len; i++) { gaussMatrix[i] /= gaussSum; } //x 方向一维高斯运算 for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { r = g = b = a = 0; gaussSum = 0; for (j = -radius; j <= radius; j++) { k = x + j; if (k >= 0 && k < width) {//确保 k 没超出 x 的范围 //r,g,b,a 四个一组 i = (y * width + k) * 4; r += pixes[i] * gaussMatrix[j + radius]; g += pixes[i + 1] * gaussMatrix[j + radius]; b += pixes[i + 2] * gaussMatrix[j + radius]; // a += pixes[i + 3] * gaussMatrix[j]; gaussSum += gaussMatrix[j + radius]; } } i = (y * width + x) * 4; // 除以 gaussSum 是为了消除处于边缘的像素, 高斯运算不足的问题 // console.log(gaussSum) pixes[i] = r / gaussSum; pixes[i + 1] = g / gaussSum; pixes[i + 2] = b / gaussSum; // pixes[i + 3] = a ; } } //y 方向一维高斯运算 for (x = 0; x < width; x++) { for (y = 0; y < height; y++) { r = g = b = a = 0; gaussSum = 0; for (j = -radius; j <= radius; j++) { k = y + j; if (k >= 0 && k < height) {//确保 k 没超出 y 的范围 i = (k * width + x) * 4; r += pixes[i] * gaussMatrix[j + radius]; g += pixes[i + 1] * gaussMatrix[j + radius]; b += pixes[i + 2] * gaussMatrix[j + radius]; // a += pixes[i + 3] * gaussMatrix[j]; gaussSum += gaussMatrix[j + radius]; } } i = (y * width + x) * 4; pixes[i] = r / gaussSum; pixes[i + 1] = g / gaussSum; pixes[i + 2] = b / gaussSum; } } //end return imgData; } // 模糊图片 function blurImg(img, ele) { var w = img.width, h = img.height, canvasW = 40, canvasH = 40; var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'); canvas.width = canvasW; canvas.height = canvasH; ctx.drawImage(img, 0, 0, w, h, 0, 0, canvasW, canvasH); var pixel = ctx.getImageData(0, 0, canvasH, canvasH); gaussBlur(pixel); ctx.putImageData(pixel, 0, 0); var imageData = canvas.toDataURL(); ele.css('background-image', 'url(' + imageData + ')'); } root.blurImg = blurImg; })(window.Zepto, window.player || (window.player = {}));
索引值改变
(function($,root){ //控制索引 function controlManager(len){ this.index = 0; this.len = len; } controlManager.prototype = { prev : function(){ return this.getIndex(-1); }, next : function(){ return this.getIndex(1); }, getIndex : function(val){ var index = this.index; var len = this.len; var curIndex = (index + val + len) % len; this.index = curIndex; return curIndex; } } root.controlManager = controlManager; })(window.Zepto,window.player || (window.player = {}));
渲染img,bgImg,info,喜欢的样式
(function($,root){ var $scope = $(document.body); console.log(root) //渲染当前这首歌的信息 function renderInfo(info){ var html = '<div class="song-name">'+info.song+'</div>'+ '<div class="singer-name">'+info.singer+'</div>'+ '<div class="album-name">'+info.album+'</div>'; $scope.find(".song-info").html(html) } //渲染当前这首歌的图片 function renderImg(src){ var img = new Image(); img.onload = function(){ root.blurImg(img,$scope); //bgimg 高斯模糊 $scope.find(".song-img img").attr("src",src) } img.src = src; } function renderIsLike(isLike){ if(isLike){ //心的样式 $scope.find(".like-btn").addClass("liking"); }else{ $scope.find(".like-btn").removeClass("liking"); } } root.render = function(data){ renderInfo(data); renderImg(data.image); renderIsLike(data.isLike) } })(window.Zepto,window.player || (window.player = {}))
音频播放,暂停,监听音频是否ended,如果结束,触发下一首按钮的点击事件,修改当前播放音乐的位置
(function($,root){ var $scope = $(document.body); function audioManager(){ this.audio = new Audio(); this.status = "pause"; // this.bindEvent(); } audioManager.prototype = { //绑定监听歌曲是否播放完成事件,如果播放完成,触发下一首的点击事件 bindEvent:function(){ $(this.audio).on("ended",function(){ $scope.find(".next-btn").trigger("click"); }) }, play : function(){ this.audio.play(); this.status = "play"; }, pause : function(){ this.audio.pause(); this.status = "pause"; }, setAudioSource : function(src){ this.audio.src = src; this.audio.load(); //将音频加载进来 }, jumpToplay : function(time){ this.audio.currentTime = time; //将音频的currentTime修改,继续播放 this.play(); } } root.audioManager = audioManager; })(window.Zepto,window.player || (window.player ={}))
渲染歌曲的时间,当前的播放时间,将时间转化成minute:second,更新进度条位置,进度条开始走,进度条结束
(function($,root){ var $scope = $(document.body); //进度条渲染,拖拽 var curDuration; var frameId; var lastPercent = 0; var startTime; //把秒转换成分和秒 function formatTime(duration){ duration = Math.round(duration); //取整 var minute = Math.floor(duration / 60); //向下取整,分钟 var second = duration - minute * 60; //秒数 if(minute < 10){ minute = "0" + minute; } if(second < 10){ second = "0" + second; } return minute + ":" +second; } function renderAllTime(duration){ //渲染歌曲总时长 lastPercent = 0; curDuration = duration; //记录歌曲的时长 var allTime = formatTime(duration); $scope.find(".all-time").html(allTime); } function updata(precent){ //渲染歌曲当前时长,以及进度条的位置 var curTime = precent * curDuration; //计算歌曲当前的时长 curTime = formatTime(curTime); //将当前事件装成minute:second $scope.find(".cur-time").html(curTime); //渲染时间 var percentage = (precent - 1) * 100 + "%"; //控制进度条的位置 $scope.find(".pro-top").css({ transform : "translateX("+percentage+")" }) } function start(precentage){ lastPercent = precentage === undefined ? lastPercent : precentage; cancelAnimationFrame(frameId); //清理定时器 startTime = new Date().getTime(); function frame(){ var curTime = new Date().getTime(); var precent = lastPercent + (curTime - startTime) / (curDuration * 1000); // if(precent < 1){ frameId = requestAnimationFrame(frame); //创建定时器 updata(precent); }else{ //表示进度条到了最右面,歌曲播放结束,触发下一首的按钮的点击事件 cancelAnimationFrame(frameId); $scope.find(".next-btn").trigger("click"); } } frame() } function stop(){ //进度条停止,记录当前停止的位置 var stopTime = new Date().getTime(); lastPercent = lastPercent + (stopTime - startTime) / (curDuration * 1000); cancelAnimationFrame(frameId); } root.processor = { renderAllTime : renderAllTime, start : start, stop : stop, updata : updata } })(window.Zepto,window.player || (window.player));
渲染播放列表,添加点击事件,渲染当前播放歌曲的样式
(function($,root){ //播放列表 var $scope = $(document.body); var control; var $playList = $("<div class = 'play-list'>"+ "<div class='play-header'>播放列表</div>" + "<ul class = 'list-wrapper'></ul>" + "<div class='close-btn'>关闭</div>"+ "</div>") //渲染我们的播放列表dom function renderList(songList){ var html = ''; for(var i = 0;i < songList.length;i++){ html += "<li><h3 >"+songList[i].song+"-<span>"+songList[i].singer+"</span></h3></li>" } $playList.find("ul").html(html); $scope.append($playList); bindEvent(); } function show(controlmanager){ control = controlmanager; $playList.addClass("show"); //将歌单展示出来 signSong(control.index); //将index的歌曲渲染不同的样式 } function bindEvent(){ $playList.on("click",".close-btn",function(){ $playList.removeClass("show") }) $playList.find("li").on("click",function(){ var index = $(this).index(); //获取当前点击的歌曲的索引 signSong(index); //渲染样式 control.index = index; //改变索引 $scope.trigger("play:change",[index,true]); $scope.find(".play-btn").addClass("playing"); setTimeout(function(){ $playList.removeClass("show") }, 200); }) } function signSong(index){ //给当前点击的li添加sign样式,原来的删除 $playList.find(".sign").removeClass("sign"); $playList.find("ul li").eq(index).addClass("sign"); } root.playList = { renderList : renderList, show : show } })(window.Zepto,window.player || (window.player = {}))