vue使用Howler实现音乐播放器

news2024/12/23 12:32:31

vue使用Howler实现音乐播放器

  • 前言
  • 一、引入依赖
  • 二、封装组件


前言

本文使用Howler.js进行播放。使用siriwave做的播放动画具体文档地址如下

名称地址
Howlerhttps://howlerjs.com/
siriwavehttps://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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/456207.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

教你如何根据需求编写测试用例,不用写一行代码,使用ChatGPT4自动完成。

首先来张效果图&#xff0c;需求我是放到requirements.txt文档里&#xff0c;输出的测试用例是放到test_case1.txt&#xff0c;整个代码我是让ChatGPT4自动给我写的。 我用的prompt提示语是&#xff1a; 我的想法是这样&#xff0c;通过Python代码&#xff0c;和API keys来实现…

传统制造企业在引入项目管理机制时项目组织结构的重要性

在传统的工业设备制造行业,针对以订单项目为驱动的业务模式,建立一套成熟完备的项目管理机制十分重要,同时也是企业提升自身管理水平精细度的内在要求。项目管理作为外企普遍应用的成熟管理模式,如何将其引入并与民企现存的传统职能型管理模式融合,实现成功嫁接,值得大家思考并…

AD9208之8通道高速采集

板卡概述 FMC168 是 一 款 基 于 VITA57.4 标 准 的2GSPS/2.6GSPS/3GSPS 采样率 14 位分辨率 Double FMC子卡模 块&#xff0c;该模块可以实现 8 路 14-bit、2GSPS/2.6GSPS/3GSPS 采样率模 拟信号采集。该板卡 ADC 器件采用 ADI 公司的 AD9208 芯片,该芯片 与 AD9689 完全…

快商通AI技术再获殊荣,荣膺厦门市“科学技术进步奖”

近日&#xff0c;快商通AI科研项目荣获厦门市“科学技术进步奖”&#xff0c;这是对快商通AI技术研究成果的高度肯定&#xff0c;也是快商通在人工智能领域的又一重大突破。 快商通作为一家技术领先的企业&#xff0c;始终坚持 核心技术自主研发 &#xff0c;致力于将自然语言…

【Linux命令行与shell脚本编程】 一,Shell简介

Linux命令行与shell脚本编程 第一章 Shell简介 目录 Linux命令行与shell脚本编程一,Shell简介1.1 终端(终端仿真器) 41.2 shell 提示符1.2.1 命令历史记录1.2.2 光标移动1.2.3 与 bash手册交互 命令的构成 一,Shell简介 1.1 终端(终端仿真器) 4 让用户访问 shell 使用图形用…

国产操作系统新机遇——小程序容器

信息技术应用创新不仅是各行各业实现数字转型的关键起点&#xff0c;而且还是我国加强网络安全和信息安全的重要手段。 现阶段&#xff0c;微软&#xff0c;谷歌和苹果等外国公司在操作系统市场上占据着几乎垄断的行业地位。国内操作系统行业正在努力改变过去过于分散的状态&a…

从FMCW毫米波雷达系统的性能参数理解4D成像毫米波雷达的设计思路

本文编辑&#xff1a;调皮哥的小助理 站在设计雷达的角度看&#xff0c;其实无论是传统的3D毫米波雷达&#xff0c;还是如今的4D毫米波成像雷达&#xff0c;其雷达系统性能参数都遵循一个原则&#xff0c;即&#xff1a; d res ⋅ v res ⋅ θ res d max ⁡ ⋅ v max ⁡ ⋅ …

全志v851s GPIO 应用程序编写

1. 查看硬件电路图SCH_Schematic1_2022-11-23 &#xff0c;查找合适的gpio 作为使用pin 在这里我们选取 GPIOH14&#xff08;注意目前开发使用这个pin 作为触摸屏的pin脚&#xff0c;需要将触摸屏connect断开&#xff09; &#xff0c;因为 可以通过排插使用杜邦线将其引出&am…

scala特质trait

目录 说明案例动态混入 说明 Scala 语言中&#xff0c;采用特质 trait&#xff08;特征&#xff09;来代替接口的概念&#xff0c;也就是说&#xff0c;多个类具有相同的特质&#xff08;特征&#xff09;时&#xff0c;就可以将这个特质&#xff08;特征&#xff09;独立出来…

rabbitMQ学习总结

