用vue简单写一个音乐播放器

简单地写一个功能比较全的音乐播放器

前言

因为音乐播放器是一个很可能在项目遇到的东西,早写总比晚写好。趁没事先写个。

思路

一个音乐播放器该有的东西:

  1. 封面,歌名,专辑,作者
  2. 控制器(上一首,下一首,暂停播放)
  3. 进度条(总进度,缓存进度,播放进度,播放时间,总时间,进度拖拽按钮)
  4. 播放方式(单曲循环,顺序播放,随机播放,列表循环)
  5. 声音(总声音长度,当前声音长度,声音长度拖拽按钮)
  6. 播放列表(序号,操作,歌名,歌手,专辑,时长)
  7. 提示区(各种成功错误提示)
  8. 歌词(等有时间后续开发:2019/06/10记录)
  9. 后续优化(包括但不限于加载中的进度条动画,加载中的时间显示,音乐列表的滚动条功能:2019/06/12记录)

进度

2019.06.10–仅完成1,2,3,5,7。
2019.06.12–完成1,2,3,4,5,6,7
未完待续

相关资料

B站音乐播放器(b站万岁,只要你是b站大会员我们就是朋友,欸嘿);
audio的属性与方法:https://www.jianshu.com/p/1fe701c9179f

截图

在这里插入图片描述

功能

暂停与播放

// 暂停与播放
play() {
     if (this.playing) {// 播放中,点击则为暂停
         this.playing = false;
         audio.pause();
     } else { // 暂停中,点击则为播放
         this.playing = true;
         audio.play();
     }
 },

音频监听初始化

 // 音频事件初始化
audioInit() {
    let _this = this;
    let progressL = this.$refs.track.offsetWidth; // 进度条总长

    // 事件如果不需要可以不写

    // 音频或视频文件已经就绪可以开始,在点击播放时触发
    audio.addEventListener('play', () => {
        console.log('play');
    });

    // 浏览器开始寻找指定的音频或视频
    audio.addEventListener('loadstart', () => {
        console.log('loadstart');

        // 当前音量进度
        _this.volume = audio.volume;
        _this.volumeX = _this.volume * _this.$refs.trackV.offsetWidth;
    });

    // 播放位置改变时触发[注意:播放和调整指示定位时都会触发](主要事件)
    audio.addEventListener('timeupdate', () => {
        // 当前播放时间
        _this.currentTime = _this.timeToString(audio.currentTime);

        // 总播放时间
        _this.totalTime = _this.timeToString(audio.duration);

        // 当前播放进度百分比
        let precent = audio.currentTime / audio.duration  || 0;

        // 当前播放进度
        _this.progressScaleX = precent.toFixed(3);

        // 当前缓存进度
        // 已缓存时间
        let buffered = audio.buffered.length ? audio.buffered.end(audio.buffered.length-1) : 0;
        _this.bufferedScaleX = (buffered / audio.duration).toFixed(3);

        // 当前进度按钮位置
        _this.thumbTranslateX = (precent * progressL).toFixed(3);

    });

    // 音频或视频能够不停顿地一直播放
    audio.addEventListener('canplaythrough', () => {
        console.log('canplaythrough');
    });

    // 音频或视频的时长已改变
    audio.addEventListener('durationchange', () => {
        console.log('durationchange');
        _this.totalTime = _this.timeToString(audio.duration)
    });

    // 在音频或视频终止加载时触发,包括终止当前播放(未加载完)进行下一首播放时也会触发
    audio.addEventListener('abort', () => {
        console.log('abort')
    });

    // 在音频或视频加载发生错误时触发
    audio.addEventListener('error', () => {
        console.log('error');
        console.log('-----networkState---------',audio.networkState);
        console.log('-----readyState---------',audio.readyState);
        switch (audio.error.code) {
             case 1:
                 _this.error = '未知错误';
                 break;
             case 2:
                 _this.error = '数据源发生未知错误';
                 break;
             case 3:
                 _this.error = '数据源解码错误';
                 break;
             case 4:
                 audio.networkState == 3 && (_this.error = '连接网络失败');
                 break;
         }

        setTimeout(() => {
            _this.error = '';
        },3000)
    });

    // 播放结束
    audio.addEventListener("ended", ()=> {
        console.log("ended");
        switch (_this.playType) {
            case 1: // 列表循环
                _this.index = _this.index+1 >= _this.songList.length ? 0 : _this.index+1;
                break;
            case 2: // 随机播放
                _this.index = Math.floor(Math.random()*_this.songList.length);
                break;
            case 3: // 单曲循环
                break;
        }
        _this.song = _this.songList[_this.index];
        _this.thumbSlide = true;
        // 此处需要一定的延迟让audio的dom结构获取到,额,你们自己意会或者可以试着去掉看bug
        setTimeout(() => {
            audio.play();
        },100);
        // 解决因为transition的回弹bug
        setTimeout(() => {
            _this.thumbSlide = false;
        }, 1000)
    }, true);
},
滑动进度条
// 滑动进度条
slideProgress() {
    // 由于滑动事件有相同代码,所以进行了简单的封装,具体原理在注释代码中。
    this.slideFn('progress', this.$refs.thumb, this.$refs.track.offsetWidth);
    /*let thumb = this.$refs.thumb;
    let _this = this;

    thumb.onmousedown = function (e) {
        // 移动时暂停播放并设置transition: none,解决在滑动结束后出现回弹的bug
        audio.pause();
        _this.thumbSlide = true;

        let progressL = _this.$refs.track.offsetWidth;
        let mouseInitX = e.clientX,
            mouseEndX = 0,
            moveX = 0,
            thumbInitX = _this.stringToNum(thumb.style.transform),
            thumbEndX = 0;

        document.onmousemove = function (e) {
            mouseEndX = e.clientX;
            moveX = mouseEndX - mouseInitX;

            let a = thumbInitX - 0 + moveX;
            if (moveX > 0) {
                thumbEndX = a > progressL ? progressL : a;
            } else {
                thumbEndX = a <= 0 ? 0 : a;
            }

            _this.progressScaleX = thumbEndX / progressL;
            _this.thumbTranslateX = thumbEndX;
        };

        document.onmouseup = function (e) {
            audio.currentTime = thumbEndX * audio.duration / progressL;
            audio.play();
            _this.thumbSlide = false;
            document.onmousedown = null;
            document.onmousemove = null;
            document.onmouseup = null;
        }
    }*/
},
滑动音量
// 滑动音量
slideVolume() {
    // 由于滑动事件有相同代码,所以进行了简单的封装,具体原理在注释代码中。
    this.slideFn('volume', this.$refs.thumbV, this.$refs.trackV.offsetWidth);
    /*let thumb = this.$refs.thumbV;
    let _this = this;

    thumb.onmousedown = function (e) {
        // 移动时暂停播放并设置transition: none,解决在滑动结束后出现回弹的bug
        _this.thumbVSlide = true;

        let progressL = _this.$refs.trackV.offsetWidth;
        let mouseInitX = e.clientX,
            mouseEndX = 0,
            moveX = 0,
            thumbInitX = _this.stringToNum(thumb.style.transform),
            thumbEndX = 0;

        document.onmousemove = function (e) {
            mouseEndX = e.clientX;
            moveX = mouseEndX - mouseInitX;

            let a = thumbInitX - 0 + moveX;
            if (moveX > 0) {
                thumbEndX = a > progressL ? progressL : a;
            } else {
                thumbEndX = a <= 0 ? 0 : a;
            }

            _this.volume = thumbEndX / progressL;
            _this.volumeX = thumbEndX;
        };

        document.onmouseup = function (e) {
            audio.volume = thumbEndX / progressL;
            _this.thumbVSlide = false;
            document.onmousedown = null;
            document.onmousemove = null;
            document.onmouseup = null;
        }
    }*/
},
上一首与下一首
// 上一首
skipBack() {
    this.skipFn('skipBack');
},
// 下一首
skipForward() {
    this.skipFn('skipForward');
},
//上下首封装
skipFn(type) {
    switch (this.playType) {
        case 2: // 随机播放
            this.index = Math.floor(Math.random()*this.songList.length);
            break;
        default:
            if (type == 'skipBack') {
                this.index-1>= 0? this.index -- : 0;
            } else {
                this.index = this.index+1>= this.songList.length? this.songList.length-1: this.index+1;
            }
            break;
    }

    this.song = this.songList[this.index];
    this.playing = true;
    setTimeout(() => {
        this.totalTime = '00:00';
        audio.play();
    },100)
},
选择播放方式
// 选择播放方式
chosePlayType() {
    this.playType = this.playType+1 > 3 ? 1 : this.playType+1;
},
js判断当前页面是否激活
// 解决页面切换到后台或者切换到其他页面一段时间后,切回时出现进度条回弹的bug
// 想看此bug的可以不调用该方法,并在播放之后切换到其他页面一段时间后切回,会发现进度条出现回弹的bug,因为transition的问题
setPageListen() {
    let _this = this;
    // js判断浏览当前页(关键词:当前,浏览)
    // 可以通过document.hidden属性判断当前页面是否是激活状态。
    // 兼容性:IE10+,Firefox10+,Chrome14+,Opera12.1+,Safari7.1+
    var hiddenProperty = 'hidden' in document ? 'hidden' :
        'webkitHidden' in document ? 'webkitHidden' :
            'mozHidden' in document ? 'mozHidden' :
                null;
    var visibilityChangeEvent = hiddenProperty.replace(/hidden/i, 'visibilitychange');
    var onVisibilityChange = function(){
        if (!document[hiddenProperty]) {
            document.title='被发现啦(*´∇`*)';
            _this.thumbSlide = true;
            setTimeout(() => {
                _this.thumbSlide = false;
            },300)
        }else{
            document.title='藏好啦(つд⊂)  ';
        }
    };
    document.addEventListener(visibilityChangeEvent, onVisibilityChange);
},
公方法
// 字符串转数字
stringToNum(str) {
    return Number(str.replace(/translateX\(|px\)/g , ''))
},
// 秒值转字符串
timeToString(param){
    param = parseInt(param);
    let hh = '',mm = '',ss = '';
    if (param>=0 && param<60) {
        param<10? ss = '0'+ param :ss = param;
        return '00:'+ ss;
    } else if (param>=60 && param<3600) {
        mm = parseInt(param/60);
        mm<10?mm = '0'+mm :mm;
        (param-parseInt(mm*60))<10?ss='0'+ String(param-parseInt(mm*60)) : ss =  param-parseInt(mm*60);
        return mm + ':' +ss;
    }
},

