音乐播放器页面实现

617 阅读3分钟

效果图

image.png

功能点介绍

1.切换歌曲

2.暂停/播放

3.拖拽进度

4.调整音量

5.音乐播放完成后自动切换下一首

使用到组件

element-ui 如果有小伙伴不懂的使用的话,下面简单介绍一下使用,这里以vue为例

npm 安装

推荐使用 npm 的方式安装,它能更好地和 webpack 打包工具配合使用。

npm i element-ui -S

完整引入

在 main.js 中写入以下内容:

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.use(ElementUI);

new Vue({
  el: '#app',
  render: h => h(App)
});

实现代码

我是借助于vue框架封装的一个组件(music.vue)

<template>
    <div class="music">
        <div class="img">
            <img :src="songList[currentIndex] && songList[currentIndex].img" alt="" id="img">
        </div>
        <div class="songname">
            <p>{{ songList[currentIndex] && songList[currentIndex].name }}</p>
        </div>
        <div class="showtime">
            <div class="slider">
                <div class="slidertimer">
                    <div class="timer">
                        <span>
                            {{ currentduration }}
                        </span>
                        <span>
                            {{ songduration }}
                        </span>
                    </div>
                    <el-slider v-model="progress" :show-tooltip="false" @change="changecurrentime"></el-slider>
                </div>
                <div class="sound">
                    <el-slider v-model="volume" vertical :format-tooltip="formatTooltip" @input="changevolume"
                        height="100px" :style="{ 'opacity': opacity }">
                    </el-slider>
                    <i class="el-icon-mic" @click="opacity = -opacity"></i>

                </div>
            </div>
        </div>

        <div class="player">
            <li class="el-icon-caret-left" @click="backto"></li>
            <li class="el-icon-video-pause" v-show="isStop" @click="stop"></li>
            <li class="el-icon-video-play" v-show="!isStop" @click="play"></li>
            <li class="el-icon-caret-right" @click="nextto"></li>
        </div>
    </div>

</template>

<script>
export default {
    name: 'music',
    created() {
        this.songslen = this.songList.length;
        this.initsong()
        this.audio.addEventListener('timeupdate', this.updatetime)
        this.audio.addEventListener('ended', this.nextto)
        this.audio.addEventListener('canplay', this.getalltime)

        this.currentIndex = localStorage.getItem('currentIndex') * 1 || 0;
        this.volume = localStorage.getItem('volume') * 1 || 20;

    },
    data() {
        return {
            songList: [{
                        name:'王心凌-第一次爱的人',
                        img:'https://w.wallhaven.cc/full/k7/wallhaven-k7yww1.jpg',
                        path:'https://link.jscdn.cn/sharepoint/aHR0cHM6Ly8xZHJpdi1teS5zaGFyZXBvaW50LmNvbS86dTovZy9wZXJzb25hbC9zdG9yXzFkcml2X29ubWljcm9zb2Z0X2NvbS9FUkFKVVFEdVVqUkZqWUdEZ2pZMmczY0JOSWdiUmtzVG9yb3FCdE1FTzhkcDdR.mp3'
            },
            {
                        name:'二手玫瑰乐队-沧海一声笑',
                        img:'https://w.wallhaven.cc/full/28/wallhaven-287eg9.jpg',
                        path:'https://link.jscdn.cn/sharepoint/aHR0cHM6Ly8xZHJpdi1teS5zaGFyZXBvaW50LmNvbS86dTovZy9wZXJzb25hbC9zdG9yXzFkcml2X29ubWljcm9zb2Z0X2NvbS9FVDNZRGYwWm9HbENud21KZHF2dXdWc0JWT3VLQ0p5T1ROdXBCajZLaXU4Z0h3.mp3'
            },
            {
                        name:'陶喆 - 寂寞的季节',
                        img:'https://w.wallhaven.cc/full/o3/wallhaven-o3pj1m.jpg',
                        path:'https://link.jscdn.cn/sharepoint/aHR0cHM6Ly8xZHJpdi1teS5zaGFyZXBvaW50LmNvbS86dTovZy9wZXJzb25hbC9zdG9yXzFkcml2X29ubWljcm9zb2Z0X2NvbS9FZF9FdWlVdjlKRkVvTl9qUHJIbTlWUUIyUWc0dGVfT2dBX0RxeVNvVkhXXzNn.mp3'
            }
            ],
            songslen: 0,
            currentIndex: 0,
            isStop: false,
            progress: 0,
            songduration: 0,
            currentduration: '0:00',
            audio: null,
            volume: 20,
            opacity: -1
        }
    },
    methods: {
        formateTime(time) {
            const minute = parseInt(time / 60 % 60)
            const second = Math.ceil(time % 60)
            const formatSecond = second > 59 ? 59 : second
            return `${minute}:${formatSecond < 10 ? '0' + formatSecond : formatSecond}`
        },
        play() {
            this.isStop = true
            this.audio.play();
        },
        stop() {
            this.isStop = false
            this.audio.pause();
        },
        backto() {
            this.currentIndex--;
            if (this.currentIndex === -1) {
                this.currentIndex = this.songslen - 1;
            }
            this.turn()
            this.play()
            this.preserve()
        },
        nextto() {
            this.currentIndex++;
            if (this.currentIndex === this.songslen) {
                this.currentIndex = 0;
            }
            this.turn()
            this.play();
            this.preserve()
        },

        initsong() {
            this.audio = document.createElement("audio")
            this.audio.volume = this.volume / 100
            this.turn()
        },
        turn() {
            this.audio.src = this.songList[this.currentIndex].path
        },
        updatetime() {
            this.currentduration = this.formateTime(this.audio.currentTime)
            this.progress = this.audio.currentTime / this.audio.duration * 100;

        },
        changecurrentime() {
            this.audio.currentTime = this.progress * this.audio.duration / 100
        },
        getalltime() {
            this.songduration = this.formateTime(parseInt(this.audio.duration))
        },

        changevolume() {
            if (this.audio) {
                this.audio.volume = this.volume / 100;
                this.preserve()
            }
        },
        formatTooltip(value) {
            return value + '%'
        },
        preserve() {
            localStorage.setItem('currentIndex', this.currentIndex);
            localStorage.setItem('volume', this.volume)
        }

    },
    beforeDestroy() {
        this.audio.removeEventListener('timeupdate', this.updatetime)
        this.audio.removeEventListener('ended', this.nextto)
        this.audio.removeEventListener('canplay', this.getalltime)
        this.stop()
        this.audio = null;

    }

}
</script>

