vue使用Howler实现音乐播放器

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>

猜你喜欢

转载自blog.csdn.net/qq_43548590/article/details/130282782