RabbitMQ 生产者通过-》通道-》交换机-》投到消息队列-》再通过通道-》消费者 分布式架构 何谓分布式系统 通俗一点: 就是一个请求由服务器端的 多个服务 (服务或者系统)协同处理完成 和单体架构不同的是&#xff0c;单体架构是一个请求发起ivm调度线程(确切的是tomcat线程池)…

yolov5-fastapi-demo更换中文标签

本章是基于yolov5-fastapi-demo项目的更改 WelkinU/yolov5-fastapi-demo: FastAPI Wrapper of YOLOv5 (github.com) 首先&#xff0c;因为训练的时候设置的标签是英文&#xff0c;换成中文要重新训练&#xff0c;而且使用中文训练也很繁琐要改很多东西&#xff0c;因此可以直…

防雷接地国家规范标准介绍与施工技术要点

防雷接地是一种防止雷电对建筑物、设备和人身安全造成危害的措施。在防雷接地系统中&#xff0c;将建筑物、设备、金属构件等导体与地面形成良好的导电连接&#xff0c;以便将雷电通过接地体排放到地下&#xff0c;从而保护建筑物、设备和人身安全不受雷击的影响。防雷接地系统…

mybatis粗心使用导致内存溢出

现象 服务响应变慢&#xff0c;线程日志也出现Java heap space内存溢出的错误&#xff0c;这个服务属于基础业务服务&#xff0c;出现问题要尽快的排查 分析 因为设置了gc日志和jmap启动相关参数 所以我们进行分析&#xff0c;这里模拟线上环境将堆大小参数调整到了128m&am…

Windows逆向安全(一)之基础知识(十七)

指针四 指针数组 什么是指针数组 首先回顾一下先前关于数组的知识&#xff1a; 所谓数组就是用于存储相同数据类型的集合 再结合先前关于指针的知识&#xff1a;指针的本质也是一种数据类型 于是当数组中存储的成员的数据类型为指针时&#xff0c;该数组就可以称为指针数…

2023年的深度学习入门指南(7) - SIMD和通用GPU编程

2023年的深度学习入门指南(7) - SIMD和通用GPU编程 深度学习从一开始就跟GPU有不解之缘&#xff0c;因为算力是深度学习不可或缺的一部分。 时至今日&#xff0c;虽然多任务编程早已经深入人心&#xff0c;但是很多同学还没有接触过CPU上的SIMD指令&#xff0c;更不用说GPGPU…

成为黑客猎手:从零开始学习漏洞挖掘的完整指南

一.了解基础知识 学习计算机网络、操作系统、编程语言等相关基础知识&#xff0c;这些知识对于后续的漏洞挖掘和利用非常重要。具体建议如下&#xff1a; 学习计算机网络基础知识&#xff0c;例如 OSI 模型、TCP/IP 协议、HTTP 协议等。推荐书籍&#xff1a;《计算机网络》。…

qt 动态库/静态库的创建和使用教程(step by step)

一般大型项目中, 会将实现特定功能的函数或类, 封装成链接库, 供应用程序代码调用. 下面我将一步步教你如何在qt 中创建动态库/静态库, 并使用它. 目录 创建多子目录项目创建动态链接库编辑链接库内容创建应用工程并连接动态链接库 创建多子目录项目 首先创建一个多子目录项目…

PHP+python+nodejs+ springboot+vue 社区互助平台

项目介绍 社区互助平台的功能分为管理员和用户两个部分&#xff0c;系统的主要功能包括首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;租房信息管理&#xff0c;失物招领管理&#xff0c;宠物代遛管理&#xff0c;停车位出租管理&#xff0c;其他管理&#xff0c;趣…

哈希表(底层结构剖析--下)

文章目录 开散列哈希桶的模拟实现哈希桶的基本框架增加仿函数将数据类型转换为整型哈希桶的插入函数哈希桶的删除函数哈希桶的查找函数哈希桶的析构函数建议哈希表的大小为素数 开散列与闭散列比较哈希桶的时间复杂度及其测试开散列哈希桶的模拟实现完整代码 开散列哈希桶的模拟…

Java -- ELK之从nacos获取logback.xml配置信息

背景&#xff1a; 之前本地搭建好ELK后&#xff0c;随便起一个项目&#xff0c;可正常日志上送&#xff0c;但是后面我把elk部署到测试环境中发现&#xff0c;本地项目的日志就无法正常上送了&#xff0c;之前我是把上送地址配置到nacos上的&#xff0c;logback.xml中读取nacos…