微信小程序 录音+播放组件封装

news2024/11/27 16:37:02

展示

长按录音 松开结束录音 点击播放 再次点击暂停 再次点击继续播放 展示效果:

录音功能 

录音功能(手指按下开始录音 手指松开结束录音):

使用wx原生录音功能在 component 外新建 wx.getRecorderManager()

RecorderManager | 微信开放文档

const vedioControl = wx.getRecorderManager() //录音控制器

 

 在这个位置别找错了

首先我们想到 wx按下和松开事件分别为 bindtouchstart/bindtouchend

bindtouchstart="touchstart" bindtouchend="touchend"

//我们将 touchstart设置
    touchstart: function(e:any) {   
      this.setData({
        touchStartTime:e.timeStamp
        //获取按下的时间戳
      })
      this.setData({
        setTimeFn:setTimeout(()=>{
          vedioControl.start({})
          // 100ms后开始录音
          this.setData({startVedio:true})
        },100)
      })
    },  
   touchend: function(e:any) {    
        //如果touchstart 和 startend 按下时间大与100ms 就进行记录
      if (e.timeStamp - this.data.touchStartTime >100){      
          vedioControl.stop()
          // touch结束停止录音
          // 监听事件 如果停止录音出发事件
          vedioControl.onStop((res) => {
            const ss = Math.floor(res.duration / 1000 % 60)
            const mm = Math.floor(res.duration / 1000 / 60) 
            this.setData({
              vedioTime: `${mm}:${ss>10?ss:'0'+ss}`
            })
            clearTimeout(this.data.setTimeFn)
            this.setData({startVedio:false})
            const vedioList = this.data.vedioList
            // 我们将获取到的暂时的地址存储到vediolist中 duration为录音的时间 
            vedioList.push( {
              fileName: '音频',
              url: res.tempFilePath,
              type:'mp4',
              duration:res.duration
            })
            // 将新的录音添加到vedioList中
            this.setData({
              setTimeFn:null,
              vedioList:vedioList
            })
          })
        }else{
        //如果touchstart 和 startend 按下时间小与100ms 清除掉 settimeout 不尽行任何处理
          clearTimeout(this.data.setTimeFn)
          this.setData({
            setTimeFn:null,
            startVedio:false
          })
        } 
    },

 这样我们就获取到了 录音的地址 和音频 接下来 我们需要播放音频

播放音频功能

使用wx音频播放器 wx.createInnerAudioContext() 同样我们也在组件外新建

InnerAudioContext | 微信开放文档

const innerAudioContext = wx.createInnerAudioContext({
  useWebAudioImplement: false,
})//音频播放器
innerAudioContext.onPause(()=>{
  console.log('暂停了')
})
innerAudioContext.onPlay(()=>{
  console.log('开始播放了')
})

 当我们点击播放按钮的时候执行playVedio函数

    playVedio (){
      innerAudioContext.src =  this.data.vedioList[this.data.vedioList.length-1].url
// 这里的url为上面的tempath
      const vedio = this.data.vedioList[this.data.vedioList.length - 1].duration
      const duration = this.data.vedioList[this.data.vedioList.length - 1].duration /20
//这里我设置为 每5%前进一个小格(小格变成深蓝色)
//设置 isPlayVedio变量如果 当前在播放则为true 否则是false
      if(!this.data.isPlayVedio){
        innerAudioContext.play() 
        this.setData({
          isPlayVedio:true
        })
//这个circle 的作用是每过 5%秒 将 用于记录的vedioProcess+5
//timeVedio作用为记录 已经播放到第几秒了
        const circle = setInterval(()=>{
          this.setData({
            vedioProcess:this.data.vedioProcess+5,
            timeVedio:this.data.timeVedio + duration
          })
          if(vedio<this.data.timeVedio||vedio===this.data.timeVedio){
            //如果 播放的长度已经大于或者等于 音频长度 我们则应该清除 这个播放让她回归于0     并且将播放状态设置为 false
            clearInterval(circle)
            this.setData({
              vedioProcess:0,
              timeVedio:0,
              isPlayVedio:false
            })
          }
// 如果在执行 setInterval的时候 用户点击了暂停 那么isPlayVedio的状态为false 我们就需要让 这个播放器的 process不再前进
          if(!this.data.isPlayVedio){
            clearInterval(circle)
          }
        },duration)
      }else{
//如果当前播放器正在播放 状态 那么用户点击的时候就是希望暂停
        innerAudioContext.pause() 
        this.setData({
          isPlayVedio:false
        })
      }
    },

