效果图
功能点介绍
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() | 暂停当前播放的音频。 |