<style scoped>
.music {
    width:300px;
    margin: 25px;
    padding: 30px;
    border: 3px solid rgb(168, 165, 165);
    border-radius: 10px;
}

.img {
    width: 150px;
    height: 150px;
    margin: auto;
}

#img {
    width: 100%;
    height: 100%;
    border-radius: 50%;
    border: 5px solid rgb(156, 152, 152);
    animation: 20s rotateImg linear infinite;
}

#img:hover,
.songname p:hover {
    animation-play-state: paused;
}

.songname {
    width: 150px;
    margin: auto;
    font-size: 20px;
    font-weight: bold;
    padding: 30px 0 10px 0;
    overflow: hidden;
}

.songname p {
    float: left;
    word-break: keep-all;
    white-space: nowrap;
    animation: 10s wordsLoop linear infinite;
}



.showtime {
    position: relative;
    width: 70%;
    margin: auto;
}

.timer {
    display: flex;
    justify-content: space-between;
}

.player {
    display: flex;
    justify-content: space-around;
    align-items: center;
    margin: 30px;
    border: 3px solid rgb(196, 192, 192);
    box-shadow: 2px 2px rgb(183, 180, 180);
    border-radius: 29px;
}

.player li {
    margin: 5px;
    border-radius: 50%;
    border: 2px solid rgb(88, 87, 87);
    box-shadow: 1px 1px rgb(197, 193, 193);

}

.el-icon-caret-left,
.el-icon-caret-right {
    font-size: 30px;
}

.el-icon-mic {
    font-size: 24px;
    margin: 20px;
}

.el-icon-mic:hover {
    color: rgb(20, 192, 235);
}

.el-icon-video-pause,
.el-icon-video-play {
    font-size: 40px;
}

.sound {
    position: absolute;
    top: -92px;
    right: -50px;
    display: flex;
    flex-direction: column;
    align-items: center;

}


.player li:hover {
    color: rgb(199, 192, 192);
}


@keyframes rotateImg {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(360deg);
    }
}

@keyframes wordsLoop {
    0% {
        transform: translateX(100%);

    }

    100% {
        transform: translateX(-100%);

    }
}
</style>

歌曲数据方面

songList存在三个参数;分别是name(歌曲名),img(歌曲封面图),path(音频资源路径)

我这里是自行添加的,当然这些数据你也可以自己从后端请求过来。

总结

对于音频这一类的前端处理,主要是借助于html5新加的标签audio,相关的属性与使用方法,我这里做个简单的介绍,具体的使用方法请自行查阅资料

Audio
属性描述
audioTracks返回表示可用音频轨道的 AudioTrackList 对象。
autoplay设置或返回是否在就绪(加载完成)后随即播放音频
buffered返回表示音频已缓冲部分的 TimeRanges 对象。
controller返回表示音频当前媒体控制器的 MediaController 对象。
controls设置或返回音频是否应该显示控件(比如播放/暂停等)。
crossOrigin设置或返回音频的 CORS 设置。
currentSrc返回当前音频的 URL。
currentTime设置或返回音频中的当前播放位置(以秒计)。
defaultMuted设置或返回音频默认是否静音。
defaultPlaybackRate设置或返回音频的默认播放速度。
duration返回音频的长度(以秒计)。
ended返回音频的播放是否已结束。
error返回表示音频错误状态的 MediaError 对象。
loop设置或返回音频是否应在结束时再次播放。
mediaGroup设置或返回音频所属媒介组合的名称。
muted设置或返回是否关闭声音。
networkState返回音频的当前网络状态。
paused设置或返回音频是否暂停。
playbackRate设置或返回音频播放的速度。
played返回表示音频已播放部分的 TimeRanges 对象。
preload设置或返回音频的 preload 属性的值。
readyState返回音频当前的就绪状态。
seekable返回表示音频可寻址部分的 TimeRanges 对象。
seeking返回用户当前是否正在音频中进行查找。
src设置或返回音频的 src 属性的值。
textTracks返回表示可用文本轨道的 TextTrackList 对象。
volume设置或返回音频的音量
方法描述
addTextTrack()向音频添加新的文本轨道。
canPlayType()检查浏览器是否能够播放指定的音频类型。
fastSeek()在音频播放器中指定播放时间。
getStartDate()返回新的 Date 对象,表示当前时间线偏移量。
load()重新加载音频元素。
play()开始播放音频。
pause()暂停当前播放的音频。