前端h5录音
时隔差不多半个月, 现在才来写这编博客。由于某些原因,我一直没有写,请大家原谅。前段时间开发了一个小模块。模块的主要功能就是有一个录音的功能。也就是说,模仿微信发送语音的功能一样。不多说,直接来一段代码
//自定义长按事件,注意, directives是跟 data ,created() 同一级的,注意注意
directives: {longpress: {bind(el, binding, vnode) {let pressTimer = null;let maxPressTime = 60000; // 60秒 = 60000毫秒 let startCallback = binding.value.start || Vue.noop;let endCallback = binding.value.end || Vue.noop;function handlePress() {startCallback();pressTimer = setTimeout(() => {endCallback();clearTimeout(pressTimer);pressTimer = null;}, maxPressTime);}function handleRelease() {if (pressTimer) {clearTimeout(pressTimer);pressTimer = null;endCallback();}}// 确保这些函数在unbind中可用 el._handlePress = handlePress;el._handleRelease = handleRelease;// 添加事件监听器 el.addEventListener('mousedown', handlePress);el.addEventListener('touchstart', handlePress);el.addEventListener('mouseup', handleRelease);el.addEventListener('touchend', handleRelease);el.addEventListener('mouseleave', handleRelease);el.addEventListener('touchcancel', handleRelease);},unbind(el) {// 移除事件监听器 el.removeEventListener('mousedown', el._handlePress);el.removeEventListener('touchstart', el._handlePress);el.removeEventListener('mouseup', el._handleRelease);el.removeEventListener('touchend', el._handleRelease);el.removeEventListener('mouseleave', el._handleRelease);el.removeEventListener('touchcancel', el._handleRelease);// 清理自定义属性 el._handlePress = null;el._handleRelease = null;}}
},
data(){return {zheZhao:false,//录音时的遮罩层isMai:false,//初始化,是否支持录音luYinAttr: {isAn:false,s: 0,timer:null,audioArray:[],startTime: null,//录音开始时间endTime: null, //录音结束时间mediaRecorder: null,recording: false, //录音状态(用于录音记时)timeLength: 0,//录音时长recordedChunks: [],timer: null,longPressThreshold:800,},}
}
//初始化录音对象(记住,录音一定得在 https里面。当然,在本地开发 localhost 可以不需要,但上线一定得在https里面)
luYin() {//这儿先做判断,判断当前设备或当前网络是否支持录音,如果不支持,那下面的也就不用多说了。//console.log(navigator, 'navigator.mediaDevices')if (!('mediaDevices' in navigator)) {this.isMai = false;return false;}navigator.mediaDevices.getUserMedia({ audio: true }).then(this.luYinSuccess) //支持录音,继续执行下面的操作.catch(this.luYinErr) //不支持录音,没下文
},
luYinSuccess(stream) {const audioContext = new AudioContext();//const sourceNode = audioContext.createMediaStreamSource(stream); //没用上,先注释const mediaRecorder = new MediaRecorder(stream);const recordedChunks = [];this.luYinAttr.mediaRecorder = mediaRecorderthis.luYinAttr.recordedChunks = recordedChunksthis.isMai = true;
},
luYinErr(err) {this.isMai = false;//console.log('The following error occurred: ' + err);
},//startRecording , stopRecording是开始计时和结束计时(由于录音的时间不能太长,这儿我,限制为1分分钟的时长。所以,我得有一个录音计时,超过1分钟,录音取消)
//开始计时
startRecording() {console.log('开始计时')this.luYinAttr.isAn = true;this.zheZhao = true;this.luYinAttr.s = 0;this.luYinAttr.recording = true;this.luYinAttr.startTime = Date.now();this.luYinAttr.timer = setInterval(() => {this.luYinAttr.s = this.luYinAttr.s + 1;} , 500)
},
//结束录音计时
stopRecording() {this.luYinAttr.recording = false;const endTime = Date.now();const startTime = this.luYinAttr.startTimeconst recordDuration = (endTime - startTime) / 1000; // 计算录音时长(秒) if (recordDuration >= 60) {this.luYinAttr.timeLength = 60;//结束录音}this.luYinAttr.timeLength = recordDuration;/*const maxWidth = 300; // 设置图标的最大宽度 const indicatorWidth = Math.min(recordDuration * 20, maxWidth); // 根据录音时长计算图标宽度,假设每秒对应2个像素宽度 console.log(indicatorWidth, '时长')console.log('结束计时')*/
},startLongPress() {//console.log('长按开始');let that = this;clearTimeout(this.luYinAttr.timer);that.luYinAttr.timer = setTimeout(function () {// 执行长按操作 //console.log('长按操作开始');that.luYinAttr.mediaRecorder.start();//console.log(that.luYinAttr.mediaRecorder.state , 'qqggg');//startRecord.disabled = true;//stopRecord.disabled = false;that.startRecording();}, that.luYinAttr.longPressThreshold);// 在这里编写长按开始时的逻辑
},
endLongPress() {//长按结束事件let that = this;//console.log('取消')clearTimeout(that.luYinAttr.timer);that.luYinAttr.mediaRecorder.stop();that.stopRecording();that.luYinAttr.mediaRecorder.ondataavailable = async function (e) {console.log(e)if (e.data.size > 0) {const blobObj = e.data;let nowTime = new Date().getTime();let fileName = 'LY_' + nowTime + '.mp3';const file = new File([blobObj], fileName, { type: 'audio/mpeg' });//that.uplodAudio(file) //上传录音that.luYinAttr.isAn = false;that.zheZhao = false;clearInterval(that.luYinAttr.timer)} else {// no data to push }};
},//试听录音(在苹果机上不支持)
listenAudio(obj, index) {let myTimer = null;this.play.listenStatus = this.play.listenStatus + 1;if (this.play.listenStatus > 1) {clearInterval(myTimer)return false;}if (!this.playActive.isLongPressing) {let that = this;let itemAudio = [(that.luYinAttr.recordedChunks)[index]]//console.log(itemAudio, 'itemAudio', obj)let len = JSON.parse(JSON.stringify(obj.timeLength));obj.timeLength = 0;myTimer = setInterval(() => {obj.timeLength = obj.timeLength + 1;if (obj.timeLength == len) {clearInterval(myTimer)this.play.listenStatus = 0;}} , 1000)const blob = new Blob(itemAudio, { 'type': 'audio/ogg; codecs=opus' });//const blob = new Blob(that.luYinAttr.recordedChunks, { 'type': 'audio/ogg; codecs=opus' });//console.log(blob, 'blob')const audioURL = window.URL.createObjectURL(blob);//console.log(audioURL, 'audioURL')const audio = new Audio(audioURL);//console.log(audio,'audio')audio.play().catch(err => {console.log('Failed to play sound:', err);});}
},
<template v-if="isMai == true"><div class="" style="background:#ffffff; padding:10px 16px;"><div class="list" style="text-align:center"><template v-for="(audioArrayItem , audioArrayIndex) in luYinAttr.audioArray"><el-tooltip placement="top" :manual="true" :value="audioArrayItem.tooltip"><div slot="content"><span @click="returnAudioArrayItem(audioArrayItem , audioArrayIndex)">移除</span></div><div style="margin-bottom:10px;" @click.prevent="listenAudio(audioArrayItem , audioArrayIndex)"><div style="overflow:hidden"><div style="float:left"><p style="background: #3975C6; color: #ffffff; padding:0 8px; display:inline-block; border-radius:4px;"><span class="icon iconfont playIcon" style="display:inline-block; height:20px; font-size:14px; text-align:center;"></span><span style="position:relative; top:-5px; margin:0 5px;"><template v-for="(item , index) in audioArrayItem.timeLength"><span>,,</span></template></span><span>{{ audioArrayItem.timeLength }}'</span></p></div><div style="float:right"><van-button type="info" size="mini" @click.stop="returnAudioArrayItem(audioArrayItem , audioArrayIndex)">移除</van-button></div></div></div></el-tooltip></template></div><div class="byBtn" :class="luYinAttr.isAn == true ? 'myIsYesAn': 'myIsNoAn' " style="text-align: center; font-size:14px;" v-longpress="{ start: startLongPress, end: endLongPress }"><p class="icon iconfont" style="color: #3975C6; font-size: 24px;"></p><p>任务详情 - 按住说话</p></div></div>
</template>
相关文章:
前端h5录音
时隔差不多半个月, 现在才来写这编博客。由于某些原因,我一直没有写,请大家原谅。前段时间开发了一个小模块。模块的主要功能就是有一个录音的功能。也就是说,模仿微信发送语音的功能一样。不多说,直接来一段代码 //自…...

Android Studio 使用Flutter开发第一个Web页面(进行中)
附上Flutter官方文档 1、新建Flutter项目(需要勾选web选项) 新建项目构成为: 2、配置 Flutter 使用 path 策略 官方文档 在main.dart中,需要导入flutter_web_plugins/url_strategy.dart包,并在main(){}函数中usePath…...

Vue.js组件精讲 第2章 基础:Vue.js组件的三个API:prop、event、slot
如果您已经对 Vue.js 组件的基础用法了如指掌,可以跳过本小节,不过当做复习稍读一下也无妨。 组件的构成 一个再复杂的组件,都是由三部分组成的:prop、event、slot,它们构成了 Vue.js 组件的 API。如果你开发的是一个…...

npm install 报 ERESOLVE unable to resolve dependency tree 异常解决方法
问题 在安装项目依赖时,很大可能会遇到安装不成功的问题,其中有一个很大的原因,可能就是因为你的npm版本导致的。 1.npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree 2.ERESOLVE unable to resolve dependenc…...

RPC还是HTTP
RPC是一个远程调用的通讯协议 RPC要比HTTP快一些 1. HTTP体积大 原因是HTTP协议会带着一堆无用信息 HTTP由三部分组成 请求头 请求行 请求体 这三部分只有请求体是需要的 2. HTTP支持的序列化协议比较少 RPC支持更多轻量级的通讯协议 3. RPC协议支持定制...
Conda 常用命令总结
创建虚拟环境 conda create -n name python[your_version] 激活环境 conda activate name 退出环境 conda deactivate 查看虚拟环境 conda info --envs 删除虚拟环境 conda remove -n name --all 删除所有的安装包及cache(索引缓存、锁定文件、未使用过的包和tar包) …...
Spring MVC 文件上传和下载
文章目录 Spring MVC 中文件上传利用 commons-fileupload 文件上传使用 Servlet 3.1 内置的文件上传功能 Spring MVC 中文件下载 Spring MVC 中文件上传 为了能上传文件,必须将 from 表单的 method 设置为 POST,并将 enctype 设置为 multipart/form-data…...

WSL访问adb usb device
1.Windows上用PowerShell运行: winget install --interactive --exact dorssel.usbipd-win 2.在WSLUbuntu上终端运行: sudo apt install linux-tools-generic hwdata sudo update-alternatives --install /usr/local/bin/usbip usbip /usr/lib/linux-too…...
CDF与PDF(描述随机变量的分布情况)
一、概念解释 CDF(Cumulative Distribution Function)和PDF(Probability Density Function)是概率论和统计学中常用的两个评价指标,用于描述随机变量的分布情况。 1. CDF(累积分布函数): - CDF是描述随机变量在某个取值及其之前所有可能取值的概率的函数。它表示了累…...
react项目中需要条形码功能,安装react-barcode使用时报错
react项目中需要条形码功能,用yarn add安装react-barcode后,在项目中使用import Barcode from ‘react-barcode’,页面中一直白屏,加载中 查看控制台报以下错误 load component failed Error: Module "./react-barcode"…...

ES6基础(JavaScript基础)
本文用于检验学习效果,忘记知识就去文末的链接复习 1. ECMAScript介绍 ECMAScript是一种由Ecma国际(前身为欧洲计算机制造商协会,英文名称是European Computer Manufacturers Association)通过ECMA-262标准化的脚本程序设计语言…...
[蓝桥杯] 纸张尺寸(C语言)
题目链接 蓝桥杯2022年第十三届省赛真题-纸张尺寸 - C语言网 题目理解 输入一行包含一个字符串表示纸张的名称,该名称一定是 A0、A1、A2、A3、A4、A5、A6、A7、A8、A9 之一,输出两行,每行包含一个整数,依次表示长边和短边的长度…...
AI推介-多模态视觉语言模型VLMs论文速览(arXiv方向):2024.04.05-2024.04.10
文章目录~ 1.BRAVE: Broadening the visual encoding of vision-language models2.ORacle: Large Vision-Language Models for Knowledge-Guided Holistic OR Domain Modeling3.MedRG: Medical Report Grounding with Multi-modal Large Language Model4.InternLM-XComposer2-4…...

【golang】动态生成微信小程序二维码实战下:golang 生成 小程序二维码图片 并通过s3协议上传到对象存储桶 | 腾讯云 cos
项目背景 在自研的系统,需要实现类似草料二维码的功能 将我们自己的小程序,通过代码生成相想要的小程序二维码 代码已经上传到 Github 需要的朋友可以自取 https://github.com/ctra-wang/wechat-mini-qrcode 一、生成Qrcode并提交到对象存储 通过源生A…...
kubeadm k8s 1.24之后版本安装,带cri-dockerd
最后编辑时间:2024/3/26 适用于1.24之后的版本 单节点配置 检查是否已经安装kubectl, kubelet, kubeadm直接输入命令确定,如果提示没有该指令则正确 kubectl kubelet kubeadm如果之前安装,首先reset,然后使用apt remove和snap r…...

13-pyspark的共享变量用法总结
目录 前言广播变量广播变量的作用 广播变量的使用方式 累加器累加器的作用累加器的优缺点累加器的使用方式 PySpark实战笔记系列第四篇 10-用PySpark建立第一个Spark RDD(PySpark实战笔记系列第一篇)11-pyspark的RDD的变换与动作算子总结(PySpark实战笔记系列第二篇))12-pysp…...

BI数据分析软件:行业趋势与功能特点剖析
随着数据量的爆炸性增长,企业对于数据的需求也日益迫切。BI数据分析软件作为帮助企业实现数据驱动决策的关键工具,在当前的商业环境中扮演着不可或缺的角色。本文将从行业趋势、功能特点以及适用场景等方面,深入剖析BI数据分析软件࿰…...

centos7上docker搭建vulhub靶场
1 vulhub靶场概述 VulHub是一个在线靶场平台,提供了丰富的漏洞环境供安全爱好者学习和实践。 该平台主要面向网络安全初学者和进阶者,通过模拟真实的漏洞环境,帮助用户深入了解漏洞的成因、利用方式以及防范措施。 此外,VulHub还…...
Flutter入门指南
文章目录 一、环境搭建二、基本概念三、创建一个简单的Flutter应用四、常用组件及代码示例五、总结推荐阅读 笔者项目中使用Flutter的模块并不多。虽然笔者还没有机会在项目中正式使用Flutter,但是也在学习Flutter的一些基本用法。本文就是一篇Flutter的入门介绍&am…...

keepalived脑裂问题
脑裂问题产生的原因 就是vip同时存在 master和backup 就叫做脑裂 比如说 backup 机器的防火墙没关,并且没有允许vrrp通过,backup 没有收到master的心跳数据,就会抢夺资源,发生脑裂问题测试 我们打开test3的防火墙,此…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

内窥镜检查中基于提示的息肉分割|文献速递-深度学习医疗AI最新文献
Title 题目 Prompt-based polyp segmentation during endoscopy 内窥镜检查中基于提示的息肉分割 01 文献速递介绍 以下是对这段英文内容的中文翻译: ### 胃肠道癌症的发病率呈上升趋势,且有年轻化倾向(Bray等人,2018&#x…...
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined.
这个警告表明您在使用Vue的esm-bundler构建版本时,未明确定义编译时特性标志。以下是详细解释和解决方案: 问题原因: 该标志是Vue 3.4引入的编译时特性标志,用于控制生产环境下SSR水合不匹配错误的详细报告1使用esm-bundler…...