一步一步实战html音乐播放器

王乐平      2022-02-07     450

关键词:

在这里我用HTML5从头开始一步一步来制作一个简约的音乐播放器,大家可以参考一下,接下来正式开始。


音乐播放器效果


播放器分析

这里将播放器分两块来做:

  • 视图层(html + css)
  • 逻辑层 ( js )

视图层分析

视图中包含:

  • 播放器容器
    • 播放器名称
    • 音乐专辑图
    • 音乐信息
      • 歌曲名
      • 歌手
      • 专辑名
    • 控制区
      • 上一曲
      • 播放
      • 下一曲
    • 播放进度条
    • 播放时间
      • 当前时间
      • 歌曲总时间
    • 音频控件
  • 页面背景

逻辑层分析

逻辑层处理包括:

  • 加载歌单
  • 渲染歌曲信息
    • 专辑图
    • 歌曲名
    • 歌手
    • 专辑名
    • 歌曲时长
    • 歌曲音频地址
  • 监听控制按钮
    • 播放按钮 (修改播放状态)
    • 上一曲(判断歌单边界,重新渲染歌曲信息)
    • 下一曲(判断歌单边界,重新渲染歌曲信息)
  • 定时器
    • 同步歌曲当前时间和播放进度条
    • 歌曲播放完,自动切换下一曲

好了,播放器基本分析完成,接下来开始编码了,先进行视图层的编写。


视图层结构编写

根据我在上面的视图层分析,来构建HTML结构。

建立index.html,结构编码如下:

<!-- 页面背景 -->
<body>
    <!-- 播放器容器 -->
    <div class="player">

        <!-- 播放器名称 -->
        <div class="header">音乐播放器</div>

        <!-- 音乐专辑图 -->
        <div class="albumPic"></div>

        <!-- 音乐信息  -->
        <div class="trackInfo">

            <!-- 歌曲名 -->
            <div class="name"></div>

            <!-- 歌手 -->
            <div class="artist"></div>

            <!-- 专辑名 -->
            <div class="album"></div>

        </div>

        <!-- 播放进度条 -->
        <div class="progress"></div>

        <!-- 控制区  -->
        <div class="controls">

            <!-- 播放 -->
            <div class="play">
                <i class="icon-play"></i>
            </div>

            <!-- 上一曲 -->
            <div class="previous">
                <i class="icon-previous"></i>
            </div>

            <!-- 下一曲 -->
            <div class="next">
                <i class="icon-next"></i>
            </div>

        </div>      

        <!-- 播放时间  -->
        <div class="time">

            <!-- 当前时间 -->
            <div class="current"></div>

            <!-- 歌曲总时间 -->
            <div class="total"></div>

        </div>

        <!-- 音频控件 -->
        <audio id="audio"><source src=""></audio>
    </div>
</body>

好了,结构编写完毕,接下来编写视图层样式。


视图层样式编写

:这里我是用LESS写的CSS,后面我会贴出完整代码,或者到 CSDN CODE 下载源码

先重置标记样式:

html, body, div, 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

//这里主要设置背景和flex布局,用于播放器垂直居中
@body-bg: #111;

html,body{
    height: 100%;
}

body{
    background-color: @body-bg;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    font: 16px "微软雅黑";
}

设置播放器容器 .player

//主要设置播放器的大小、背景颜色、定位等信息
@player-bg: lighten(@body-bg, 10%);
@player-w: 375px;
@player-h: 550px;

.player{
    width: @player-w;
    height: @player-h;
    background-color: @player-bg;
    border-radius: 10px;
    position: relative;
}

设置播放器名称.header样式:

.player{    
    .header{
        padding: 15px 0;
        text-align: center;
    }
}

专辑图.albumPic样式:

