学习uni-app开发,实现了一个微信聊天的demo,简单记录下其中的语音发送功能。这里只是介绍从发送到显示的过程,暂不涉及websocket做聊天对话。若有错误和不足之处留言指正,谢谢!

功能就不细说了,最终效果的UI界面如下,用户昵称头像皆为测试数据。

长按按住说话按钮弹出遮罩层按住说话,直接松开即可发送语音。

若向左上方滑动松开后取消发送当前语音。

一. 绑定事件

首先给按钮是绑定事件,需要给按钮绑定三个事件

1.touchstart事件会在触摸按钮时触发,可以获取手指的初始坐标;

2.touchmove事件在触摸后移动手指时触发,可以用来计算手手指的滑动距离;

3.touchend事件在松开手指时候触发。

<button type="default" v-show="mode === 'voice'" @touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd"
>按住 说话</button>

页面遮罩层

<!-- 语音遮罩层 -->
<view class="voice-mask" v-show="mask"><!--语音条 --><view class="voice-bar voice-del" :class="{voiceDel:needCancel}" :style={width:getVoiceBarWidth}><image src="../static/icon/wave.png" class="voice-volume" :class="{volumeDel:needCancel}"></image><view class="trangle-bottom" :class="{trangleDel:needCancel}"></view></view><!-- 底部区域 --><view class="voice-send"><!-- 取消和转文字图标 --><view class="voice-middle-wrapper"><!-- 取消 --><view class="voice-left-wrapper"><view class="cancel-del" :class="{delTip:needCancel}">松开 取消</view><view class="voice-middle-inner close" :class="{bigger:needCancel}"><image src="../static/icon/close-grey.png" class="close-icon"></image></view></view><!-- 转文字 --><view class="voice-middle-inner to-text"><text class="wen">文</text><!-- <image src="" class="wen"></image> --></view><view class="send-tip" :class="{sendTipNone:needCancel}">松开 发送</view></view> <!-- 底部语音按钮 --><view class="mask-bottom"><image src="../static/icon/voice-left.png"></image></view></view>
</view>......<style>.voice-mask{position:fixed;top:0;right:0;bottom:0;left:0;/* display: flex;flex-direction: column;justify-content: flex-end;align-items: center; */background-color: rgba(0,0,0,0.8);}.voice-bar{position: absolute;left:50%;top: 50%;transform: translate(-50%,-30%);/* width: 230rpx; */height:150rpx;background-color:#51ff50;border-radius: 26rpx;margin-bottom: 220rpx;}.voiceDel{left:80rpx;top: 50%;width: 170rpx !important;transform: translateX(0%);transform: translateY(-30%);background-color: red;}.voice-volume{position: absolute;top: 50%;left: 50%;transform: translate(-50%,-50%);width: 160rpx;height: 36rpx;}.volumeDel{width: 80rpx;}.trangle-bottom{position: absolute;bottom: -38rpx;left:50%;transform: translateX(-50%);border-width: 20rpx;border-style: solid;border-color: #51FF50 transparent transparent transparent;}.trangleDel{border-color: red transparent transparent transparent;}.voice-send{position: absolute;bottom: 0;width: 100%;}.voice-middle-wrapper{width: 100%;display: flex;position:relative;justify-content: space-between;align-items: flex-end;margin-bottom: 40rpx;}.voice-left-wrapper{display: flex;flex-direction: column;justify-content: center;align-items: flex-end;}.cancel-del{display:none;}.delTip{display:block;color:#bfbfbf;margin: 0 22rpx 18rpx 0;}.voice-middle-inner{display: flex;justify-content: center;align-items: center;background-color: rgba(0,0,0,0.2);width: 140rpx;height: 140rpx;border-radius: 50%;}.close{transform: rotate(350deg);margin-left: 80rpx;} .bigger{width: 170rpx;height: 170rpx;}.to-text{transform: rotate(10deg);margin-right: 80rpx;}.close-icon{width: 80rpx;height: 80rpx;    }.wen{font-size: 40rpx;color:#bfbfbf;}.send-tip{position: absolute;left: 50%;bottom:0rpx;transform: translate(-50%,36%);color:#bfbfbf;}.sendTipNone{display: none;}.mask-bottom{position: relative;width: 100%;height:190rpx;border-top: #BABABB 8rpx solid;border-radius: 300rpx 300rpx 0 0;background-image: linear-gradient(#949794,#e1e3e1);}.mask-bottom image{position: absolute;width: 60rpx;height: 60rpx;top: 0;right:0;bottom: 0;left: 0;margin: auto;}
</style>

随后定义事件回调, 三个事件中可以在事件对象event中touches数组获取当前屏幕上所有触摸点的列表,touches数组中保存着多个手指触摸点的信息,触摸点信息中的pageX,pageY属性获取的是触摸目标在页面中的x和y坐标。

二.音频录制

录制语音需要获取uni-app提供的全局录音管理器uni.getRecorderManager();

官方文档:录音管理 - uni-app官网 (dcloud.io)

开始录音调用该对象的recorderManager.start()方法,结束录音调用recorderManager.stop(),并在结束的回调recorderManager.onStop中获取音频的文件地址。

开始录音时需要记录保存初始的触摸坐标pageX和pageY到当前组件实例,此外由于语音需要显示时长,还需要开启计时器setInterval计算录音时长,长度保存在组件实例的length属性中,且录音的时长上限为59s。

const recorderManager = uni.getRecorderManager();
// 开始录制语音
handleTouchStart(e){this.mask = true;recorderManager.start();this.length = 1;this.startX = e.touches[0].pageX;this.startY = e.touches[0].pageY;this.timer = setInterval(() => {this.length += 1;if(this.length >= 60) {clearInterval(this.timer);this.handleTouchEnd()}},1000);
},

此外还需实现了一个细节,遮罩层中的语音条的长度是随录制时长递增的,如下图:

使用计算属性实现,录音时长和长度关系如下:

computed:{// 计算语音条宽度getVoiceBarWidth(){return (230 + this.length * 4) + 'rpx';}
},

三.取消发送行为判断

开始录制语音后开启遮罩层,此时若用户移动手指会触发touchmove事件,在该事件的事件对象中获取当前用户手指在页面的实时坐标pageX和pageY,判断用户是否存在向左上方方向移动手指至取消发送图标行为,这里做了一些测试,取this.startX - e.touches[0].pageX > 14 && this.startY - e.touches[0].pageY > 50为判断条件,满足条件即为取消发送。

// 语音录制时滑动事件
handleTouchMove(e){if(this.startX - e.touches[0].pageX > 14 && this.startY - e.touches[0].pageY > 50){this.needCancel = true;} else {this.needCancel = false;}
},

四.提交音频

用户松开手指执行录制结束的回调,其中需要根据needCancel 标志判断该音频是否需要发送;在onStop回调中整理音频信息,调用提交音频的方法。

// 语音录制结束
handleTouchEnd(){this.mask = false;clearInterval(this.timer);recorderManager.stop();recorderManager.onStop((res) => {const message = {voice:res.tempFilePath,length:this.length};if(!this.needCancel){this.inputSubmit(message,2);}this.needCancel = false});
}

这里将聊天界面底部菜单栏单独封装成一个组件bottomBar,所以在音频录制完毕后将信息提交父组件chatroom遍历展示。

inputSubmit(msg,types){if(msg.types === 0 && this.inputText == '') return;this.$emit('sub',msg,types);  if(this.inputText){this.inputText = '';}
},

语音条的页面结构如下所示

<!-- 2 - 当前用户的语音 -->
<view class="chat-voice-right" :style="{width:handleVoiceWidth(item.message.length)}" v-if="item.types === '2'" @tap="handleVoicePlay(item)"><view class="chat-voice-right-inner"><!-- 时长 --><text decode='true' class="chat-voice-length-right">{{item.message.length}}</text><!-- 语音条主体 --><text class="chat-voice-second-right">"</text><!-- 语音图标 --><image src="../../static/icon/voice-right.png" class="voice-img"></image></view><!-- 向右三角形 --><view class="trangle trangle-right"></view>
</view>

六.音频长度处理

语音条长度和时长之间存在一个转换关系,10秒内的语音和大于10秒的语音长度增长比例是不一致的,这里规定在10秒的语音长度正好为可变长度的一半,随后的50秒缓慢增长到可变长度的最大值Lmax,具体转换关系如下:

// 处理语音长度
handleVoiceWidth(lenght){lenght = lenght - 1;let Lmin = 138;let Lmax = 366let barCanChangeLen = Lmax - Lmin;// 11秒以内的语音if (lenght < 11) {// VoicePlayTimes 为10秒时,正好为可变长度的一半return (Lmin + lenght * 0.05 * barCanChangeLen) + 'rpx'; } else {// 12-60秒的语音return (Lmin + 0.5 * barCanChangeLen + (lenght - 10) * 0.01 * barCanChangeLen) + 'rpx';}
},

七.音频播放

播放语音需要创建并返回内部 audio 上下文 innerAudioContext 对象;

官方文档:音频组件控制 - uni-app官网 (dcloud.io)

需要指定src属性为播放音频的链接,调用play()播放,调用stop()停止,播放自然结束触发触发方法onEnded,onStop修改播放标志。

const innerAudioContext = uni.createInnerAudioContext();
// 播放语音
handleVoicePlay(item){item.isFirstPlay = false;innerAudioContext.src = item.message.voice;this.isPlay = !this.isPlay;this.isPlay ? innerAudioContext.play() : innerAudioContext.stop();innerAudioContext.onEnded(() => {this.isPlay = false;})innerAudioContext.onStop(() => {this.isPlay = false;})
},

【uni-app】模仿微信实现简易发送/取发语音功能相关推荐

  1. android按住录音按钮_Android模仿微信录音、发送语音效果实现

    在项目开发中,有个需求:实现模仿微信录音,发送语音的功能.长按按钮录音,弹框显示语音时间,以及上滑取消发送.我重写了一个发送语音的控件,以实现该功能. 首先添加权限: AudioRecorderBut ...

  2. uni app 开发微信小程序及上线体验

    uni app 开发微信小程序及上线体验 项目创建及微信小程序AppId的申请 本次开发的是电商类的微信小程序,这里用到的是HBuilderX这个编辑器.之前用的Visual Studio Code ...

  3. 李长军android语音开发_Android模仿微信录音、发送语音效果实现

    在项目开发中,有个需求:实现模仿微信录音,发送语音的功能.长按按钮录音,弹框显示语音时间,以及上滑取消发送.我重写了一个发送语音的控件,以实现该功能. 首先添加权限: AudioRecorderBut ...

  4. uni.app H5(微信公众号定位) uni.getLocation

    最近在开发公众号,由于之前经常使用uni,app写APP,索性就用uni.app来开发公众号了, 不过也遇到了一个问题,就是在公众号的首页要获取用户的定位.我看了看官网的API 有个uni.getLo ...

  5. 微信打字的隐藏鸿蒙系统,为什么有些人微信聊天只打字不发语音?

    我就是这种人,简单从我的角度分析一下. 1,同样一个意思.文字比语音意思更容易表达清楚,"阅读"时间更短.对于接收方体验更好. 2,大家都碰到过60秒语音.对于接收方有多痛苦不言而 ...

  6. 微信朋友圈也可以发语音你们造吗?

    在微信朋友圈发照片太矫情,小视频又耗流量,只发文字又太单调,何不发段语音来呼朋唤友呢?你是说朋友圈发语音?这是真的吗?sure,开发者已经利用微信JS-SDK接口实现了,扫描下方的二维码 然后开始录音 ...

  7. 记录uni.app开发微信小程序中地图的使用,以及项目中的解决办法

    标题先讲一下需求:需要获取用户的地址信息以及经纬度,并在地图中展示时出来 uniapp官方也提供了api uni.getLocation(OBJECT) getLocation只能获取用户的经纬度,不 ...

  8. 微信小程序 发送模板消息的功能实现

    背景 - 小程序开发的过程中,绝大多数会满足微信支付 - 那么,作为友好交互的体现,自然就会考虑到支付后的消息通知咯 - 所以,我的小程序项目也要求完成这个效果,so.分享一下自己的实现步骤,以方便道 ...

  9. 微信公众号拉取扫码功能

    点击扫码按钮拉取微信扫码 // 扫码添加设备goAddEquipment() {const wx = window.wxlet _this = this// wx.ready(function() { ...

  10. iOS模仿微信滑块动态设置字体大小的功能

    首先说一下实现这一功能的核心类别,UITableViewCell+FSAutoCountHeight.这个类其实是利用运行时将每个cell高度通过子控件的约束动态加以控制进而返回一个值,这个值就是我们 ...

最新文章

  1. 如何锁定计算机硬盘,Win7系统怎么锁住磁盘|Win7系统锁住磁盘的详细步骤
  2. JSON解析的几种方式
  3. ML.NET Cookbook:(17)如何在分类数据上训练模型?
  4. 【渝粤教育】国家开放大学2018年春季 0064-22T20世纪欧美文学 参考试题
  5. Shell脚本完成hadoop的集群安装
  6. HiveQL与SQL区别
  7. 程序员面试金典 - 面试题 16.14. 最佳直线(哈希map+set)
  8. 【pytorch】torch.meshgrid()==>常用于生成二维网格,比如图像的坐标点
  9. 30-80k/月!影创科技算法岗招聘,含实习生
  10. Python适合自己的IDE才是最好的IDE
  11. FreeRTOS(2)---学习FreeRTOS前的准备工作
  12. AC日记——单词替换 1.7 21
  13. manage key mysql_mysql相关操作
  14. 22计算机408考研—数据结构—线性表、栈、队列、数组
  15. android.net.http.AndroidHttpClient Android6.0 API23以后失效
  16. html中鼠标冒泡泡,鼠标经过出现气泡框的简单实例
  17. HCIE-Routing Switching认证
  18. 局域网出现广播风暴怎么办?如何阻止广播风暴?
  19. openwrt编译ifb.ko模块问题
  20. 位(bit)、字节(Byte)、MB(兆位)之间的换算关系

热门文章

  1. simplest_ffmpeg_demuxer_simple(新版ffmpeg函数)
  2. 设计一个电商平台积分兑换系统
  3. 深度 | 蚂蚁金融科技全面开放战略背后的“硬实力” 1
  4. 前端cookie详解
  5. 通过深度学习实现对网络异常流量检测
  6. ps磨皮滤镜portraiture3 破解方法
  7. ARPG游戏DEMO
  8. 计算机读不了硬盘分区,修复移动硬盘分区故障和无法识别计算机
  9. 【黑马旅游网】项目完结+未完成功能实现+个人总结+bug记录
  10. 高可靠性技术之RRPP和VRRP