这里的url为上面的tempath 

这里我设置为 每5%前进一个小格(小格变成深蓝色)

isPlayVedio:用来判断是否在播放

vedioProcess:播放进度 全部为100

timeVedio:当前播放到的时间

circle:每5%的时间进行一次小格渲染

关于展示小格子:

  

        <view class="vedio-file" bindtap="playVedio">
          <image src="{{isPlayVedio?'../images/vedio-start.svg':'../images/vedio-end.svg'}}"></image>
          <view class="item-list">
            <view wx:for="{{20}}" wx:for-item="item" class="item {{vedioProcess>item*5?'active':''}}" wx:key="index"></view>
          </view>
          <view class="time">{{vedioTime}}</view>
        </view>

画一个竖小格子 循环20次 我们这里设置的是100/20 每5%进行一次改色

vedioProces为当前播放的进度 全部为为100

如果小格子的index*5>vedioProces 那么这个小格子就应该变深蓝色 (改变类名)

接下来我们需要设置长短不一 的小格子

使用css的n-th(n)这个特性 以6个为一组 小->大  大->小

      .item{
        height: 12px;
        width: 2px;
        background-color: #3370FF33;
        margin-left: 5px;
      }
      .active{
        background-color: #3370FF;
      }
      .item:nth-child(6n-5),.item:nth-child(6n-1){
        height: 4px;
      }
      .item:nth-child(6n-4), .item:nth-child(6n-2){
        height: 8px;
      }
      .item:nth-child(6n-3){
        height: 14px;
      }

特别注意: 

ios静音格式下 播放器播放没有声音 我们需要在 app.ts全局设置 obeyMuteSwitch:false

  onLaunch() {
    wx.setInnerAudioOption({
      obeyMuteSwitch:false
    })
  },

wx.setInnerAudioOption(Object object) | 微信开放文档 

纯净无备注版组件:

wxml:

<van-overlay show="{{ vedioVisible }}" z-index="99">
  <view class="vedioDialog">
    <view class="close-icon" bindtap="closeVedio">
      <image src="../images/close-icon.svg"></image>
    </view>
    <view class="bigCircle" bindtouchstart="touchstart" bindtouchend="touchend">
      <view class="smallCircle {{startVedio?'onVedio':''}}">
        <text>{{startVedio?'正在录音':'长按录音'}}</text>
      </view>
    </view>
    <view>
      <view class="vedio-player" style="{{vedioTime==='0:00'?'display:none':null}}">
        <view class="vedio-file" bindtap="playVedio">
          <image src="{{isPlayVedio?'../images/vedio-start.svg':'../images/vedio-end.svg'}}"></image>
          <view class="item-list">
            <view wx:for="{{20}}" wx:for-item="item" class="item {{vedioProcess>item*5?'active':''}}" wx:key="index"></view>
          </view>
          <view class="time">{{vedioTime}}</view>
        </view>
        <view class="del-vedio" bindtap="delVedio">
          <image src="../images/bg-close-icon.svg"></image>
        </view>
      </view>
    </view>
  </view>
</van-overlay>

ts

 