完整代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="apple-mobile-web-app-title" content="">
    <meta name="format-detection" content="telephone=no">
    <!-- android部分浏览器会缓存页面 -->
    <meta http-equiv="Expires" content="0">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Cache-control" content="no-cache">
    <meta http-equiv="Cache" content="no-cache">
    <title>音乐播放器</title>

    <!--es6转es5插件-->
    <script src="https://cdn.bootcss.com/babel-polyfill/7.4.4/polyfill.min.js" type="text/javascript"></script>
    <!--vue-->
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>

    <style>
        /*init.style*/
        a, article, body, div, footer, form, h1, h2, h3, h4, h5, h6, header, html, iframe, img, input, li, p, section, span, textarea, ul, video {
            margin: 0;
            padding: 0;
        }
    </style>

    <style>


        /*公共css*/
        .forbid-transition{
            transition: none !important;
        }
    </style>

    <style>
        /*控制器综合*/
        .audioplayer{
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 60px;
            white-space: nowrap;
            background-color: #384245;
            color: #737A7C;
            font-size: 14px;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            z-index: 104;
        }
        .audioplayer .ap-status{
            position: relative;
            margin: 0 auto;
            width: 1100px;
        }
        .audioplayer .ap-controller{
            display: flex;
            width: 100%;
            height: 100%;
            background-color: #384245;
            position: relative;
        }
        .audioplayer .ap-button {
            cursor: pointer;
            transition: 0.2s;
        }
        .audioplayer .ap-controller .ap-controller-left > *,
        .audioplayer .ap-controller .ap-controller-right > * {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            height: 100%;
            vertical-align: middle;
        }
        svg:not(:root) {
            overflow: hidden;
        }
        .audioplayer svg:not(.ap-svg-exclude) {
            width: 28px;
            height: 28px;
            vertical-align: middle;
            transition: 0.2s;
        }
        .audioplayer .ap-progress-thumb svg{
            width: 21px;
            height: 19px;
            display: block;
            cursor: pointer;
        }
        .audioplayer .ap-controller .ap-controller-center .ap-buffer-bar,
        .audioplayer .ap-controller .ap-controller-center .ap-progress-bar{
            position: absolute;
            -webkit-transform-origin: 0 0;
            transform-origin: 0 0;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            transition: transform 0.5s linear, -webkit-transform 0.5s linear;
        }

        /*left*/
        .audioplayer .ap-controller-left .ap-cover{
            width: 60px;
            height: 60px;
            background: #E7E7E7;
            -webkit-background-size: 100% 100%;
            background-size: 100% 100%;
            cursor: pointer;
        }
        .audioplayer .ap-controller-left .ap-button{
            padding: 0 8px;
        }
        .audioplayer .ap-controller-left .ap-skipback-button{
            margin-left: 16px;
        }
        .audioplayer .ap-controller-left .ap-skipforward-button{
            margin-right: 16px;
        }

        /*center*/
        .audioplayer .ap-controller-center{
            padding: 10px 0;
            display: flex;
            flex: 1;
            flex-direction: column;
            overflow: hidden;
        }
        .audioplayer .ap-controller-center .ap-song-msg{

        }
        .audioplayer .ap-controller-center .ap-song-msg{
            display: flex;
            flex: 1;
            align-items: center;
        }
        .audioplayer .ap-controller-center .ap-song-msg .ap-name{
            cursor: pointer;
            overflow: hidden;
            white-space: nowrap;
            -ms-text-overflow: ellipsis;
            text-overflow: ellipsis;
        }
        .audioplayer .ap-controller-center .ap-song-msg .ap-artist{
            font-size: 12px;
            margin-left: 6px;
            cursor: default;
            white-space: nowrap;
            -ms-text-overflow: ellipsis;
            text-overflow: ellipsis;
        }
        .audioplayer .ap-controller-center .ap-song-msg .ap-time{
            font-size: 12px;
            flex: 1;
            text-align: right;
            cursor: default;
            min-width: 110px;
        }
        .audioplayer .ap-controller-center .ap-song-msg .ap-time .ap-current-time{
            color: #fff;
        }
        .audioplayer .ap-controller-center .ap-progress-line{
            flex: 1;
            display: flex;
            align-items: center;
        }
        .audioplayer .ap-controller-center .ap-play-track{
            position: relative;
            width: 100%;
            height: 3px;
        }
        .audioplayer .ap-controller-center .ap-play-bar-wrap{
            position: absolute;
            top: 0;
            left: 0;
            bottom: 0;
            right: 0;
            border-radius: 1.5px;
            overflow: hidden;
            background: #909090;
        }
        .audioplayer .ap-controller-center .ap-buffer-bar{
            background: rgba(50, 176, 218, 0.47);
        }
        .audioplayer .ap-controller-center .ap-progress-bar{
            background: #32B0DA;;
        }
        .audioplayer .ap-controller .ap-controller-center .ap-progress-thumb{
            transition: -webkit-transform 0.5s linear;
            transition: transform 0.5s linear;
            transition: transform 0.5s linear, -webkit-transform 0.5s linear;
            position: absolute;
            top: -5px;
            left: -4px;
        }

        /*right*/
        .audioplayer .ap-controller-right {
            width: 360px;
            padding: 0 20px;
        }
        .audioplayer .ap-controller-right .ap-line {
            font-size: 24px;
            padding: 0 8px;
            cursor: default;
        }
        .audioplayer .ap-controller-right .ap-button {
            padding: 0 8px;
        }
        .audioplayer .ap-controller-right .ap-volume-line {
            width: 86px;
            margin-right: 8px;
            height: 28px;
        }
        .audioplayer .ap-controller-right .ap-line-draw {
            height: 24px;
            width: 1px;
            background: #979797;
        }
        .audioplayer .ap-controller-right .ap-volume-track {
            position: relative;
            width: 100%;
            height: 3px;
        }
        .audioplayer .ap-controller-right .ap-volume-bar-wrap {
            position: absolute;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            border-radius: 1.5px;
            overflow: hidden;
            background: #909090;
        }
        .audioplayer .ap-controller-right .ap-volume-bar {
            position: absolute;
            -webkit-transform-origin: 0 0;
            transform-origin: 0 0;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            background: #32B0DA;
        }
        .audioplayer .ap-controller-right .ap-volume-thumb {
            position: absolute;
            top: -4px;
            left: -6px;
            cursor: pointer;
        }
        .audioplayer .ap-controller-right .ap-volume-thumb-dot {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: #32B0DA;
            -webkit-transform: scale(0);
            transform: scale(0);
            transition: 0.2s;
        }
        .audioplayer .ap-controller-right .ap-volume-line:hover .ap-volume-thumb-dot{
            transform: scale(1);
        }
        .audioplayer .ap-controller-right .ap-playlist-button-bg {
            background: #4A5355;
            height: 28px;
            border-radius: 14px;
            padding: 0 8px 0 6px;
        }
        .audioplayer .ap-controller-right .ap-playlist-button-amount {
            color: #fff;
            font-size: 12px;
            vertical-align: middle;
            transition: 0.2s;
        }


        /*提示区*/
        .audioplayer .ap-toast {
            position: absolute;
            bottom: 72px;
            left: 50%;
            -webkit-transform: translate(-50%, 0);
            transform: translate(-50%, 0);
            padding: 10px 20px;
            background-color: rgba(0, 0, 0, 0.8);
            border-radius: 4px;
            color: #fff;
            opacity: 1;
            pointer-events: none;
            transition: 0.3s;
        }
        .audioplayer .ap-toast-hide {
            opacity: 0;
            -webkit-transform: translate(-50%, 12px);
            transform: translate(-50%, 12px);
        }
    </style>

    <style>
        /*播放列表*/
        .ps {
            /*overflow: hidden !important;*/
            overflow-y: scroll;
            overflow-anchor: none;
            -ms-overflow-style: none;
            touch-action: auto;
            -ms-touch-action: auto;
        }
        ol{
            padding: 0;
            margin: 0;
        }

        .audioplayer .ap-playlist {
            width: 620px;
            position: absolute;
            right: 0;
            bottom: 60px;
            background-color: #384245;
            border-radius: 6px 6px 0 0;
            transition: 0.3s;
            -webkit-transform-origin: 0 100%;
            transform-origin: 0 100%;
            color: rgba(255, 255, 255, 0.4);
            font-weight: lighter;
        }
        .audioplayer .ap-playlist.ap-playlist-hide {
            -webkit-transform: translateY(100%);
            transform: translateY(100%);
        }
        .audioplayer .ap-playlist .ap-playlist-header {
            padding: 16px 20px 16px 20px;
            display: flex;
            cursor: default;
        }
        .audioplayer .ap-playlist .ap-playlist-header .ap-playlist-header-name {
            flex: 1;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
        }
        .audioplayer .ap-playlist-header .ap-playlist-header-close{
            font-size: 14px;
            margin-left: 20px;
        }
        .audioplayer .ap-playlist .ap-playlist-body {
            position: relative;
            background-color: #455155;
            height: 260px;
        }
        .audioplayer .ap-playlist .ap-playlist-line{
            display: flex;
            cursor: default;
            justify-content: space-between;
        }
        .audioplayer .ap-playlist .ap-playlist-line:hover{
            background: #425961;
            color: #50c9da;
        }
        .audioplayer .ap-playlist .ap-playlist-line.ap-playlist-line-active{
            background: #425961;
            color: #32B0DA;
        }
        .audioplayer .ap-playlist .ap-playlist-body li{
            padding: 10px 50px 10px 20px;
            transition: 0.2s;
            cursor: pointer;
            line-height: 21px;
        }
    </style>
