音乐播放器效果

播放器分析
这里将播放器分两块来做:
视图层(html + css) 逻辑层 ( js )
视图层分析
视图中包含:
播放器容器
播放器名称 音乐专辑图 音乐信息
歌曲名 歌手 专辑名 控制区
上一曲 播放 下一曲 播放进度条 播放时间
当前时间 歌曲总时间 音频控件 页面背景
逻辑层分析
逻辑层处理包括:
加载歌单 渲染歌曲信息
专辑图 歌曲名 歌手 专辑名 歌曲时长 歌曲音频地址 监听控制按钮
播放按钮 (修改播放状态) 上一曲(判断歌单边界,重新渲染歌曲信息) 下一曲(判断歌单边界,重新渲染歌曲信息) 定时器
同步歌曲当前时间和播放进度条 歌曲播放完,自动切换下一曲
好了,播放器基本分析完成,接下来开始编码了,先进行视图层的编写。
视图层结构编写
根据我在上面的视图层分析,来构建HTML结构。
建立index.html,结构编码如下:
<!-- 页面背景 -->
<body>
    <!-- 播放器容器 -->
    <p class="player">
        <!-- 播放器名称 -->
        <p class="header">音乐播放器</p>
        <!-- 音乐专辑图 -->
        <p class="albumPic"></p>
        <!-- 音乐信息  -->
        <p class="trackInfo">
            <!-- 歌曲名 -->
            <p class="name"></p>
            <!-- 歌手 -->
            <p class="artist"></p>
            <!-- 专辑名 -->
            <p class="album"></p>
        </p>
        <!-- 播放进度条 -->
        <p class="progress"></p>
        <!-- 控制区  -->
        <p class="controls">
            <!-- 播放 -->
            <p class="play">
                <i class="icon-play"></i>
            </p>
            <!-- 上一曲 -->
            <p class="previous">
                <i class="icon-previous"></i>
            </p>
            <!-- 下一曲 -->
            <p class="next">
                <i class="icon-next"></i>
            </p>
        </p>      
        <!-- 播放时间  -->
        <p class="time">
            <!-- 当前时间 -->
            <p class="current"></p>
            <!-- 歌曲总时间 -->
            <p class="total"></p>
        </p>
        <!-- 音频控件 -->
        <audio id="audio"><source src=""></audio>
    </p>