// components/multifunctionInput/multifunctionInput.ts
const vedioControl = wx.getRecorderManager() //录音
const innerAudioContext = wx.createInnerAudioContext({
  useWebAudioImplement: false,
})//音频播放器
innerAudioContext.onPause(()=>{
  console.log('暂停了')
})
innerAudioContext.onPlay(()=>{
  console.log('开始播放了')
})
Component({
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {
    vedioVisible:false,
    vedioProcess:0,
    vedioTime:'0:00',
    touchStartTime: 0,
    startVedio: false,
    setTimeFn:null as any,
    vedioList:[{
      url:'https://img.yzcdn.cn/vant/leaf.jpg',
      fileName:'音频.mp4',
      type: 'mp4',
      duration:4000
    }] ,
    isPlayVedio:false,
    timeVedio:0
  },

  /**
   * 组件的方法列表
   */
  methods: {
    openVedio(){
      this.setData({vedioVisible:true})
    },
    closeVedio(){
      this.setData({vedioVisible:false})
    },
    touchstart: function(e:any) {   
      this.setData({
        touchStartTime:e.timeStamp
      })
      this.setData({
        setTimeFn:setTimeout(()=>{
          vedioControl.start({})
          this.setData({startVedio:true})
        },100)
      })
    },  
   //touch end 
   touchend: function(e:any) {    
      if (e.timeStamp - this.data.touchStartTime >100){      
          vedioControl.stop()
          vedioControl.onStop((res) => {
            const ss = Math.floor(res.duration / 1000 % 60)
            const mm = Math.floor(res.duration / 1000 / 60) 
            this.setData({
              vedioTime: `${mm}:${ss>10?ss:'0'+ss}`
            })
            clearTimeout(this.data.setTimeFn)
            this.setData({startVedio:false})
            const vedioList = this.data.vedioList
            vedioList.push( {
              fileName: '音频',
              url: res.tempFilePath,
              type:'mp4',
              duration:res.duration
            })
            this.setData({
              setTimeFn:null,
              vedioList:vedioList
            })
          })
        }else{
          clearTimeout(this.data.setTimeFn)
          this.setData({
            setTimeFn:null,
            startVedio:false
          })
        } 
    },
    playVedio (){
      innerAudioContext.src =  this.data.vedioList[this.data.vedioList.length-1].url
      const vedio = this.data.vedioList[this.data.vedioList.length - 1].duration
      const duration = this.data.vedioList[this.data.vedioList.length - 1].duration /20
      if(!this.data.isPlayVedio){
        innerAudioContext.play() 
        this.setData({
          isPlayVedio:true
        })
        const circle = setInterval(()=>{
          this.setData({
            vedioProcess:this.data.vedioProcess+5,
            timeVedio:this.data.timeVedio + duration
          })
          if(vedio<this.data.timeVedio){
            clearInterval(circle)
            this.setData({
              vedioProcess:0,
              timeVedio:0,
              isPlayVedio:false
            })
          }
          if(!this.data.isPlayVedio){
            clearInterval(circle)
          }
        },duration)
      }else{
        innerAudioContext.pause() 
        this.setData({
          isPlayVedio:false
        })
      }
      // innerAudioContext.pause() // 暂停
      // innerAudioContext.stop() // 停止
    },
    delVedio (){
      this.setData({
        vedioTime:'0:00',
        isPlayVedio:false,
        vedioProcess:0,
        timeVedio:0,
      })
    },
    removeVedio (e:any) {
      const index = e.target.dataset.index
      const {vedioList = []} = this.data
      vedioList.splice(index,1)
      this.setData({
        vedioList:vedioList
      })
    },

  }
})

less 

.vedioDialog{
  height: 350px;
  width: 90%;
  background-color: white;
  box-shadow: 0px 0px 4px 6px rgba(91, 202, 151, 0.03);
  border-radius: 7px;
  position: absolute;
  top: calc(50% - 175px);
  right: 5%;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  .close-icon{
    position: absolute;
    top: 20px;
    right: 20px;
    image{
      height: 30rpx;
      width: 30rpx;
    }
  }
  .bigCircle{
    height: 166px;
    width: 166px;
    border-radius: 100%;
    background-color: #3370FF2E;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    .smallCircle{
      width: 100px;
      height: 100px;
      background-color: #3370FF;
      border-radius: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
    }
  }
  .onVedio{
    background-color: #04D7B9 !important; 
  }
  .vedio-player{
    margin-top: 20px;
    display: flex;
    justify-content: center;
    align-items: center;
    .vedio-file{
      width: 250px;
      height: 44px;
      background: #F5F5F5;
      border-radius: 34.5px;
      display: flex;
      align-items: center;
      padding:0 10px;
      box-sizing: border-box;
      justify-content:space-evenly;
      image{
        height: 33px;
        width: 33px;
      }
      .item-list{
        width: 150px;
        height: 15px;
        display: flex;
        align-items: center;
      }
      .time{
        color: #0D296E;
        font-size: 12px;
      }
      .item{
        height: 12px;
        width: 2px;
        background-color: #3370FF33;
        margin-left: 5px;
      }
      .active{
        background-color: #3370FF;
      }
      .item:nth-child(6n-5),.item:nth-child(6n-1){
        height: 4px;
      }
      .item:nth-child(6n-4), .item:nth-child(6n-2){
        height: 8px;
      }
      .item:nth-child(6n-3){
        height: 14px;
      }
  
    }
    .del-vedio{
      margin-left: 15px;
      image{
        height: 25px;
        width: 25px;
      }
    }
  }

}