</head>
<body>
<div id="app">
    <!-- 浏览器原生播放器 -->
    <audio :src="song.url" id="audio" controls></audio>


    <!-- 音乐播放器 -->
    <div id="music-container" class="audioplayer">
        <div class="ap-status">
            <!-- 播放列表 -->
            <div class="ap-playlist" :class="{'ap-playlist-hide': !listShow}">
                <!-- 列表头部 -->
                <div class="ap-playlist-header">
                    <div class="ap-playlist-header-name">
                        <span class="ap-playlist-header-title">播放列表</span>
                        <span class="ap-playlist-header-amount">(共{{songList.length}}首)</span>
                    </div>
                    <div class="ap-playlist-header-artist">歌手</div>
                    <div class="ap-playlist-header-close" @click="handleList">×</div>
                </div>
                <!-- 列表内容 -->
                <div class="ap-playlist-body ps">
                    <ol>
                        <li class="ap-playlist-line"
                            :class="{'ap-playlist-line-active': index == i}"
                            v-for="(li, i) in songList"
                            @click="choseSong(li, i)"
                            :key="li.id" :data-index="i">
                            <div class="ap-playlist-line-name">{{li.name}}-{{li.album}}</div>
                            <div class="ap-playlist-line-artist">{{li.artist}}</div>
                        </li>
                    </ol>
                </div>
            </div>
            <!-- 控制器综合,包含控制器,音量,进度条等 -->
            <div class="ap-controller">
                <div class="ap-controller-left">
                    <!-- 封面 -->
                    <div class="ap-cover" :style="{backgroundImage: 'url('+song.cover+')'}"></div>
                    <!-- 上一首 -->
                    <div class="ap-button ap-skipback-button" @click="skipBack">
                        <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M20.229 24.343l-10.286-6.171c-0.914-0.571-1.486-1.371-1.486-2.171s0.571-1.714 1.486-2.171l10.286-6.171c0.914-0.571 1.829-0.686 2.629-0.229 0.686 0.457 1.143 1.257 1.143 2.286v12.571c0 1.029-0.457 1.943-1.143 2.286-0.343 0.229-0.686 0.229-1.029 0.229-0.457 0.114-1.029-0.114-1.6-0.457zM21.143 9.143l-10.286 6.171c-0.343 0.229-0.571 0.457-0.571 0.686s0.229 0.571 0.571 0.686l10.286 6.171c0.457 0.229 0.686 0.229 0.914 0.229 0.229-0.114 0.229-0.457 0.229-0.8v-12.571c0-0.457-0.114-0.686-0.229-0.8 0 0-0.114 0-0.229 0-0.229-0.114-0.457 0-0.686 0.229z"></path><path d="M8.229 7.771v16.571c0 0.457 0.343 0.914 0.914 0.914v0c0.457 0 0.914-0.343 0.914-0.914v-16.571c0-0.457-0.343-0.914-0.914-0.914v0c-0.457 0-0.914 0.343-0.914 0.914z"></path></svg>
                    </div>
                    <!-- 播放与暂停 -->
                    <div class="ap-button ap-play-button" @click="play">
                        <svg v-if="!playing" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M22.756 16.711l-8.8 5.422c-0.444 0.267-0.978 0.089-1.244-0.267-0.089-0.178-0.089-0.267-0.089-0.444v-10.844c0-0.533 0.356-0.889 0.889-0.889 0.178 0 0.356 0.089 0.444 0.089l8.8 5.422c0.444 0.267 0.533 0.8 0.267 1.244-0.089 0.089-0.178 0.178-0.267 0.267z"></path></svg>
                        <svg v-else version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M11.556 8.889v0c0.711 0 1.333 0.622 1.333 1.333v11.556c0 0.711-0.622 1.333-1.333 1.333v0c-0.711 0-1.333-0.622-1.333-1.333v-11.556c0-0.711 0.622-1.333 1.333-1.333z"></path><path d="M21.333 8.889v0c0.711 0 1.333 0.622 1.333 1.333v11.556c0 0.711-0.622 1.333-1.333 1.333v0c-0.711 0-1.333-0.622-1.333-1.333v-11.556c0-0.711 0.622-1.333 1.333-1.333z"></path></svg>
                    </div>
                    <!-- 下一首 -->
                    <div class="ap-button ap-skipforward-button" @click="skipForward">
                        <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M10.514 24.914c-0.343 0-0.686-0.114-1.029-0.229-0.686-0.457-1.143-1.257-1.143-2.286v-12.571c0-1.029 0.457-1.943 1.143-2.286 0.686-0.457 1.714-0.343 2.629 0.229l10.286 6.171c0.914 0.571 1.486 1.371 1.486 2.171s-0.571 1.714-1.486 2.171l-10.286 6.171c-0.571 0.229-1.143 0.457-1.6 0.457zM10.514 8.8c-0.114 0-0.114 0-0.229 0-0.229 0.114-0.229 0.457-0.229 0.8v12.571c0 0.457 0.114 0.686 0.229 0.8s0.457 0.114 0.914-0.229l10.286-6.171c0.343-0.229 0.571-0.457 0.571-0.686s-0.229-0.457-0.571-0.686l-10.286-6.171c-0.343-0.114-0.571-0.229-0.686-0.229z"></path><path d="M23.086 6.857v0c-0.457 0-0.914 0.343-0.914 0.914v16.571c0 0.457 0.343 0.914 0.914 0.914v0c0.457 0 0.914-0.343 0.914-0.914v-16.571c0-0.571-0.343-0.914-0.914-0.914z"></path></svg>
                    </div>
                </div>
                <div class="ap-controller-center">
                    <!-- 歌曲信息 -->
                    <div class="ap-song-msg">
                        <span class="ap-name">{{song.name}}</span>
                        <span class="ap-artist">-{{song.artist}}</span>
                        <span class="ap-time">
                            <span class="ap-current-time">{{currentTime}}</span>
                            <span> / </span>
                            <span class="ap-total-time">{{totalTime}}</span>
                        </span>
                    </div>
                    <!-- 进度条 -->
                    <div class="ap-progress-line">
                        <div class="ap-play-track" ref="track">
                            <!-- 总进度 -->
                            <div class="ap-play-bar-wrap">
                                <!-- 缓存进度 -->
                                <div class="ap-buffer-bar" :style="{transform: 'scaleX('+bufferedScaleX+')'}"></div>
                                <!-- 当前进度 -->
                                <div class="ap-progress-bar" :class="{'forbid-transition': thumbSlide}" :style="{transform: 'scaleX('+progressScaleX+')'}"></div>
                            </div>
                            <!-- 进度按钮 -->
                            <div class="ap-progress-thumb" :class="{'forbid-transition': thumbSlide}" ref="thumb" :style="{transform: 'translateX('+thumbTranslateX+'px)'}">
                                <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 35 32" class="ap-svg-exclude"><path fill="#32b0da" opacity="0.28" d="M11.789 1.207h11.789c3.72 0 6.737 3.016 6.737 6.737v8.421c0 3.72-3.016 6.737-6.737 6.737h-11.789c-3.72 0-6.737-3.016-6.737-6.737v-8.421c0-3.72 3.016-6.737 6.737-6.737z"></path><path fill="#32b0da" opacity="0.14" d="M12.211 2.050h10.947c3.488 0 6.316 2.828 6.316 6.316v7.579c0 3.488-2.828 6.316-6.316 6.316h-10.947c-3.488 0-6.316-2.828-6.316-6.316v-7.579c0-3.488 2.828-6.316 6.316-6.316z"></path><path fill="#000" d="M22.737 2.969c3.256 0 5.895 2.627 5.895 5.866v6.705c0 3.24-2.639 5.866-5.895 5.866h-10.105c-3.256 0-5.895-2.626-5.895-5.866v-6.705c0-3.239 2.639-5.866 5.895-5.866h10.105z"></path><path fill="#fff" d="M22.737 2.969c3.256 0 5.895 2.627 5.895 5.866v6.705c0 3.24-2.639 5.866-5.895 5.866h-10.105c-3.256 0-5.895-2.626-5.895-5.866v-6.705c0-3.239 2.639-5.866 5.895-5.866h10.105z"></path><path fill="#333" d="M13.053 9.706c0.697 0 1.263 0.566 1.263 1.263v2.526c0 0.697-0.566 1.263-1.263 1.263s-1.263-0.566-1.263-1.263v-2.526c0-0.697 0.566-1.263 1.263-1.263z"></path><path fill="#333" d="M22.316 9.706c0.697 0 1.263 0.566 1.263 1.263v2.526c0 0.697-0.566 1.263-1.263 1.263s-1.263-0.566-1.263-1.263v-2.526c0-0.697 0.566-1.263 1.263-1.263z"></path><path fill="#333" d="M26.947 15.538c0 2.321-1.89 4.211-4.211 4.211h-10.105c-2.321 0-4.211-1.89-4.211-4.211v-6.737c0-2.323 1.89-4.211 4.211-4.211h10.105c2.321 0 4.211 1.888 4.211 4.211v6.737zM23.020 2.935l0.6-1.041c0.349-0.603 0.143-1.376-0.461-1.725s-1.376-0.141-1.725 0.461l-1.314 2.275h-4.871l-1.314-2.275c-0.349-0.603-1.122-0.81-1.725-0.461-0.605 0.349-0.812 1.122-0.463 1.725l0.601 1.041c-3.121 0.15-5.612 2.708-5.612 5.866v6.737c0 3.256 2.639 5.895 5.895 5.895h10.105c3.256 0 5.895-2.639 5.895-5.895v-6.737c0-3.16-2.491-5.716-5.612-5.866z"></path></svg>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="ap-controller-right">
                    <!-- 竖线 -->
                    <div class="ap-line">
                        <div class="ap-line-draw"></div>
                    </div>
                    <!-- 音量 -->
                    <div class="ap-button ap-volume-button">
                        <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M16.571 27.429c-0.457 0-0.8-0.114-1.143-0.457l-5.943-5.257h-2.743c-1.257 0-2.286-1.029-2.286-2.286v-5.829c0-1.257 1.029-2.286 2.286-2.286h2.743l5.943-5.257c0.686-0.571 1.829-0.571 2.4 0.114 0.229 0.343 0.457 0.686 0.457 1.143v18.286c0 1.029-0.8 1.829-1.714 1.829zM6.857 13.143c-0.343 0-0.571 0.229-0.571 0.571v5.829c0 0.343 0.229 0.571 0.571 0.571h3.429l6.4 5.714v-18.171l-6.4 5.6h-3.429z"></path><path d="M22.154 21.797c1.714-1.45 2.703-3.532 2.703-5.797 0-2.247-0.864-4.207-2.449-5.66-0.349-0.32-0.891-0.296-1.211 0.053s-0.296 0.891 0.053 1.211c1.232 1.129 1.893 2.631 1.893 4.397 0 1.755-0.763 3.361-2.097 4.489-0.361 0.306-0.406 0.847-0.101 1.208s0.847 0.406 1.208 0.101z"></path><path d="M25.355 24.539c2.517-2.139 3.959-5.253 3.959-8.653 0-3.22-1.389-6.329-3.697-8.509-0.344-0.325-0.887-0.31-1.212 0.035s-0.31 0.887 0.035 1.212c1.971 1.861 3.16 4.523 3.16 7.263 0 2.896-1.221 5.533-3.355 7.347-0.361 0.307-0.405 0.848-0.098 1.208s0.848 0.405 1.208 0.098z"></path></svg>
                    </div>
                    <!-- 音量进度条 -->
                    <div class="ap-volume-line">
                        <div class="ap-volume-track" ref="trackV">
                            <div class="ap-volume-bar-wrap">
                                <div class="ap-volume-bar" :class="{'forbid-transition': thumbVSlide}" :style="{transform: 'scaleX('+volume+')'}"></div>
                            </div>
                            <div class="ap-volume-thumb" ref="thumbV" :class="{'forbid-transition': thumbVSlide}" :style="{transform: 'translateX('+volumeX+'px)'}">
                                <div class="ap-volume-thumb-dot"></div>
                            </div>
                        </div>
                    </div>
                    <!-- 播放模式 -->
                    <div class="ap-button ap-order-button" @click="chosePlayType">
                        <!-- 列表循环 -->
                        <svg v-if="playType == 1" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M25.6 15.657c-0.343 0-0.686 0.343-0.686 0.686 0 0.114 0 0.229 0 0.229v1.829c0 2.4-1.829 4.229-4.229 4.229h-10.286l0.914-0.8c0.114-0.114 0.229-0.343 0.229-0.457 0-0.343-0.343-0.686-0.686-0.686-0.229 0-0.457 0.114-0.571 0.229l-2.057 1.829-0.8 0.686 2.857 2.514c0.114 0.229 0.343 0.343 0.571 0.343 0.457 0 0.8-0.229 0.8-0.686 0-0.229-0.114-0.457-0.343-0.571l-1.029-0.914v-0.114h10.4c3.086 0 5.6-2.514 5.6-5.6v-1.829c0-0.114 0-0.114 0-0.229 0-0.343-0.343-0.686-0.686-0.686zM8.229 16.571v-1.829c0-2.4 1.829-4.229 4.229-4.229h10.971l-0.914 0.914c-0.229 0.114-0.343 0.343-0.343 0.571 0 0.343 0.343 0.686 0.8 0.686 0.229 0 0.457-0.114 0.571-0.343l1.486-1.371 1.257-1.257-2.743-2.629c-0.229-0.114-0.457-0.229-0.686-0.229-0.457 0-0.686 0.343-0.686 0.8 0 0.229 0.114 0.343 0.229 0.571l0.914 0.8v0.114h-10.971c-2.971 0-5.486 2.514-5.486 5.6v1.829c0 0.114 0 0.114 0 0.229 0 0.343 0.343 0.686 0.686 0.686s0.686-0.343 0.686-0.686c0-0.114 0-0.229 0-0.229z"></path></svg>
                        <!-- 随机播放 -->
                        <svg v-else-if="playType == 2" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M17.6 15.543c0.229 0 0.457-0.114 0.571-0.229l2.629-2.4c0.914-0.8 2.171-1.257 3.429-1.486l-1.029 0.914c-0.229 0.114-0.343 0.343-0.343 0.457 0 0.343 0.343 0.686 0.8 0.686 0.229 0 0.457-0.114 0.686-0.229l3.086-2.4-3.086-2.514c-0.114-0.229-0.343-0.343-0.571-0.343-0.457 0-0.914 0.229-0.914 0.686 0 0.229 0.114 0.457 0.343 0.571v0l1.029 0.8c-1.714 0.114-3.314 0.8-4.457 1.829l-2.629 2.4c-0.114 0.114-0.229 0.343-0.229 0.457 0 0.229 0.114 0.343 0.229 0.457 0.114 0.343 0.343 0.343 0.457 0.343zM14.057 17.486c-0.229 0-0.457 0.114-0.571 0.229l-2.743 2.514c-1.143 0.914-2.629 1.371-4.114 1.371 0 0 0 0 0 0-0.457 0-0.8 0.343-0.8 0.686s0.343 0.686 0.8 0.686c0 0 0 0 0 0 1.943 0 3.771-0.571 5.257-1.829l2.857-2.514c0.114-0.114 0.229-0.343 0.229-0.457 0-0.229-0.114-0.343-0.229-0.457-0.229-0.114-0.457-0.229-0.686-0.229zM23.771 19.543c-0.457 0-0.8 0.343-0.8 0.686 0 0.229 0.114 0.343 0.343 0.457l1.029 0.8c-1.029-0.114-2.057-0.457-2.857-1.029l-9.029-8.229c-1.486-1.371-3.543-2.171-5.714-2.171-0.571 0-1.029 0.343-1.029 0.686s0.457 0.686 0.914 0.686c1.714 0 3.429 0.686 4.571 1.714l9.029 8.229 0.229 0.114c1.143 0.8 2.514 1.257 4 1.486l-1.143 0.914c-0.343 0.114-0.457 0.343-0.457 0.571 0 0.343 0.343 0.686 0.8 0.686 0.229 0 0.457-0.114 0.571-0.343l3.086-2.514-3.086-2.629c0-0.114-0.229-0.114-0.457-0.114z"></path></svg>
                        <!-- 单曲循环 -->
                        <svg v-else version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M25.6 15.657c-0.343 0-0.686 0.343-0.686 0.686 0 0.114 0 0.229 0 0.229v1.829c0 2.4-1.829 4.229-4.229 4.229h-10.286l0.914-0.8c0.114-0.114 0.229-0.343 0.229-0.457 0-0.343-0.343-0.686-0.686-0.686-0.229 0-0.457 0.114-0.571 0.229l-2.057 1.829-0.8 0.686 2.857 2.514c0.114 0.229 0.343 0.343 0.571 0.343 0.457 0 0.8-0.229 0.8-0.686 0-0.229-0.114-0.457-0.343-0.571l-1.029-0.914v-0.114h10.4c3.086 0 5.6-2.514 5.6-5.6v-1.829c0-0.114 0-0.114 0-0.229 0-0.343-0.343-0.686-0.686-0.686zM8.229 16.571v-1.829c0-2.4 1.829-4.229 4.229-4.229h10.971l-0.914 0.914c-0.229 0.114-0.343 0.343-0.343 0.571 0 0.343 0.343 0.686 0.8 0.686 0.229 0 0.457-0.114 0.571-0.343l1.486-1.371 1.257-1.257-2.743-2.629c-0.229-0.114-0.457-0.229-0.686-0.229-0.457 0-0.686 0.343-0.686 0.8 0 0.229 0.114 0.343 0.229 0.571l0.914 0.8v0.114h-10.971c-2.971 0-5.486 2.514-5.486 5.6v1.829c0 0.114 0 0.114 0 0.229 0 0.343 0.343 0.686 0.686 0.686s0.686-0.343 0.686-0.686c0-0.114 0-0.229 0-0.229z"></path><path d="M18.286 20v-6.857h-1.257l-2.171 1.714 0.457 1.257 1.486-0.914v4.8z"></path></svg>
                    </div>
                    <!-- 播放列表 -->
                    <div class="ap-button ap-playlist-button" @click="handleList">
                        <div class="ap-playlist-button-bg">
                            <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M5.371 6.857h9.943c0.457 0 0.8 0.343 0.8 0.8v0c0 0.457-0.343 0.8-0.8 0.8h-9.943c-0.457 0-0.8-0.343-0.8-0.8v0c0-0.457 0.343-0.8 0.8-0.8z"></path><path d="M5.371 11.086h7.429c0.457 0 0.914 0.343 0.914 0.8v0c0 0.457-0.343 0.8-0.8 0.8h-7.543c-0.457 0-0.8-0.343-0.8-0.8v0c0-0.457 0.343-0.8 0.8-0.8z"></path><path d="M5.371 15.2h5.829c0.457 0 0.8 0.343 0.8 0.8v0c0 0.457-0.343 0.8-0.8 0.8h-5.829c-0.457 0.114-0.8-0.343-0.8-0.8v0c0-0.343 0.343-0.8 0.8-0.8z"></path><path d="M20.571 6.857v0c0.457 0 0.8 0.343 0.8 0.8v14.057c0 0.457-0.343 0.8-0.8 0.8v0c-0.457 0-0.8-0.343-0.8-0.8v-14.057c0-0.457 0.457-0.8 0.8-0.8z"></path><path d="M20.914 8.457c0 0 0.114 0 0.229 0.114 0.229 0.114 0.571 0.229 0.914 0.343 0.914 0.457 1.943 1.257 2.743 2.286 0.343 0.343 0.686 0.8 0.914 1.257 0.229 0.343 0.8 0.457 1.143 0.229s0.457-0.8 0.229-1.143c-0.343-0.571-0.686-1.029-1.029-1.486-0.914-1.143-2.057-2.057-3.2-2.514-0.686-0.343-1.257-0.571-1.6-0.571-0.457-0.114-0.914 0.229-0.914 0.686-0.114 0.343 0.114 0.8 0.571 0.8z"></path><path d="M17.371 24.686c1.371 0 2.4-1.143 2.4-2.4s-1.143-2.4-2.4-2.4-2.514 1.029-2.514 2.4 1.143 2.4 2.514 2.4zM17.371 26.286c-2.286 0-4.114-1.829-4.114-4s1.829-4 4.114-4 4.114 1.829 4.114 4-1.829 4-4.114 4z"></path></svg>
                            <span class="ap-playlist-button-amount">{{songList.length}}</span>
                        </div>
                    </div>
                </div>
            </div>
            <!-- 提示区 -->
            <div class="ap-toast" :class="{'ap-toast-hide': error.length<=0}">{{error}}</div>
        </div>
    </div>