</body>
好了,结构编写完毕,接下来编写视图层样式。
视图层样式编写
注:这里我是用LESS写的CSS,后面我会贴出完整代码,或者到 CSDN CODE 下载源码
先重置标记样式:
html, body, p, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
    margin: 0;
    padding: 0;
    border: 0;
    font-size: 100%;
    font: inherit;
    vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
    display: block;
}
body {
    line-height: 1;
}
ol, ul {
    list-style: none;
}
blockquote, q {
    quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
    content: '';
    content: none;
}
table {
    border-collapse: collapse;
    border-spacing: 0;
}
设置body:
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/playlist.js"></script>
playlist.js就是一堆json的歌单数据,数据是从网易云音乐上获取的,基本内容如下:
//因为做静态页有跨域问题,所以就直接把数据拿出来赋值给`playlist`这个对象上了,下面只保留了需要用到的数据对象
var playlist = {
    "result": {
        "tracks": [
            {
                "name": "歌曲名",
                "artists": [
                    {
                        "name": "演唱者",
                    }
                ],
                "album": {
                    "name": "专辑名",
                    "picUrl":专辑图",
                "duration": 时长(ms),
                "mp3Url": "音乐地址"
            },
            ...//等等等
        ],
    },
};
网易云音乐歌单json数据接口:https://music.163.com/api/playlist/detail?id=xxx
建立index.js,开始编码:
先定义一个播放状态对象playStatus:
//当前播放器状态
var playStatus = {
currentTrackLen: playlist.result.tracks.length, //歌单歌曲数
currentTrackIndex: 0, //当前播放的歌曲索引,默认加载第一首歌
currentTime: 0, //当前歌曲播放的时间
currentTotalTime: 0, //当前歌曲的总时间
playStatus: true, //true为播放状态,false为暂停状态
};
因为要用到时间的转换,所以编写一个时间转换函数:
var timeConvert = function(timestamp){
var minutes = Math.floor(timestamp / 60);
var seconds = Math.floor(timestamp - (minutes * 60));
if(seconds < 10) {
seconds = '0' + seconds;
}
timestamp = minutes + ':' + seconds;
return timestamp;
};
接下来编写一个对象,内部方法是控制播放器的:
//播放器控制方法对象
var playerControls = {
    //歌曲基本信息设置
    trackInfo: function(args){
        //playerlist是playlist.js中的歌单数据,根据需求进行数据读取即可
        var obj = playlist.result.tracks[playStatus.currentTrackIndex];
        args = args || {
            name:obj.name,
            artist:obj.artists[0].name,
            album:obj.album.name,
            albumPic:obj.album.picUrl + '?param=270y270',
            total:obj.duration,
            src: obj.mp3Url,
        };
        $('.player .trackInfo .name').text(args.name);
        $('.player .trackInfo .artist').text(args.artist);
        $('.player .trackInfo .album').text(args.album);   
        $('.player .albumPic').css('background','url(' + args.albumPic + ')');         
        $('.player .time .total').text(timeConvert(args.total / 1000)); //因为歌单数据中的播放长度用ms表示的,所以这里 / 1000
        playStatus.currentTotalTime = Math.floor(args.total / 1000);
        $('#audio source').attr('src',args.src); //切换音乐通过修改<source>中的src
    },
    //播放、暂停状态处理
    playStatus: function(){
        $('.player .controls .play i').attr('class', 'icon-' + (playStatus.playStatus?'pause':'play'));
        if(playStatus.playStatus){
            //用jquery获取<audio>对象,必须加上[0]
            $('#audio')[0].play();
        }else{
            $('#audio')[0].pause();
        }
    },
    //当前时间和进度处理
    playTime: function(){
        $('.player .time .current').text(timeConvert(playStatus.currentTime));
        $('.player .progress').css('width', playStatus.currentTime / playStatus.currentTotalTime * 100 + '%'); //同步进度条
    }
};
还剩下一个初始化方法了:
var init = function(){
    //置一下歌曲信息和播放状态
    playerControls.trackInfo();     
    playerControls.playStatus();
    //播放按钮事件监听
    $('.player .controls .play').click(function(){
        //修改播放状态
        playStatus.playStatus = !playStatus.playStatus; 
        //置播放信息
        playerControls.playStatus();
    });
    //上一曲按钮事件监听
    $('.player .controls .previous').click(function(){
        //边界判断
        if(playStatus.currentTrackIndex - 1 < 0){
            alert('已经没有上一首了');
        }else{
            playStatus.currentTrackIndex --;
        }
        //此处切换歌曲功能,原来打算采用直接修改src的方法实现,但是修改src后,之前播放的音乐还继续播放着,切换后的音乐却不播放,所以最终采用移除<audio>,再加入<audio>的方式来切换音乐
        $('#audio').remove();
        $('.player').append('<audio id="audio"><source src=""></audio>');  
        //更新音轨信息和播放状态       
        playerControls.trackInfo();
        playerControls.playStatus();
    });
    //下一曲按钮事件监听
    $('.player .controls .next').click(function(){
        if(playStatus.currentTrackIndex + 1 >= playStatus.currentTrackLen){
            alert('已经没有下一首了');
        }else{
            playStatus.currentTrackIndex ++;
        }
        //换src的方法没法切换声音,试了好几种方法都不行,只能删了再重建了
        $('#audio').remove();
        $('.player').append('<audio id="audio"><source src=""></audio>');          
        playerControls.trackInfo();
        playerControls.playStatus();
    });
    //用时钟来实时修改当前播放时间及播放完当前曲目自动下一曲
    setInterval(function(){
        playStatus.currentTime = $('#audio')[0].currentTime;           
        playerControls.playTime();
        if(playStatus.currentTime >= playStatus.currentTotalTime){
            $('.player .controls .next').click();
        }
    }, 300);
};
其后,再加入init()执行一下就OK了。
完整代码
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>H5音乐播放器</title>
    <link rel="stylesheet" type="text/css" href="css/index.css">
    <script type="text/javascript" src="js/jquery.min.js"></script>
    <script type="text/javascript" src="js/playlist.js"></script>
    <script type="text/javascript" src="js/index.js"></script>
</head>
<body>
    <p class="player">
        <p class="header">音乐播放器</p>
        <p class="albumPic"></p>
        <p class="trackInfo">
            <p class="name"></p>
            <p class="artist"></p>
            <p class="album"></p>
        </p>
        <p class="progress"></p>
        <p class="controls">
            <p class="play">
                <i class="icon-play"></i>
            </p>
            <p class="previous">
                <i class="icon-previous"></i>
            </p>
            <p class="next">
                <i class="icon-next"></i>
            </p>
        </p>      
        <p class="time">
            <p class="current"></p>
            <p class="total"></p>
        </p>
        <audio id="audio"><source src=""></audio>
    </p>
</body>
</html>
index.css:
@import 'reset.css';
@import 'fonts.css';
html,
body {
  height: 100%;
}
body {
  background-color: #111111;
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -webkit-align-items: center;
      -ms-flex-align: center;
          align-items: center;
  -webkit-box-pack: center;
  -webkit-justify-content: center;
      -ms-flex-pack: center;
          justify-content: center;
  color: #fff;
  font: 16px "微软雅黑";
}
.player {
  width: 375px;
  height: 550px;
  background-color: #2b2b2b;
  border-radius: 10px;
  position: relative;
}
.player .header {
  padding: 15px 0;
  text-align: center;
}
.player .albumPic {
  background-size: cover;
  width: 270px;
  height: 270px;
  margin: auto;
  border-radius: 10px;
}
.player .trackInfo {
  text-align: center;
  padding: 20px 0 15px;
  font-size: 14px;
  white-space: nowrap;
}
.player .trackInfo .name {
  font-size: 24px;
  margin-bottom: 10px;
  font-weight: bold;
}
.player .progress {
  width: 0;
  height: 20%;
  position: absolute;
  bottom: 0;
  left: 0;
  background-image: -webkit-linear-gradient(top, rgba(255, 255, 255, 0), #0099ff);
  background-image: linear-gradient(top, rgba(255, 255, 255, 0), #0099ff);
  border-bottom-left-radius: 10px;
  border-bottom-right-radius: 10px;
  opacity: .4;
}
.player .controls {
  position: relative;
}
.player .controls .play {
  cursor: pointer;
  width: 75px;
  height: 75px;
  border: 2px solid #ccc;
  border-radius: 50%;
  margin: auto;
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  -webkit-box-align: center;
  -webkit-align-items: center;
      -ms-flex-align: center;
          align-items: center;
  -webkit-box-pack: center;
  -webkit-justify-content: center;
      -ms-flex-pack: center;
          justify-content: center;
  color: #fff;
  font-size: 35px;
}
.player .controls .play:hover {
  font-size: 40px;
}
.player .controls .previous {
  cursor: pointer;
  position: absolute;
  top: 25px;
  font-size: 30px;
  left: 60px;
}
.player .controls .previous:hover {
  font-size: 32px;
}
.player .controls .next {
  cursor: pointer;
  position: absolute;
  top: 25px;
  font-size: 30px;
  right: 60px;
}
.player .controls .next:hover {
  font-size: 32px;
}
.player .time {
  width: 335px;
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  position: absolute;
  bottom: 0;
  padding: 20px;
  -webkit-box-align: center;
  -webkit-align-items: center;
      -ms-flex-align: center;
          align-items: center;
  -webkit-box-pack: justify;
  -webkit-justify-content: space-between;
      -ms-flex-pack: justify;
          justify-content: space-between;
}
index.js:
$().ready(function(){
    //当前播放器状态
    var playStatus = {
        currentTrackLen: playlist.result.tracks.length,
        currentTrackIndex: 0,
        currentTime: 0,
        currentTotalTime: 0,
        playStatus: true,
    };
    //播放器控制方法
    var playerControls = {
        //歌曲基本信息
        trackInfo: function(args){
            var obj = playlist.result.tracks[playStatus.currentTrackIndex];
            args = args || {
                name:obj.name,
                artist:obj.artists[0].name,
                album:obj.album.name,
                albumPic:obj.album.picUrl + '?param=270y270',
                total:obj.duration,
                src: obj.mp3Url,
            };
            $('.player .trackInfo .name').text(args.name);
            $('.player .trackInfo .artist').text(args.artist);
            $('.player .trackInfo .album').text(args.album);   
            $('.player .albumPic').css('background','url(' + args.albumPic + ')');         
            $('.player .time .total').text(timeConvert(args.total / 1000));
            playStatus.currentTotalTime = Math.floor(args.total / 1000);
            $('#audio source').attr('src',args.src);
        },
        //播放、暂停状态处理
        playStatus: function(){
            $('.player .controls .play i').attr('class', 'icon-' + (playStatus.playStatus?'pause':'play'));
            if(playStatus.playStatus){
                $('#audio')[0].play();
            }else{
                $('#audio')[0].pause();
            }
        },
        //当前时间和进度处理
        playTime: function(){
            $('.player .time .current').text(timeConvert(playStatus.currentTime));
            $('.player .progress').css('width', playStatus.currentTime / playStatus.currentTotalTime * 100 + '%');
        }
    };
    var timeConvert = function(timestamp){
        var minutes = Math.floor(timestamp / 60);
        var seconds = Math.floor(timestamp - (minutes * 60));
        if(seconds < 10) {
          seconds = '0' + seconds;
        }
        timestamp = minutes + ':' + seconds;
        return timestamp;
    };
    (function(){
        playerControls.trackInfo();     
        playerControls.playStatus();
        $('.player .controls .play').click(function(){
            playStatus.playStatus = !playStatus.playStatus; 
            playerControls.playStatus();
        });
        $('.player .controls .previous').click(function(){
            if(playStatus.currentTrackIndex - 1 < 0){
                alert('已经没有上一首了');
            }else{
                playStatus.currentTrackIndex --;
            }
            $('#audio').remove();
            $('.player').append('<audio id="audio"><source src=""></audio>');          
            playerControls.trackInfo();
            playerControls.playStatus();
        });
        $('.player .controls .next').click(function(){
            if(playStatus.currentTrackIndex + 1 >= playStatus.currentTrackLen){
                alert('已经没有下一首了');
            }else{
                playStatus.currentTrackIndex ++;
            }
            //换src的方法没法切换声音,试了好几种方法都不行,只能删了再重建了
            $('#audio').remove();
            $('.player').append('<audio id="audio"><source src=""></audio>');          
            playerControls.trackInfo();
            playerControls.playStatus();
        });
        setInterval(function(){
            playStatus.currentTime = $('#audio')[0].currentTime;           
            playerControls.playTime();
            if(playStatus.currentTime >= playStatus.currentTotalTime){
                $('.player .controls .next').click();
            }
        }, 300);
    })();
});



 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                 
            
                