json 

{
  "component": true,
  "usingComponents": {
    "van-overlay": "@vant/weapp/overlay/index",
  }
}

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

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

相关文章

国巨 :硬件设计基础60条

硬件设计是现代科技发展中至关重要的领域之一。它涵盖了从微电子器件到复杂的系统级设计的各个方面&#xff0c;是现代电子产品的核心。在这篇文章中&#xff0c;我将介绍60个基础概念&#xff0c;这些概念是硬件设计工程师必备的知识&#xff0c;并且是设计出高质量硬件的关键…

【web】学习ajax和fetch

1/什么是ajax ajax 全名 async javascript and XML(异步JavaScript和XML) 是前后台交互的能⼒。 也就是我们客户端给服务端发送消息的⼯具&#xff0c;以及接受响应的⼯具。 在不重新加载整个网页的情况下&#xff0c;对网页的某部分进行更新。而传统的网页(不使用 Ajax)如果需…

设计师必备的5个素材库,马住

今天就告诉大家设计师都是去哪些网站找素材&#xff0c;分享五个网站&#xff0c;解决你80%的设计素材&#xff0c;建议收藏&#xff01; 1、菜鸟图库 https://www.sucai999.com/?vNTYxMjky 这是一个为新手设计师提供免费素材的设计网站&#xff0c;站内有超多平面模板、海报…

成为一名黑客需要学什么

想成为一名专业黑客&#xff0c;但不知道从哪里学起”很多人向盾叔问过这个问题&#xff0c;今天盾叔就为你介绍成为专业黑客必须学习的十个方面的知识&#xff0c;希望能为迷惘中的你指明方向。 一、基本的计算机知识 把它列为第一条&#xff0c;相信很多人肯定会觉得不以为…

Java虚拟机快速入门 | JVM引言、JVM内存结构、直接内存

目录 一&#xff1a;JVM引言 1. 什么是 JVM ? 2. 常见的 JVM 3. 学习路线 二&#xff1a;JVM内存结构 1. 程 序 计 数 器&#xff08;PC Register&#xff09; 2. 虚 拟 机 栈&#xff08;JVM Stacks&#xff09; 3. 本 地 方 法 栈&#xff08;Native Method Stacks&…

Vscode C++环境配置

