vue使用Howler实现音乐播放器
- 前言
- 一、引入依赖
- 二、封装组件
前言
本文使用Howler.js进行播放。使用siriwave做的播放动画具体文档地址如下
名称 | 地址 |
---|---|
Howler | https://howlerjs.com/ |
siriwave | https://github.com/kopiro/siriwave |
最后实现效果如下:
实现暂停、开始、快进、后退、拖拽进度
一、引入依赖
npm install howler
npm install siriwave
二、封装组件
播放器index.vue:
<template>
<div class="howler-audio">
<div class="play-top">幼稚园杀手-红色</div>
<div id="siri-classic"></div>
<div class="play-bottom">
<div style="width:100%;display: flex;justify-content: space-between;color: #fff">
<div>{{getTime(seek)}}</div>
<div>{{getTime(audioDuration)}}</div>
</div>
<slisd :min="0" :max="100" :value="audioSeek" :isDrag="true" bgColor="#4ab157" @handleClickSlider="handleClickSlider"></slisd>
<div class="play-btns">
<svg @click="handleBackUp" t="1681985414476" class="icon" viewBox="0 0 1260 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2345" width="200" height="200"><path d="M1181.831855 12.560526l-536.645044 462.060479a50.329422 50.329422 0 0 0 0 72.76543l536.645044 463.879615a46.084772 46.084772 0 0 0 77.616459-36.382715V48.943241a46.084772 46.084772 0 0 0-77.616459-36.382715zM552.410888 12.560526L15.765843 474.621005A49.723044 49.723044 0 0 0 15.765843 546.173678l536.038666 465.092372a46.084772 46.084772 0 0 0 77.616459-36.382715V48.943241A46.084772 46.084772 0 0 0 552.410888 12.560526z" fill="#2c2c2c" p-id="2346"></path></svg>
<svg v-if="isPlay==false" @click="handleStop" t="1681985495172" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8658" width="200" height="200"><path d="M128 138.666667c0-47.232 33.322667-66.666667 74.176-43.562667l663.146667 374.954667c40.96 23.168 40.853333 60.8 0 83.882666L202.176 928.896C161.216 952.064 128 932.565333 128 885.333333v-746.666666z" fill="#2c2c2c" p-id="8659"></path></svg>
<svg v-else @click="handleStop" t="1681985509006" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4793" width="200" height="200"><path d="M128 106.858667C128 94.976 137.621333 85.333333 149.12 85.333333h85.76c11.648 0 21.12 9.6 21.12 21.525334V917.12c0 11.882667-9.621333 21.525333-21.12 21.525333H149.12A21.290667 21.290667 0 0 1 128 917.141333V106.88z m640 0c0-11.882667 9.621333-21.525333 21.12-21.525334h85.76c11.648 0 21.12 9.6 21.12 21.525334V917.12c0 11.882667-9.621333 21.525333-21.12 21.525333h-85.76a21.290667 21.290667 0 0 1-21.12-21.525333V106.88z" fill="#2c2c2c" p-id="4794"></path></svg>
<svg @click="handleAdvance" t="1681985467601" class="icon" viewBox="0 0 1260 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2518" width="200" height="200"><path d="M77.738464 1011.260664l536.872038-462.255925a50.350711 50.350711 0 0 0 0-72.796208L77.738464 12.739336A46.104265 46.104265 0 0 0 0.089175 48.530806v926.331753a46.104265 46.104265 0 0 0 77.649289 36.398105z m629.687204 0l536.872038-462.255925a49.744076 49.744076 0 0 0 0-72.796208L707.425668 12.739336a46.104265 46.104265 0 0 0-77.649289 36.398105v925.725118a46.104265 46.104265 0 0 0 77.649289 36.398105z" fill="#2c2c2c" p-id="2519"></path></svg>
</div>
</div>
</div>
</template>
<script>
import slisd from "./slisd.vue";
import SiriWave from "siriwave";
export default {
name: "index",
components:{slisd},
data(){
return{
classic:undefined,
isPlay:false,
sound:undefined,
audioDuration:0,
audioSeek:0,
seek:0
}
},
mounted() {
this.classic = new SiriWave({
container: document.getElementById("siri-classic"),
height: 150,
autostart:false,
});
let that=this
this.sound = new Howl({
src: ['https://koalaclass-website.oss-ap-southeast-2.aliyuncs.com/null/%E5%B9%BC%E7%A8%9A%E5%9B%AD%E6%9D%80%E6%89%8B%20-%20%E7%BA%A2%E8%89%B2.mp3'],
onplay: function() {
console.log("onplay")
that.classic.start()
},
onload: function() {
console.log("onload")
},
onend: function() {
console.log("onend")
that.classic.stop()
},
onpause: function() {
console.log("onpause")
that.classic.stop()
},
onstop: function() {
console.log("onstop")
},
onseek: function() {
console.log("onseek")
}
});
this.loadSeek()
},
methods:{
handleStop(){
this.isPlay=!this.isPlay
if(this.isPlay){
this.sound.play()
}else{
this.sound.pause();
}
},
loadSeek(){
return setInterval(e=>{
let seek=parseInt(this.sound.seek()/this.sound._duration*100)
this.audioSeek=seek?seek:0
this.seek=this.sound.seek()
this.audioDuration=this.sound._duration
},500)
},
handleClickSlider(e){
this.sound.seek(this.sound._duration*(e/100))
},
handleBackUp(){
this.sound.seek(this.sound.seek()-10)
},
handleAdvance(){
this.sound.seek(this.sound.seek()+10)
},
getTime(t){
let m = parseInt(t / 60 % 60)
let s = parseInt(t % 60)
m = m < 10 ? '0' + m : m
s = s < 10 ? '0' + s : s
return `${m}:${s}`
}
}
}
</script>
<style lang="less" scoped>
.howler-audio{
height: 100%;
width: 100%;
background: linear-gradient(135deg, #bb71f3 0%, #3d4d91 100%);
display: flex;
justify-content: space-between;
flex-direction: column;
}
.play-top{
text-align: center;
margin-top: 10px;
color: #ffffff;
}
.play-bottom{
display: flex;
justify-content: center;
width: 100%;
margin-bottom: 10px;
flex-direction: column;
align-items: center;
.play-btns{
display: flex;
justify-content: space-between;
width: 50%;
.el-icon{
font-size: 25px;
}
}
}
svg{
width: 50px;
height: 50px;
}
</style>
进度条:slisd.vue
<template>
<div class="slider" ref="slider" @click.stop="handelClickSlider">
<div class="process" :style="{ width,background:bgColor }"></div>
<div class="thunk" ref="trunk" :style="{ left }">
<div class="block" ref="dot"></div>
</div>
</div>
</template>
<script>
/*
* min 进度条最小值
* max 进度条最大值
* v-model 对当前值进行双向绑定实时显示拖拽进度
* */
export default {
props: {
// 最小值
min: {
type: Number,
default: 0,
},
// 最大值
max: {
type: Number,
default: 100,
},
// 当前值
value: {
type: Number,
default: 0,
},
// 进度条颜色
bgColor: {
type: String,
default: "#4ab157",
},
// 是否可拖拽
isDrag: {
type: Boolean,
default: true,
},
},
data() {
return {
slider: null, //滚动条DOM元素
thunk: null, //拖拽DOM元素
per: this.value, //当前值
};
},
mounted() {
this.slider = this.$refs.slider;
this.thunk = this.$refs.trunk;
var _this = this;
if (!this.isDrag) return;
this.thunk.onmousedown = function (e) {
var width = parseInt(_this.width);
var disX = e.clientX;
document.onmousemove = function (e) {
// value, left, width
// 当value变化的时候,会通过计算属性修改left,width
// 拖拽的时候获取的新width
var newWidth = e.clientX - disX + width;
// 计算百分比
var scale = newWidth / _this.slider.offsetWidth;
_this.per = Math.ceil((_this.max - _this.min) * scale + _this.min); //取整
// 限制值大小
_this.per = Math.max(_this.per, _this.min);
_this.per = Math.min(_this.per, _this.max);
_this.$emit("input", _this.per);
};
document.onmouseup = function () {
//当拖拽停止发送事件
_this.$emit("stop", _this.per);
//清除拖拽事件
document.onmousemove = document.onmouseup = null;
};
};
},
methods: {
handelClickSlider(event) {
//禁止点击
if (!this.isDrag) return;
const dot = this.$refs.dot;
if (event.target == dot) return;
//获取元素的宽度l
let width = this.slider.offsetWidth;
//获取元素的左边距
let ev = event || window.event;
//获取当前点击位置的百分比
let scale = ((ev.offsetX / width) * 100).toFixed(2);
this.per = scale;
this.$emit("handleClickSlider",scale)
},
},
computed: {
// 设置一个百分比,提供计算slider进度宽度和trunk的left值
// 对应公式为 当前值-最小值/最大值-最小值 = slider进度width / slider总width
// trunk left = slider进度width + trunk宽度/2
scale() {
return (this.per - this.min) / (this.max - this.min);
},
width() {
return this.slider ? this.slider.offsetWidth * this.scale + "px" : "0px";
},
left() {
return this.slider ? this.slider.offsetWidth * this.scale - this.thunk.offsetWidth / 2 +"px" : "0px";
},
},
watch: {
value: {
handler: function () {
this.per = this.value;
},
},
},
};
</script>
<style scoped>
.box {
margin: 100px auto 0;
width: 80%;
}
.clear:after {
content: "";
display: block;
clear: both;
}
.slider {
position: relative;
margin: 20px 0;
width: 100%;
height: 10px;
background: #f8f8f8;
border-radius: 5px;
cursor: pointer;
z-index: 99999;
}
.slider .process {
position: absolute;
left: 0;
top: 0;
width: 112px;
height: 10px;
border-radius: 5px;
background: #4ab157;
z-index: 111;
}
.slider .thunk {
position: absolute;
left: 100px;
top: -4px;
width: 10px;
height: 6px;
z-index: 122;
}
.slider .block {
width: 16px;
height: 16px;
border-radius: 50%;
background: rgba(255, 255, 255, 1);
transition: 0.2s all;
}
.slider .block:hover {
transform: scale(1.1);
opacity: 0.6;
}
</style>