.player{
    .albumPic{
        background-image: url(http://p3.music.126.net/SR9eFEjRB0NsscxN7-fHMw==/3344714372906000.jpg); //这里先放一张临时图片,用于看效果,编写完成后,把这条属性删除即可
        background-size: cover; //背景模式
        width: @player-w * 0.72; //通过计算设置宽高,可直接用百分比
        height: @player-w * 0.72;
        margin: auto; //居中
        border-radius: 10px;
    }
}

专辑信息区域样式:

.player{
    .trackInfo{
        text-align: center;
        padding: 20px 0 15px;
        font-size: 14px;
        white-space: nowrap;

        //单独将歌曲名设置一下大小
        .name{
            font-size: 24px;
            margin-bottom: 10px;
            font-weight: bold;
        }
    }
}

播放进度条样式:

.player{
    .progress{
        width: 30%; //这里用于看效果,制作完成后,设置为0
        height: 20%;
        position: absolute; //用绝对定位放到播放器容器最下面和最左面
        bottom: 0;
        left: 0;
        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;

        //播放按钮同样采用flex布局,用于对内部的网络字体按钮垂直居中
        .play{
            cursor: pointer;
            width: 75px;
            height: 75px;
            border: 2px solid #ccc;
            border-radius: 50%; //加个圆框
            margin: auto;
            display: flex;
            align-items:center;
            justify-content:center;
            color: #fff;
            font-size: 35px;

            &:hover{
                font-size: 40px; //鼠标经过变大字体
            }
        }

        //上、下一曲 共用样式
        .btn(){
            cursor: pointer;
            position: absolute;         
            top: 25px;
            font-size: 30px;

            &:hover{
                font-size: 32px;
            }
        }

        //设置一下按钮位置
        .previous{  
            .btn;       
            left: 60px;
        }

        .next{
            .btn;
            right: 60px;
        }
    }
}

播放时间区域设置:

.player{
    .time{
        width: @player-w - 40px; //计算pdding后的宽度,可自行计算
        display: flex;
        position: absolute;
        bottom: 0;
        padding: 20px;
        align-items: center;
        justify-content: space-between; //两端分布
    }
}

好了,通过上面的样式设置,播放器的视图层已完毕,看下效果(三个播放控制按钮是引用的字体):

接下来开始编写逻辑层。


逻辑层编写

逻辑层用到了Jquery和歌曲清单数据playlist.js,先引用一下:

<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数据接口:http://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>
    <div class="player">
        <div class="header">音乐播放器</div>
        <div class="albumPic"></div>
        <div class="trackInfo">
            <div class="name"></div>
            <div class="artist"></div>
            <div class="album"></div>
        </div>
        <div class="progress"></div>
        <div class="controls">
            <div class="play">
                <i class="icon-play"></i>
            </div>
            <div class="previous">
                <i class="icon-previous"></i>
            </div>
            <div class="next">
                <i class="icon-next"></i>
            </div>
        </div>      
        <div class="time">
            <div class="current"></div>
            <div class="total"></div>
        </div>
        <audio id="audio"><source src=""></audio>
    </div>
</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;
  qt实战之酷狗音乐

...先放出效果图。这段时间忙,等有空时,将又一次架构。一步一步带领新手写出简版酷狗音乐。实现MV等播放。播放时的单词界面:实现平滑滚动效果 当中背景写真来源于酷我音乐。。。可点击进度条快进快退 。。歌词会... 查看详情

项目实战15.1—企业级堡垒机jumpserver一步一步搭建(代码片段)

本文收录在Linux运维企业架构实战系列环境准备系统:CentOS7IP:192.168.10.101关闭selinux和防火墙#CentOS7$setenforce0#可以设置配置文件永久关闭$systemctlstopiptables.service$systemctlstopfirewalld.service#CentOS6$setenforce0$serviceiptablesstop&n 查看详情

我的新书一步一步学springboot2:微服务项目实战

开心一笑【声音有磁性的人适合做直播,可以吸引到很多老铁】提出问题我的新书具体内容???购买地址淘宝地址1:https://detail.tmall.com/item.htm?spm=a230r.1.14.4.288c2acbVNjuUw&id=571212826401&cm_id=1401 查看详情

一步一步学springboot2微服务项目实战-黄文毅-2018年8月第一次印刷

properties配置文件的优先级高于.yml。在properties文件中配置了server.port=8080同时在.yml中配置了server.port=8090 SpringBoot将使用.properties中的8080端口@SpringBootApplication是一个组合注解包含@EnableAutoConfiguration@ComponentScan@SpringB 查看详情

贪吃蛇--[纯c实现]--[一步一步的讲解]--有音乐(代码片段)

目录一、游戏说明1.1游戏按键说明1.2计分系统二、游戏运行2.1游戏效果展示2.2一个报错的纠正 2.3 游戏代码三、游戏框架构建3.1游戏界面的大小3.2蛇头和蛇身3.2.1蛇头3.2.2蛇身3.3标记游戏区3.3.1存储游戏区的各个位置是什么3.3.2用... 查看详情

转载一步一步理解线段树

目录一、概述二、从一个例子理解线段树  创建线段树  线段树区间查询  单节点更新  区间更新三、线段树实战--------------------------一概述线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),... 查看详情