多文件编译 打开设置搜索coderunner 找到Executor Map 加入-I目录名 目录名/*.cpp 调试 点击调试以后会产生tasks.json文件&#xff0c;加入链接文件和库文件

map用法以及特殊值的情况

map用法以及特殊值的情况 一、map用法的说明 map(callbackFn, thisArg); // callbackFn回调函数&#xff0c;thisArg可选 callbackFn是个回调函数&#xff0c;该回调函数的参数按照顺序为element&#xff08;当前正在处理的元素&#xff09;&#xff0c;index&#xff08;正…

WPF MaterialDesign 初学项目实战(1)首页搭建

前言 最近在学WPF&#xff0c;由于人比较烂&#xff0c;有一个星期没怎么动代码了。感觉有点堕落。现在开始记录WPF项目&#xff0c;使用MaterialDesignInXamlToolkit。 环境搭建 如果没下载MaterialDesign 的源码 github源码运行 在Nuget里面引入MaterialDesign Materia…

数字孪生技术在环境保护领域怎样应用?

近年来&#xff0c;环境保护成为全球范围内的热点话题&#xff0c;各国都在积极探索创新的解决方案。其中&#xff0c;数字孪生技术的出现为环境保护带来了全新的机遇和挑战。数字孪生技术将物理世界与数字世界相结合&#xff0c;通过精确的模拟和实时数据分析&#xff0c;为环…

华为ensp 防火墙的基础配置

拓扑图&#xff1a; [FW3-zone-isp1]set priority 12 #配置防火墙优先级 步骤一 #首先进入防火墙需要输入默认账号和密码&#xff0c;必须修改密码。 [USG6000V1] undo in en #关闭提示。 #先配置ip。 [USG6000V1]ip route-static 0.0.0.0 0.0.0.0 64.1.1.10 #配置去往外网的默…

【Redis】Redisson入门以及Redisson可重入锁的lua脚本实现

目录 一、Redisson介绍 二、Redisson的入门 1、引入依赖 2、配置客户端 3、使用锁 三、Redisson可重入锁的原理 1、原理 2、实现 3、lua脚本保证原子性 1.获取锁 2.释放锁 一、Redisson介绍 在之前的文章里我们通过redis中的setn实现了一个简单的分布式锁以及解决了…

远程协助软件推荐,有哪些远程协助工具?

Win10、11自带远程协助工具-快速助手 Win10、11的快速助手使用非常简单。只要在左下角的搜索框搜索一下就可以找到了。 我们都知道&#xff0c;Windows带有远程桌面RDP功能&#xff0c;而快速助手是通过Windows的远程连接机制实现的。所以在使用前&#xff0c;被控端需要开启系…

谈薪谈蹦了,阿里HR说我不配21K....

好家伙&#xff0c;这奇葩事可真是多&#xff0c;前两天和粉丝聊天&#xff0c;他说前段时间面试阿里的测开岗&#xff0c;最后和面试官干起来了。 我问他为什么&#xff0c;他说没啥&#xff0c;就觉得面试官太装了&#xff0c;我说要24K&#xff0c;他说太高了&#xff0c;说…

Kyligence Zen 产品体验——超好用指标平台一站式体验教程

目录 背景介绍Kyligence Zen介绍上手指南数据概览可视化图表 自定义数据新建表新建视图 指标体验目标仪表盘集成优点个人建议体验总结每文一语 背景介绍 在数字化建设初期&#xff0c;许多企业主要采用基于商业智能&#xff08;BI&#xff09;报表的方式来处理数据&#xff0c…

杨红春没有“雷军”,良品铺子“高端”之路焦虑

文 | 螳螂观察 作者 | 图霖 如果休闲零食赛道要评一个六边形战士&#xff0c;良品铺子绝对是个不错的候选人。 尽管搭乘电商的风头起势&#xff0c;但得益于早期线下开店的经验&#xff0c;成功实现了两条腿走路。最新年报显示&#xff0c;其2022年线上收入占比为50.42%&…

《统计学习方法》——隐马尔可夫模型(上)

引言 隐马尔可夫模型(Hidden Markov Model,HMM)是描述隐藏的马尔可夫链随机生成观测数据过程的模型。 前置知识 马尔可夫链 马尔可夫链(Markov chain)又称离散时间马尔可夫链&#xff0c;使用 t t t来表示时刻&#xff0c;用 X t X_t Xt​来表示在时刻 t t t链的状态&#…

( 位运算 ) 338. 比特位计数 ——【Leetcode每日一题】

❓338. 比特位计数 难度&#xff1a;简单 给你一个整数 n &#xff0c;对于 0 < i < n 中的每个 i &#xff0c;计算其二进制表示中 1 的个数 &#xff0c;返回一个长度为 n 1 的数组 ans 作为答案。 示例 1&#xff1a; 输入&#xff1a;n 2 输出&#xff1a;[0,1,…

JavaScript变量声明

声明变量三个var let和const 1.首先var先排除&#xff0c;老派写法&#xff0c;问题很多&#xff0c;可以淘汰掉… 2.const优先&#xff0c;尽量使用const,原因&#xff1a; &#xff08;1&#xff09;const语义化更好 &#xff08;2&#xff09;很多变量声明的时候就知道它不会…

中本聪思想精髓难以领悟?Web3实际上还在“幻想”之中?

Web3概念是不错&#xff0c;有人说它是下一代互联网&#xff0c;有人说它是NFT和元宇宙等未来应用的基础设施。然而理论炒得火热&#xff0c;但却仍不见像ChatGPT一样能引爆市场的杀手级应用出现。 原因在于&#xff0c;当前的Web3概念是对中本聪思想的不断概括和提炼&#xff…

21 KVM管理虚拟机-在线修改虚拟机配置

文章目录 21 KVM管理虚拟机-在线修改虚拟机配置21.1 概述21.2 操作步骤 21 KVM管理虚拟机-在线修改虚拟机配置 21.1 概述 虚拟机创建之后用户可以修改虚拟机的配置信息&#xff0c;称为在线修改虚拟机配置。在线修改配置以后&#xff0c;新的虚拟机配置文件会被持久化&#x…