</div>
<script>
    /*
    * 参考资料:
    * https://www.jianshu.com/p/1fe701c9179f
    * */
    let audio = '';
    var vm = new Vue({
        el: '#app',
        data() {
            return {
                playing: false, // 播放状态
                index: 0, // 当前播放歌曲在列表中的下标
                id: 2, // 当前播放歌曲的id(此处貌似没有用到)
                currentTime: '00:00', // 当前播放时间
                totalTime: '00:00', // 总播放时间
                bufferedScaleX: 0, // 缓存进度
                progressScaleX: 0, // 播放进度
                thumbTranslateX: 0, // 进度条滑块位置
                volume: 1, // 音量进度
                volumeX: 0, // 音量滑块位置
                thumbSlide: false, // 进度条滑块滑动时标记
                thumbVSlide: false, // 音量滑块滑动时标记
                error: '', // 报错内容
                playType: 1, // 播放类型:1-列表循环,2-随机播放,3-单曲循环
                listShow: false, // 播放列表是否展示

                song: { // 当前播放歌曲信息
                    cover: '',
                    name: '',
                    artist: '',
                    time: '',
                    url: '',
                },

                songList: [ // 歌曲列表
                    {
                        id: 1,
                        name: '刚烈女子',
                        time: '04:31',
                        artist: '锦零',
                        album: '?',
                        cover: 'http://img.ytmp3.cn/image/32.jpg',
                        url: 'http://www.ytmp3.cn/down/58151.mp3',
                    },{
                        id: 2,
                        name: '绿色',
                        time: '04:22',
                        artist: '陈凝雪',
                        album: 'lvse',
                        cover: 'http://p2.music.126.net/R4ZP3AJ9xV0vvw8LX7AbMA==/109951163860425334.jpg',
                        url: 'http://www.ytmp3.cn/down/60380.mp3',
                    },{
                        id: 3,
                        name: '青衣',
                        time: '04:22',
                        artist: '马天宇',
                        album: '(*^_^*)',
                        cover: 'http://img.ytmp3.cn/image/5.jpg',
                        url: 'http://www.ytmp3.cn/down/43680.mp3',
                    },{
                        id: 4,
                        name: '风筝误',
                        time: '04:31',
                        artist: '刘珂矣',
                        album: '?',
                        cover: 'http://img.ytmp3.cn/image/52.jpg',
                        url: 'http://www.ytmp3.cn/down/46644.mp3',
                    },{
                        id: 5,
                        name: '红昭愿(Cover 音阙诗听)',
                        time: '04:31',
                        artist: '锦零',
                        album: '红昭愿',
                        cover: 'http://img.ytmp3.cn/image/18.jpg',
                        url: 'http://www.ytmp3.cn/down/46633.mp3',
                    },{
                        id: 6,
                        name: '故城',
                        time: '04:31',
                        artist: '银临',
                        album: '世俗',
                        cover: 'http://img.ytmp3.cn/image/39.jpg',
                        url: 'http://www.ytmp3.cn/down/46641.mp3',
                    },{
                        id: 7,
                        name: '白山茶',
                        time: '04:31',
                        artist: '陈雪凝',
                        album: '?',
                        cover: 'http://img.ytmp3.cn/image/5.jpg',
                        url: 'http://www.ytmp3.cn/down/44355.mp3',
                    }
                ],

            }
        },
        mounted() {
            // audio的DOM结构获取需要在mounted中,在created中话会变成 <audio :src="song.url" id="audio" controls autoplay></audio>
            audio = document.getElementById('audio');
            this.getData();
            this.setPageListen();
        },
        methods: {
            // 获取数据,并放入当前播放列表
            getData() {
                this.song = this.songList[0];
                // 初始化音频的监听事件
                this.audioInit();
                // 初始化进度条与音量的鼠标滑动事件
                this.slideProgress();
                this.slideVolume();
            },

            // 暂停与播放
            play() {
                if (this.playing) {// 播放中,点击则为暂停
                    this.playing = false;
                    audio.pause();
                } else { // 暂停中,点击则为播放
                    this.playing = true;
                    audio.play();
                }
            },

            // 音频事件初始化
            audioInit() {
                let _this = this;
                let progressL = this.$refs.track.offsetWidth; // 进度条总长

                // 事件如果不需要可以不写

                // 音频或视频文件已经就绪可以开始,在点击播放时触发
                audio.addEventListener('play', () => {
                    console.log('play');
                });

                // 浏览器开始寻找指定的音频或视频
                audio.addEventListener('loadstart', () => {
                    console.log('loadstart');

                    // 当前音量进度
                    _this.volume = audio.volume;
                    _this.volumeX = _this.volume * _this.$refs.trackV.offsetWidth;
                });

                // 播放位置改变时触发[注意:播放和调整指示定位时都会触发](主要事件)
                audio.addEventListener('timeupdate', () => {
                    // 当前播放时间
                    _this.currentTime = _this.timeToString(audio.currentTime);

                    // 总播放时间
                    _this.totalTime = _this.timeToString(audio.duration);

                    // 当前播放进度百分比
                    let precent = audio.currentTime / audio.duration  || 0;

                    // 当前播放进度
                    _this.progressScaleX = precent.toFixed(3);

                    // 当前缓存进度
                    // 已缓存时间
                    let buffered = audio.buffered.length ? audio.buffered.end(audio.buffered.length-1) : 0;
                    _this.bufferedScaleX = (buffered / audio.duration).toFixed(3);

                    // 当前进度按钮位置
                    _this.thumbTranslateX = (precent * progressL).toFixed(3);

                });

                // 音频或视频能够不停顿地一直播放
                audio.addEventListener('canplaythrough', () => {
                    console.log('canplaythrough');
                });

                // 音频或视频的时长已改变
                audio.addEventListener('durationchange', () => {
                    console.log('durationchange');
                    _this.totalTime = _this.timeToString(audio.duration)
                });

                // 在音频或视频终止加载时触发,包括终止当前播放(未加载完)进行下一首播放时也会触发
                audio.addEventListener('abort', () => {
                    console.log('abort')
                });

                // 在音频或视频加载发生错误时触发
                audio.addEventListener('error', () => {
                    console.log('error');
                    console.log('-----networkState---------',audio.networkState);
                    console.log('-----readyState---------',audio.readyState);
                    switch (audio.networkState) {
                        case '0':
                            _this.error = '尚未初始化';
                            break;
                        case '1':
                            _this.error = '正在下载数据';
                            break;
                        case '3':
                            _this.error = '未找到资源';
                            break;
                    }
                    audio.readyState == '0' && (_this.error = '音频地址错误');

                    setTimeout(() => {
                        _this.error = '';
                    },3000)
                });

                // 播放结束
                audio.addEventListener("ended", ()=> {
                    console.log("ended");
                    switch (_this.playType) {
                        case 1: // 列表循环
                            _this.index = _this.index+1 >= _this.songList.length ? 0 : _this.index+1;
                            break;
                        case 2: // 随机播放
                            _this.index = Math.floor(Math.random()*_this.songList.length);
                            break;
                        case 3: // 单曲循环
                            break;
                    }
                    _this.song = _this.songList[_this.index];
                    _this.thumbSlide = true;
                    // 此处需要一定的延迟让audio的dom结构获取到,额,你们自己意会或者可以试着去掉看bug
                    setTimeout(() => {
                        audio.play();
                    },100);
                    // 解决因为transition的回弹bug
                    setTimeout(() => {
                        _this.thumbSlide = false;
                    }, 1000)
                }, true);
            },

            // 滑动进度条
            slideProgress() {
                // 由于滑动事件有相同代码,所以进行了简单的封装,具体原理在注释代码中。
                this.slideFn('progress', this.$refs.thumb, this.$refs.track.offsetWidth);
                /*let thumb = this.$refs.thumb;
                let _this = this;

                thumb.onmousedown = function (e) {
                    // 移动时暂停播放并设置transition: none,解决在滑动结束后出现回弹的bug
                    audio.pause();
                    _this.thumbSlide = true;

                    let progressL = _this.$refs.track.offsetWidth;
                    let mouseInitX = e.clientX,
                        mouseEndX = 0,
                        moveX = 0,
                        thumbInitX = _this.stringToNum(thumb.style.transform),
                        thumbEndX = 0;

                    document.onmousemove = function (e) {
                        mouseEndX = e.clientX;
                        moveX = mouseEndX - mouseInitX;

                        let a = thumbInitX - 0 + moveX;
                        if (moveX > 0) {
                            thumbEndX = a > progressL ? progressL : a;
                        } else {
                            thumbEndX = a <= 0 ? 0 : a;
                        }

                        _this.progressScaleX = thumbEndX / progressL;
                        _this.thumbTranslateX = thumbEndX;
                    };

                    document.onmouseup = function (e) {
                        audio.currentTime = thumbEndX * audio.duration / progressL;
                        audio.play();
                        _this.thumbSlide = false;
                        document.onmousedown = null;
                        document.onmousemove = null;
                        document.onmouseup = null;
                    }
                }*/
            },

            // 滑动音量
            slideVolume() {
                // 由于滑动事件有相同代码,所以进行了简单的封装,具体原理在注释代码中。
                this.slideFn('volume', this.$refs.thumbV, this.$refs.trackV.offsetWidth);
                /*let thumb = this.$refs.thumbV;
                let _this = this;

                thumb.onmousedown = function (e) {
                    // 移动时暂停播放并设置transition: none,解决在滑动结束后出现回弹的bug
                    _this.thumbVSlide = true;

                    let progressL = _this.$refs.trackV.offsetWidth;
                    let mouseInitX = e.clientX,
                        mouseEndX = 0,
                        moveX = 0,
                        thumbInitX = _this.stringToNum(thumb.style.transform),
                        thumbEndX = 0;

                    document.onmousemove = function (e) {
                        mouseEndX = e.clientX;
                        moveX = mouseEndX - mouseInitX;

                        let a = thumbInitX - 0 + moveX;
                        if (moveX > 0) {
                            thumbEndX = a > progressL ? progressL : a;
                        } else {
                            thumbEndX = a <= 0 ? 0 : a;
                        }

                        _this.volume = thumbEndX / progressL;
                        _this.volumeX = thumbEndX;
                    };

                    document.onmouseup = function (e) {
                        audio.volume = thumbEndX / progressL;
                        _this.thumbVSlide = false;
                        document.onmousedown = null;
                        document.onmousemove = null;
                        document.onmouseup = null;
                    }
                }*/
            },

            // 上一首
            skipBack() {
                this.skipFn('skipBack');
            },

            // 下一首
            skipForward() {
                this.skipFn('skipForward');
            },

            // 选择播放方式
            chosePlayType() {
                this.playType = this.playType+1 > 3 ? 1 : this.playType+1;
            },

            // 解决页面切换到后台或者切换到其他页面一段时间后,切回时出现进度条回弹的bug
            // 想看此bug的可以不调用该方法,并在播放之后切换到其他页面一段时间后切回,会发现进度条出现回弹的bug,因为transition的问题
            setPageListen() {
                let _this = this;
                // js判断浏览当前页(关键词:当前,浏览)
                // 可以通过document.hidden属性判断当前页面是否是激活状态。
                // 兼容性:IE10+,Firefox10+,Chrome14+,Opera12.1+,Safari7.1+
                var hiddenProperty = 'hidden' in document ? 'hidden' :
                    'webkitHidden' in document ? 'webkitHidden' :
                        'mozHidden' in document ? 'mozHidden' :
                            null;
                var visibilityChangeEvent = hiddenProperty.replace(/hidden/i, 'visibilitychange');
                var onVisibilityChange = function(){
                    if (!document[hiddenProperty]) {
                        document.title='被发现啦(*´∇`*)';
                        _this.thumbSlide = true;
                        setTimeout(() => {
                            _this.thumbSlide = false;
                        },300)
                    }else{
                        document.title='藏好啦(つд⊂)  ';
                    }
                };
                document.addEventListener(visibilityChangeEvent, onVisibilityChange);
            },

            // 是否显示播放列表
            handleList() {
                this.listShow = !this.listShow;
            },

            // 列表选歌
            choseSong(li, i) {
                this.song = li;
                this.index = i;
                this.playing = true;
                setTimeout(() => {
                    audio.play();
                },100)
            },

            //上下首封装
            skipFn(type) {
                switch (this.playType) {
                    case 2: // 随机播放
                        this.index = Math.floor(Math.random()*this.songList.length);
                        break;
                    default:
                        if (type == 'skipBack') {
                            this.index-1>= 0? this.index -- : 0;
                        } else {
                            this.index = this.index+1>= this.songList.length? this.songList.length-1: this.index+1;
                        }
                        break;
                }

                this.song = this.songList[this.index];
                this.playing = true;
                setTimeout(() => {
                    this.totalTime = '00:00';
                    audio.play();
                },100)
            },

            // 滑动封装
            slideFn(type, thumbDom, progressLength) {
                let thumb = thumbDom;
                let _this = this;

                thumb.onmousedown = function (e) {
                    // 移动时暂停播放并设置transition: none,解决在滑动结束后出现回弹的bug
                    if (type == 'progress') {
                        audio.pause();
                        _this.thumbSlide = true;
                    } else {
                        _this.thumbVSlide = true;
                    }


                    let progressL = progressLength;
                    let mouseInitX = e.clientX,
                        mouseEndX = 0,
                        moveX = 0,
                        thumbInitX = _this.stringToNum(thumb.style.transform),
                        thumbEndX = 0;

                    document.onmousemove = function (e) {
                        mouseEndX = e.clientX;
                        moveX = mouseEndX - mouseInitX;

                        let a = thumbInitX - 0 + moveX;
                        if (moveX > 0) {
                            thumbEndX = a > progressL ? progressL : a;
                        } else {
                            thumbEndX = a <= 0 ? 0 : a;
                        }

                        if (type == 'progress') {
                            _this.progressScaleX = thumbEndX / progressL;
                            _this.thumbTranslateX = thumbEndX;
                        } else {
                            _this.volume = thumbEndX / progressL;
                            _this.volumeX = thumbEndX;
                        }

                    };

                    document.onmouseup = function (e) {
                        if (type == 'progress') {
                            audio.currentTime = thumbEndX * audio.duration / progressL;
                            audio.play();
                            _this.thumbSlide = false;
                        } else {
                            audio.volume = thumbEndX / progressL;
                            _this.thumbVSlide = false;
                        }

                        document.onmousedown = null;
                        document.onmousemove = null;
                        document.onmouseup = null;
                    }
                }
            },

            // 字符串转数字
            stringToNum(str) {
                return Number(str.replace(/translateX\(|px\)/g , ''))
            },
            // 秒值转字符串
            timeToString(param){
                param = parseInt(param);
                let hh = '',mm = '',ss = '';
                if (param>=0 && param<60) {
                    param<10? ss = '0'+ param :ss = param;
                    return '00:'+ ss;
                } else if (param>=60 && param<3600) {
                    mm = parseInt(param/60);
                    mm<10?mm = '0'+mm :mm;
                    (param-parseInt(mm*60))<10?ss='0'+ String(param-parseInt(mm*60)) : ss =  param-parseInt(mm*60);
                    return mm + ':' +ss;
                }

            },
        }
    });


</script>
</body>
</html>



评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值