一步一步搭建vue

一步一步搭建自己搭建类似与vue-cli的框架第一步:搭建项目目录结构进入项目目录1.vue_demo>npminit-y(如果没有y则需要自己去写配置信息)2.安装依赖包vuedemo>npminstallvuewebpackwebpack-dev-servervue-loadervue-html-loadercss-loadervue-style-loadervue... 查看详情

一步一步理解线段树——转载自justdoit

目录一、概述二、从一个例子理解线段树  创建线段树  线段树区间查询  单节点更新  区间更新三、线段树实战--------------------------一概述线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组),... 查看详情

代码一步一步执行(同步)

】代码一步一步执行(同步)【英文标题】:Codeexecutionstepbystep(synchronous)【发布时间】:2014-07-0112:03:54【问题描述】:我的index.html中只有两行(见下文)代码。我想在执行第二行之前执行第一行。我该怎么做/确保这一点?目前... 查看详情

一步一步安装cygwin

...,双击安装包安装cygwin 选择"installfrominternet",点击下一步 选择安装目录,点击下一步 选择包的下载存放目录,点击“下一步”&nb 查看详情

一步一步学vue

为了提升代码的逼格,之后代码改为Vue文件组件,之前代码虽然读起来容易理解,而且适合在小的项目中使用,但是有如下缺点:全局定义(Globaldefinitions) 强制要求每个component中的命名不得重复字符串模板(Stringtemplates) 缺... 查看详情

一步一步理解paxos算法

转自:http://www.open-open.com/lib/view/open1420635646984.html背景Paxos算法是Lamport于1990年提出的一种基于消息传递的一致性算法。由于算法难以理解起初并没有引起人们的重视,使Lamport在八年后重新发表到TOCS上。即便如此paxos算法还是没... 查看详情

由浅入深理解latentdiffusion/stablediffusion:一步一步搭建自己的stablediffusionmodels

...和读者一起遨游diffusionmodels的世界!本文主要介绍带大家一步步搭建自己的stablediffusi 查看详情

贪吃蛇--[纯c实现]--[一步一步的讲解]--有音乐(代码片段)

目录一、游戏说明1.1游戏按键说明1.2计分系统二、游戏运行2.1游戏效果展示2.2一个报错的纠正 2.3 游戏代码三、游戏框架构建3.1游戏界面的大小3.2蛇头和蛇身3.2.1蛇头3.2.2蛇身3.3标记游戏区3.3.1存储游戏区的各个位置是什么3.3.2用... 查看详情

超详细入门精讲数据仓库原理&实战一步一步搭建数据仓库内附相应实验代码和镜像数据和脚本(代码片段)

超详细【入门精讲】数据仓库原理&实战一步一步搭建数据仓库内附相应实验代码和镜像数据和脚本感谢B站UP主哈喽鹏程!!!文章目录0.B站课程链接和搭建数据仓库资源下载1.项目介绍及1.1项目介绍1.2数据仓库架... 查看详情

一步一步造个ioc轮子:构造基本的ioc容器

一步一步造个Ioc轮子目录一步一步造个IoC轮子(一):Ioc是什么一步一步造个IoC轮子(二):详解泛型工厂一步一步造个IoC轮子(三):构造基本的IoC容器定义容器首先,我们来画个大饼,定义好构造函数,注册函数及获取函数... 查看详情

一步一步手绘springdi运行时序图

查看详情

一步一步手绘springioc运行时序图

查看详情