项目初始化
This commit is contained in:
25
uni_modules/all-speech/changelog.md
Normal file
25
uni_modules/all-speech/changelog.md
Normal file
@@ -0,0 +1,25 @@
|
||||
## 1.1.1(2024-05-22)
|
||||
1.新增node后端方便测试
|
||||
2.修复H5及H5上传示例
|
||||
## 1.0.7(2024-05-22)
|
||||
更新H5上传示例,新增node后端测试 自行进行测试
|
||||
## 1.0.6(2024-03-19)
|
||||
上传示例项目
|
||||
## 1.0.5(2024-03-19)
|
||||
修复PC语音录制
|
||||
## 1.0.4(2024-03-19)
|
||||
兼容pc
|
||||
## 1.0.0(2024-03-19)
|
||||
兼容PC
|
||||
## 1.0.3(2024-03-15)
|
||||
修复安卓导入失败
|
||||
## 1.0.2(2024-03-14)
|
||||
修复小程序报错,支持vue3
|
||||
## 1.0.1(2024-03-05)
|
||||
补充说明,原作者信息。本人只是补充
|
||||
|
||||
|
||||
## 1.0.0(2024-03-05)
|
||||
### 首次发布
|
||||
1.原地址 https://ext.dcloud.net.cn/plugin?id=9595
|
||||
2.原有基础上兼容H5,如有侵权联系立刻下架
|
652
uni_modules/all-speech/components/all-speech/all-speech.vue
Normal file
652
uni_modules/all-speech/components/all-speech/all-speech.vue
Normal file
@@ -0,0 +1,652 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="cbb-record">
|
||||
<view class="conbox record">
|
||||
<!-- 此处可放置倒计时,可根据需要自行添加 -->
|
||||
<view class="time">
|
||||
{{showRecordTime}}
|
||||
</view>
|
||||
|
||||
<view class="c999">
|
||||
最短{{minTime}}秒,最长{{maxTime}}秒
|
||||
</view>
|
||||
|
||||
<view class="record-box">
|
||||
<view class="stop" @tap.stop="stopVoice" v-if="_voicePath && playing==1 && recordTime1 > minTime-1">
|
||||
<u-icon color="#3cc9a4" name="pause-circle" size="80"></u-icon>
|
||||
</view>
|
||||
<view class="paly" @tap.stop="playVoice" v-if="_voicePath && playing==0 && recordTime1 > minTime-1">
|
||||
<u-icon color="#3cc9a4" name="play-circle" size="80"></u-icon>
|
||||
</view>
|
||||
<!-- #ifdef APP-PLUS ||MP-WEIXIN -->
|
||||
<view class="circle-box"
|
||||
@longpress="startRecord"
|
||||
@touchend="endRecord">
|
||||
<view class="voice-btn">
|
||||
<view class="progress-icon">
|
||||
<u-icon name="mic" color="#fff" size="70"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- <u-circle-progress width="170" active-color="#2979ff" :percent="percent" :duration="duration">
|
||||
<view class="u-progress-content">
|
||||
<view class="u-progress-dot">
|
||||
<u-icon name="mic" size="60"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</u-circle-progress> -->
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef H5 -->
|
||||
<view class="circle-box" @click="startRecord">
|
||||
<view class="voice-btn">
|
||||
<view class="progress-icon">
|
||||
<u-icon name="mic" color="#fff" size="70"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- <u-circle-progress width="170" active-color="#2979ff" :percent="percent" :duration="duration">
|
||||
<view class="u-progress-content">
|
||||
<view class="u-progress-dot">
|
||||
<u-icon name="mic" size="60"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</u-circle-progress> -->
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
|
||||
<view class="confirm" @tap.stop="okClick" v-if="_voicePath && recordTime1 > minTime-1">
|
||||
<u-icon color="#3cc9a4" name="checkmark-circle" size="80"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="c666 fz32 domess">{{ btnTextContent }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
var that;
|
||||
var innerAudioContext;//播放
|
||||
// #ifdef APP-PLUS || MP-WEIXIN
|
||||
const recorderManager = uni.getRecorderManager();
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
import speech from '../../js_sdk/h5-speech/speech.js';
|
||||
|
||||
const recorderManager = new speech(8000);
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
// 引入权限判断
|
||||
import permision from '../../js_sdk/wa-permission/permission.js';
|
||||
// #endif
|
||||
|
||||
import tuiCircularProgress from "@/components/thorui/tui-circular-progress/tui-circular-progress.vue"
|
||||
|
||||
export default {
|
||||
name: 'nbVoiceRecord',
|
||||
components:{
|
||||
tuiCircularProgress,
|
||||
},
|
||||
/**
|
||||
* 录音交互动效组件
|
||||
* @property {Object} recordOptions 录音配置
|
||||
* @property {Object} btnStyle 按钮样式
|
||||
* @property {Object} btnHoverFontcolor 按钮长按时字体颜色
|
||||
* @property {String} btnHoverBgcolor 按钮长按时背景颜色
|
||||
* @property {String} btnDefaultText 按钮初始文字
|
||||
* @property {String} btnRecordingText 录制时按钮文字
|
||||
* @property {Boolean} vibrate 弹窗时是否震动
|
||||
* @property {String} popupTitle 弹窗顶部文字
|
||||
* @property {String} popupDefaultTips 录制时弹窗底部提示
|
||||
* @property {String} popupCancelTips 滑动取消时弹窗底部提示
|
||||
* @property {String} popupMaxWidth 弹窗展开后宽度
|
||||
* @property {String} popupMaxHeight 弹窗展开后高度
|
||||
* @property {String} popupFixBottom 弹窗展开后距底部高度
|
||||
* @property {String} popupBgColor 弹窗背景颜色
|
||||
* @property {String} lineHeight 声波高度
|
||||
* @property {String} lineStartColor 声波波谷时颜色色值
|
||||
* @property {String} lineEndColor 声波波峰时颜色色值
|
||||
* @event {Function} startRecord 开始录音回调
|
||||
* @event {Function} endRecord 结束录音回调
|
||||
* @event {Function} cancelRecord 滑动取消录音回调
|
||||
* @event {Function} stopRecord 主动停止录音
|
||||
*/
|
||||
props: {
|
||||
recordOptions: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
duration: 600000
|
||||
}; // 请自行查看各端的的支持情况,这里全部使用默认配置
|
||||
}
|
||||
},
|
||||
btnStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
width: '300rpx',
|
||||
height: '80rpx',
|
||||
borderRadius: '20rpx',
|
||||
backgroundColor: '#fff',
|
||||
border: '1rpx solid whitesmoke',
|
||||
permisionState: false
|
||||
};
|
||||
}
|
||||
},
|
||||
btnHoverFontcolor: {
|
||||
type: String,
|
||||
default: '#000' // 颜色名称或16进制色值
|
||||
},
|
||||
btnHoverBgcolor: {
|
||||
type: String,
|
||||
default: 'whitesmoke' // 颜色名称或16进制色值
|
||||
},
|
||||
btnDefaultText: {
|
||||
type: String,
|
||||
default: '长按开始录音'
|
||||
},
|
||||
btnRecordingText: {
|
||||
type: String,
|
||||
default: '录音中'
|
||||
},
|
||||
vibrate: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// #ifdef APP-PLUS || MP-WEIXIN
|
||||
popupTitle: {
|
||||
type: String,
|
||||
default: '正在录制音频'
|
||||
},
|
||||
popupDefaultTips: {
|
||||
type: String,
|
||||
default: '左右滑动后松手完成录音'
|
||||
},
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
popupTitle: {
|
||||
type: String,
|
||||
default: '点击录制音频'
|
||||
},
|
||||
popupDefaultTips: {
|
||||
type: String,
|
||||
default: '点击完成录音'
|
||||
},
|
||||
// #endif
|
||||
popupCancelTips: {
|
||||
type: String,
|
||||
default: '松手取消录音'
|
||||
},
|
||||
popupMaxWidth: {
|
||||
type: Number,
|
||||
default: 600 // 单位为rpx
|
||||
},
|
||||
popupMaxHeight: {
|
||||
type: Number,
|
||||
default: 300 // 单位为rpx
|
||||
},
|
||||
popupFixBottom: {
|
||||
type: Number,
|
||||
default: 200 // 单位为rpx
|
||||
},
|
||||
popupBgColor: {
|
||||
type: String,
|
||||
default: 'whitesmoke'
|
||||
},
|
||||
lineHeight: {
|
||||
type: Number,
|
||||
default: 50 // 单位为rpx
|
||||
},
|
||||
lineStartColor: {
|
||||
type: String,
|
||||
default: 'royalblue' // 颜色名称或16进制色值
|
||||
},
|
||||
lineEndColor: {
|
||||
type: String,
|
||||
default: 'indianred' // 颜色名称或16进制色值
|
||||
},
|
||||
voicePath: { //默认地址
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
maxTime: { // 录音最大时长,单位秒
|
||||
type: Number,
|
||||
default: 15
|
||||
},
|
||||
minTime: { // 录音最小时长,单位毫秒
|
||||
type:Number ,
|
||||
default: 5
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
stopStatus: true, // 是否已被父页面通知主动结束录音
|
||||
btnTextContent: this.btnDefaultText,
|
||||
startTouchData: {},
|
||||
popupHeight: '0px', // 这是初始的高度
|
||||
recording: true, // 录音中
|
||||
recordPopupShow: false,
|
||||
recordTimeout: null, // 录音定时器
|
||||
h5start: false,
|
||||
isShow:false,
|
||||
playing:0,//是否播放中
|
||||
timeObj: null, //计时id
|
||||
countdownObj: null, //倒计时id
|
||||
recordTime: 0,//录音时长
|
||||
recordTime1:0,//播放录音倒计时
|
||||
percent: 0,
|
||||
duration1: true,
|
||||
newViocePath: '',
|
||||
};
|
||||
},
|
||||
created() {
|
||||
that = this;
|
||||
innerAudioContext = uni.createInnerAudioContext();//播放
|
||||
|
||||
// 请求权限
|
||||
this.checkPermission();
|
||||
// #ifdef APP-PLUS || MP-WEIXIN
|
||||
recorderManager.onStop((res) => {
|
||||
that.newViocePath = res.tempFilePath;
|
||||
that.endRecord();
|
||||
that.$emit('endRecord', res);
|
||||
});
|
||||
recorderManager.onStart((err) => {
|
||||
console.log('开始:', err);
|
||||
});
|
||||
recorderManager.onError((err) => {
|
||||
console.log('err:', err);
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
computed: {
|
||||
showRecordTime() {
|
||||
var strs = "";
|
||||
var m = Math.floor(this.recordTime/60);
|
||||
if(m<10) strs = "0"+m;
|
||||
|
||||
var s = this.recordTime%60;
|
||||
strs += (s<10) ? ":0"+s : ":"+s;
|
||||
|
||||
return strs
|
||||
},
|
||||
duration() {
|
||||
return this.duration1 ? this.maxTime*1000 : 0;
|
||||
},
|
||||
_voicePath(){
|
||||
return this.newViocePath || this.voicePath;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
upx2px(upx) {
|
||||
return uni.upx2px(upx) + 'px';
|
||||
},
|
||||
async checkPermission() {
|
||||
var that = this;
|
||||
// #ifdef APP-PLUS
|
||||
// 先判断os
|
||||
let os = uni.getSystemInfoSync().osName;
|
||||
if (os == 'ios') {
|
||||
this.permisionState = await permision.judgeIosPermission('record');
|
||||
} else {
|
||||
this.permisionState = await permision.requestAndroidPermission('android.permission.RECORD_AUDIO');
|
||||
}
|
||||
if (this.permisionState !== true && this.permisionState !== 1) {
|
||||
uni.showToast({
|
||||
title: '请先授权使用录音',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
if (!window.navigator?.mediaDevices?.getUserMedia) {
|
||||
this.permisionState = false;
|
||||
uni.showToast({
|
||||
title: '请先授权使用录音',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
this.permisionState = true;
|
||||
}
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.authorize({
|
||||
scope: 'scope.record',
|
||||
success(e) {
|
||||
that.permisionState = true;
|
||||
// that.startRecord();
|
||||
},
|
||||
fail() {
|
||||
uni.showToast({
|
||||
title: '请授权使用录音',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
|
||||
startRecord() {
|
||||
this.percent = 0;
|
||||
this.duration1 = true;
|
||||
|
||||
if (!this.permisionState) {
|
||||
this.checkPermission();
|
||||
return;
|
||||
}
|
||||
if (this.h5start) {
|
||||
this.duration1 = false;
|
||||
this.h5start = false;
|
||||
this.endRecord();
|
||||
return;
|
||||
}
|
||||
this.h5start = true;
|
||||
this.stopStatus = false;
|
||||
|
||||
this.stopVoice();
|
||||
this.percent = 100;
|
||||
this.recordTime = 0;
|
||||
this.newViocePath = "";//音频地址
|
||||
this.btnTextContent = this.btnRecordingText;
|
||||
this.timeObj = setInterval(() => {
|
||||
this.recordTime ++;
|
||||
if(this.recordTime == this.maxTime) {
|
||||
this.endRecord();
|
||||
}
|
||||
},1000);
|
||||
|
||||
setTimeout(() => {
|
||||
setTimeout(() => {
|
||||
if (this.vibrate) {
|
||||
// #ifdef APP-PLUS
|
||||
// 震动
|
||||
plus.device.vibrate(35);
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.vibrateShort();
|
||||
// #endif
|
||||
}
|
||||
// 开始录音
|
||||
recorderManager.start(this.recordOptions);
|
||||
|
||||
this.$emit('startRecord');
|
||||
}, 100);
|
||||
}, 200);
|
||||
},
|
||||
endRecord() {
|
||||
this.percent = 0;
|
||||
this.h5start = false;
|
||||
|
||||
let recordTime = this.recordTime;
|
||||
this.recordTime1 = this.recordTime;
|
||||
clearInterval(this.timeObj); //清除计时器
|
||||
|
||||
var that = this;
|
||||
if (this.stopStatus) {
|
||||
return;
|
||||
}
|
||||
//this.popupHeight = '0px';
|
||||
//this.recordPopupShow = false;
|
||||
this.btnTextContent = this.btnDefaultText;
|
||||
// #ifdef APP-PLUS || MP-WEIXIN
|
||||
recorderManager.stop();
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
const res = recorderManager.stop();
|
||||
that.newViocePath = res;
|
||||
that.$emit('endRecord', res);
|
||||
// #endif
|
||||
},
|
||||
stopRecord() {
|
||||
// 用法如你录音限制了时间,那么将在结束时强制停止组件的显示
|
||||
this.endRecord();
|
||||
this.stopStatus = true;
|
||||
},
|
||||
touchStart(e) {
|
||||
this.startTouchData.clientX = e.changedTouches[0].clientX; //手指按下时的X坐标
|
||||
this.startTouchData.clientY = e.changedTouches[0].clientY; //手指按下时的Y坐标
|
||||
},
|
||||
touchMove(e) {
|
||||
let touchData = e.touches[0]; //滑动过程中,手指滑动的坐标信息 返回的是Objcet对象
|
||||
let moveX = touchData.clientX - this.startTouchData.clientX;
|
||||
let moveY = touchData.clientY - this.startTouchData.clientY;
|
||||
if (moveY < -50) {
|
||||
if (this.vibrate && this.recording) {
|
||||
// #ifdef APP-PLUS
|
||||
plus.device.vibrate(35);
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
uni.vibrateShort();
|
||||
// #endif
|
||||
}
|
||||
this.recording = false;
|
||||
} else {
|
||||
this.recording = true;
|
||||
}
|
||||
},
|
||||
playVoice() {
|
||||
if (this._voicePath && this.playing === 0) {
|
||||
console.log('playVoice', this._voicePath)
|
||||
innerAudioContext.src = this._voicePath;
|
||||
|
||||
innerAudioContext.stop(); //todo 第一次play时若不先stop则播放不出来,未知原因
|
||||
innerAudioContext.play();
|
||||
|
||||
this.playing = 1;
|
||||
|
||||
this.recordTime = this.recordTime1;
|
||||
|
||||
this.countdownObj = setInterval(() => {
|
||||
this.recordTime--;
|
||||
if(this.recordTime === 0){
|
||||
this.recordTime = this.recordTime1;
|
||||
this.stopVoice()
|
||||
return;
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
},
|
||||
startVoice() {
|
||||
this.isShow = true;
|
||||
this.percent = 0;
|
||||
this.recordTime = 0;
|
||||
this.newViocePath = "";//音频地址
|
||||
},
|
||||
//关闭组件
|
||||
closePicker(){
|
||||
this.isShow = false;
|
||||
this.endRecord();
|
||||
this.stopVoice();
|
||||
},
|
||||
stopVoice() {
|
||||
innerAudioContext.stop();
|
||||
this.playing = 0;
|
||||
//this.recordTime = 0;
|
||||
clearInterval(this.countdownObj);
|
||||
},
|
||||
//点击确定
|
||||
okClick(){
|
||||
var data = {
|
||||
path: this._voicePath,
|
||||
sec: this.recordTime1,
|
||||
}
|
||||
this.stopVoice();
|
||||
this.$emit('okClick', data);
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
|
||||
.cbb-record {
|
||||
.conbox{
|
||||
background: #fff;
|
||||
}
|
||||
.record{
|
||||
text-align: center;
|
||||
|
||||
.time {
|
||||
text-align: center;
|
||||
font-size: 60upx;
|
||||
color: #000;
|
||||
line-height: 100upx;
|
||||
//margin-top:50upx;
|
||||
}
|
||||
.domess{margin-bottom:50upx;}
|
||||
|
||||
.c666{color:#aaa;}
|
||||
.c999{color:#999;}
|
||||
.fz28{font-size: 28upx;}
|
||||
.fz32{font-size: 32upx;}
|
||||
.record-box {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
}
|
||||
.btncom{
|
||||
width: 80upx;
|
||||
height: 80upx;
|
||||
border-radius: 80upx;
|
||||
}
|
||||
.stop{
|
||||
@extend .btncom;
|
||||
}
|
||||
.paly{
|
||||
@extend .btncom;
|
||||
}
|
||||
.confirm{
|
||||
@extend .btncom;
|
||||
}
|
||||
}
|
||||
|
||||
.circle-box {
|
||||
margin: 0 80rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.record-popup {
|
||||
position: absolute;
|
||||
bottom: var(--popup-bottom);
|
||||
left: calc(50vw - calc(var(--popup-width) / 2));
|
||||
z-index: 1;
|
||||
width: var(--popup-width);
|
||||
height: var(--popup-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10rpx;
|
||||
box-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.1);
|
||||
background: var(--popup-bg-color);
|
||||
color: #000;
|
||||
transition: 0.2s height;
|
||||
|
||||
.inner-content {
|
||||
height: var(--popup-height);
|
||||
font-size: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.tips {
|
||||
color: #999;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.voice-btn {
|
||||
width: 170rpx;
|
||||
height: 170rpx;
|
||||
}
|
||||
|
||||
.progress-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #3cc9a4;
|
||||
border-radius: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cancel-icon {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 44rpx;
|
||||
line-height: 44rpx;
|
||||
background-color: pink;
|
||||
border-radius: 50%;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.voice-line-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.voice-line {
|
||||
width: 5rpx;
|
||||
height: var(--line-height);
|
||||
border-radius: 3rpx;
|
||||
margin: 0 5rpx;
|
||||
}
|
||||
|
||||
.one {
|
||||
animation: wave 0.4s 1s linear infinite alternate;
|
||||
}
|
||||
|
||||
.two {
|
||||
animation: wave 0.4s 0.9s linear infinite alternate;
|
||||
}
|
||||
|
||||
.three {
|
||||
animation: wave 0.4s 0.8s linear infinite alternate;
|
||||
}
|
||||
|
||||
.four {
|
||||
animation: wave 0.4s 0.7s linear infinite alternate;
|
||||
}
|
||||
|
||||
.five {
|
||||
animation: wave 0.4s 0.6s linear infinite alternate;
|
||||
}
|
||||
|
||||
.six {
|
||||
animation: wave 0.4s 0.5s linear infinite alternate;
|
||||
}
|
||||
|
||||
.seven {
|
||||
animation: wave 0.4s linear infinite alternate;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wave {
|
||||
0% {
|
||||
transform: scale(1, 1);
|
||||
background-color: var(--line-start-color);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1, 0.2);
|
||||
background-color: var(--line-end-color);
|
||||
}
|
||||
}
|
||||
</style>
|
226
uni_modules/all-speech/js_sdk/h5-speech/speech.js
Normal file
226
uni_modules/all-speech/js_sdk/h5-speech/speech.js
Normal file
@@ -0,0 +1,226 @@
|
||||
class Recoder {
|
||||
constructor(sampleRate) {
|
||||
this.leftDataList = []
|
||||
this.rightDataList = []
|
||||
this.mediaPlayer = null
|
||||
this.audioContext = null
|
||||
this.source = null
|
||||
this.sampleRate = sampleRate || 44100
|
||||
}
|
||||
start() {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
sampleRate: 8000, // 采样率
|
||||
channelCount: 1, // 声道
|
||||
audioBitsPerSecond: 64,
|
||||
volume: 1.0, // 音量
|
||||
autoGainControl: true
|
||||
}
|
||||
}).then(mediaStream => {
|
||||
console.log(mediaStream, 'mediaStream')
|
||||
this.mediaPlayer = mediaStream
|
||||
this.beginRecord(mediaStream)
|
||||
resolve()
|
||||
}).catch(err => {
|
||||
// 如果用户电脑没有麦克风设备或者用户拒绝了,或者连接出问题了等
|
||||
// 这里都会抛异常,并且通过err.name可以知道是哪种类型的错误
|
||||
console.error(err)
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
beginRecord(mediaStream) {
|
||||
let audioContext = new(window.AudioContext || window.webkitAudioContext)()
|
||||
// mediaNode包含 mediaStream,audioContext
|
||||
let mediaNode = audioContext.createMediaStreamSource(mediaStream)
|
||||
console.log(mediaNode, 'mediaNode')
|
||||
// 创建一个jsNode
|
||||
// audioContext.sampleRate = 8000
|
||||
console.log(audioContext, 'audioContext')
|
||||
let jsNode = this.createJSNode(audioContext)
|
||||
console.log(jsNode, 'jsnode')
|
||||
// 需要连到扬声器消费掉outputBuffer,process回调才能触发
|
||||
// 并且由于不给outputBuffer设置内容,所以扬声器不会播放出声音
|
||||
jsNode.connect(audioContext.destination)
|
||||
jsNode.onaudioprocess = this.onAudioProcess.bind(this)
|
||||
// 把mediaNode连接到jsNode
|
||||
mediaNode.connect(jsNode)
|
||||
this.audioContext = audioContext
|
||||
}
|
||||
|
||||
onAudioProcess(event) {
|
||||
console.log('is recording')
|
||||
// 拿到输入buffer Float32Array
|
||||
let audioBuffer = event.inputBuffer
|
||||
let leftChannelData = audioBuffer.getChannelData(0)
|
||||
// let rightChannelData = audioBuffer.getChannelData(1)
|
||||
|
||||
// 需要克隆一下
|
||||
this.leftDataList.push(leftChannelData.slice(0))
|
||||
//this.rightDataList.push(rightChannelData.slice(0))
|
||||
}
|
||||
|
||||
createJSNode(audioContext) {
|
||||
const BUFFER_SIZE = 4096
|
||||
const INPUT_CHANNEL_COUNT = 1
|
||||
const OUTPUT_CHANNEL_COUNT = 1
|
||||
// createJavaScriptNode已被废弃
|
||||
let creator = audioContext.createScriptProcessor || audioContext.createJavaScriptNode
|
||||
creator = creator.bind(audioContext)
|
||||
return creator(BUFFER_SIZE, INPUT_CHANNEL_COUNT, OUTPUT_CHANNEL_COUNT)
|
||||
}
|
||||
|
||||
playRecord(arrayBuffer) {
|
||||
let blob = new Blob([new Int8Array(arrayBuffer)], {
|
||||
type: 'audio/mp3' // files[0].type
|
||||
})
|
||||
let blobUrl = URL.createObjectURL(blob)
|
||||
this.source = blob
|
||||
this.blobUrl = blobUrl
|
||||
// document.querySelector(‘.audio-node‘).src = blobUrl
|
||||
return blobUrl
|
||||
}
|
||||
|
||||
stop() {
|
||||
// 停止录音
|
||||
let leftData = this.mergeArray(this.leftDataList)
|
||||
//let rightData = this.mergeArray(this.rightDataList)
|
||||
let allData = this.interSingleData(leftData)
|
||||
let wavBuffer = this.createWavFile(allData)
|
||||
|
||||
// 转到播放
|
||||
let source = this.playRecord(wavBuffer)
|
||||
// if (source) {
|
||||
// source = source.slice(5)
|
||||
// }
|
||||
console.log("我最后转换完播放的---------------------", source);
|
||||
this.resetRecord()
|
||||
return source
|
||||
}
|
||||
|
||||
transformArrayBufferToBase64(buffer) {
|
||||
var binary = ''
|
||||
var bytes = new Uint8Array(buffer)
|
||||
for (var len = bytes.byteLength, i = 0; i < len; i++) {
|
||||
binary += String.fromCharCode(bytes[i])
|
||||
}
|
||||
return window.btoa(binary)
|
||||
}
|
||||
|
||||
// 停止控件录音
|
||||
resetRecord() {
|
||||
this.leftDataList = []
|
||||
this.rightDataList = []
|
||||
this.audioContext.close()
|
||||
this.mediaPlayer.getAudioTracks().forEach(track => {
|
||||
track.stop()
|
||||
this.mediaPlayer.removeTrack(track)
|
||||
})
|
||||
}
|
||||
|
||||
createWavFile(audioData) {
|
||||
let channelCount = 1
|
||||
const WAV_HEAD_SIZE = 44
|
||||
const sampleBits = 16
|
||||
let sampleRate = this.sampleRate
|
||||
|
||||
let buffer = new ArrayBuffer(audioData.length * 2 + WAV_HEAD_SIZE)
|
||||
// 需要用一个view来操控buffer
|
||||
let view = new DataView(buffer)
|
||||
// 写入wav头部信息
|
||||
// RIFF chunk descriptor/identifier
|
||||
this.writeUTFBytes(view, 0, 'RIFF')
|
||||
// RIFF chunk length
|
||||
view.setUint32(4, 44 + audioData.length * channelCount, true)
|
||||
// RIFF type
|
||||
this.writeUTFBytes(view, 8, 'WAVE')
|
||||
// format chunk identifier
|
||||
// FMT sub-chunk
|
||||
this.writeUTFBytes(view, 12, 'fmt ')
|
||||
// format chunk length
|
||||
view.setUint32(16, 16, true)
|
||||
// sample format (raw)
|
||||
view.setUint16(20, 1, true)
|
||||
// stereo (2 channels)
|
||||
view.setUint16(22, channelCount, true)
|
||||
// sample rate
|
||||
view.setUint32(24, sampleRate, true)
|
||||
// byte rate (sample rate * block align)
|
||||
view.setUint32(28, sampleRate * 2, true)
|
||||
// block align (channel count * bytes per sample)
|
||||
view.setUint16(32, 2 * 2, true)
|
||||
// bits per sample
|
||||
view.setUint16(34, 16, true)
|
||||
// data sub-chunk
|
||||
// data chunk identifier
|
||||
this.writeUTFBytes(view, 36, 'data')
|
||||
// data chunk length
|
||||
view.setUint32(40, audioData.length * 2, true)
|
||||
|
||||
console.log(view, 'view')
|
||||
let length = audioData.length
|
||||
let index = 44
|
||||
let volume = 1
|
||||
for (let i = 0; i < length; i++) {
|
||||
view.setInt16(index, audioData[i] * (0x7FFF * volume), true)
|
||||
index += 2
|
||||
}
|
||||
return buffer
|
||||
}
|
||||
|
||||
writeUTFBytes(view, offset, string) {
|
||||
var lng = string.length
|
||||
for (var i = 0; i < lng; i++) {
|
||||
view.setUint8(offset + i, string.charCodeAt(i))
|
||||
}
|
||||
}
|
||||
|
||||
interSingleData(left) {
|
||||
var t = left.length;
|
||||
let sampleRate = this.audioContext.sampleRate,
|
||||
outputSampleRate = this.sampleRate
|
||||
sampleRate += 0.0;
|
||||
outputSampleRate += 0.0;
|
||||
var s = 0,
|
||||
o = sampleRate / outputSampleRate,
|
||||
u = Math.ceil(t * outputSampleRate / sampleRate),
|
||||
a = new Float32Array(u);
|
||||
for (let i = 0; i < u; i++) {
|
||||
a[i] = left[Math.floor(s)];
|
||||
s += o;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
// 交叉合并左右声道的数据
|
||||
interleaveLeftAndRight(left, right) {
|
||||
let totalLength = left.length + right.length
|
||||
let data = new Float32Array(totalLength)
|
||||
for (let i = 0; i < left.length; i++) {
|
||||
let k = i * 2
|
||||
data[k] = left[i]
|
||||
data[k + 1] = right[i]
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
mergeArray(list) {
|
||||
let length = list.length * list[0].length
|
||||
let data = new Float32Array(length)
|
||||
let offset = 0
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
data.set(list[i], offset)
|
||||
offset += list[i].length
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default Recoder
|
272
uni_modules/all-speech/js_sdk/wa-permission/permission.js
Normal file
272
uni_modules/all-speech/js_sdk/wa-permission/permission.js
Normal file
@@ -0,0 +1,272 @@
|
||||
/**
|
||||
* 本模块封装了Android、iOS的应用权限判断、打开应用权限设置界面、以及位置系统服务是否开启
|
||||
*/
|
||||
|
||||
var isIos
|
||||
// #ifdef APP-PLUS
|
||||
isIos = (plus.os.name == "iOS")
|
||||
// #endif
|
||||
|
||||
// 判断推送权限是否开启
|
||||
function judgeIosPermissionPush() {
|
||||
var result = false;
|
||||
var UIApplication = plus.ios.import("UIApplication");
|
||||
var app = UIApplication.sharedApplication();
|
||||
var enabledTypes = 0;
|
||||
if (app.currentUserNotificationSettings) {
|
||||
var settings = app.currentUserNotificationSettings();
|
||||
enabledTypes = settings.plusGetAttribute("types");
|
||||
console.log("enabledTypes1:" + enabledTypes);
|
||||
if (enabledTypes == 0) {
|
||||
console.log("推送权限没有开启");
|
||||
} else {
|
||||
result = true;
|
||||
console.log("已经开启推送功能!")
|
||||
}
|
||||
plus.ios.deleteObject(settings);
|
||||
} else {
|
||||
enabledTypes = app.enabledRemoteNotificationTypes();
|
||||
if (enabledTypes == 0) {
|
||||
console.log("推送权限没有开启!");
|
||||
} else {
|
||||
result = true;
|
||||
console.log("已经开启推送功能!")
|
||||
}
|
||||
console.log("enabledTypes2:" + enabledTypes);
|
||||
}
|
||||
plus.ios.deleteObject(app);
|
||||
plus.ios.deleteObject(UIApplication);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断定位权限是否开启
|
||||
function judgeIosPermissionLocation() {
|
||||
var result = false;
|
||||
var cllocationManger = plus.ios.import("CLLocationManager");
|
||||
var status = cllocationManger.authorizationStatus();
|
||||
result = (status != 2)
|
||||
console.log("定位权限开启:" + result);
|
||||
// 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation
|
||||
/* var enable = cllocationManger.locationServicesEnabled();
|
||||
var status = cllocationManger.authorizationStatus();
|
||||
console.log("enable:" + enable);
|
||||
console.log("status:" + status);
|
||||
if (enable && status != 2) {
|
||||
result = true;
|
||||
console.log("手机定位服务已开启且已授予定位权限");
|
||||
} else {
|
||||
console.log("手机系统的定位没有打开或未给予定位权限");
|
||||
} */
|
||||
plus.ios.deleteObject(cllocationManger);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断麦克风权限是否开启
|
||||
function judgeIosPermissionRecord() {
|
||||
var result = false;
|
||||
var avaudiosession = plus.ios.import("AVAudioSession");
|
||||
var avaudio = avaudiosession.sharedInstance();
|
||||
var permissionStatus = avaudio.recordPermission();
|
||||
console.log("permissionStatus:" + permissionStatus);
|
||||
if (permissionStatus == 1684369017 || permissionStatus == 1970168948) {
|
||||
console.log("麦克风权限没有开启");
|
||||
} else {
|
||||
result = true;
|
||||
console.log("麦克风权限已经开启");
|
||||
}
|
||||
plus.ios.deleteObject(avaudiosession);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断相机权限是否开启
|
||||
function judgeIosPermissionCamera() {
|
||||
var result = false;
|
||||
var AVCaptureDevice = plus.ios.import("AVCaptureDevice");
|
||||
var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
|
||||
console.log("authStatus:" + authStatus);
|
||||
if (authStatus == 3) {
|
||||
result = true;
|
||||
console.log("相机权限已经开启");
|
||||
} else {
|
||||
console.log("相机权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(AVCaptureDevice);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断相册权限是否开启
|
||||
function judgeIosPermissionPhotoLibrary() {
|
||||
var result = false;
|
||||
var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary");
|
||||
var authStatus = PHPhotoLibrary.authorizationStatus();
|
||||
console.log("authStatus:" + authStatus);
|
||||
if (authStatus == 3) {
|
||||
result = true;
|
||||
console.log("相册权限已经开启");
|
||||
} else {
|
||||
console.log("相册权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(PHPhotoLibrary);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断通讯录权限是否开启
|
||||
function judgeIosPermissionContact() {
|
||||
var result = false;
|
||||
var CNContactStore = plus.ios.import("CNContactStore");
|
||||
var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);
|
||||
if (cnAuthStatus == 3) {
|
||||
result = true;
|
||||
console.log("通讯录权限已经开启");
|
||||
} else {
|
||||
console.log("通讯录权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(CNContactStore);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断日历权限是否开启
|
||||
function judgeIosPermissionCalendar() {
|
||||
var result = false;
|
||||
var EKEventStore = plus.ios.import("EKEventStore");
|
||||
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);
|
||||
if (ekAuthStatus == 3) {
|
||||
result = true;
|
||||
console.log("日历权限已经开启");
|
||||
} else {
|
||||
console.log("日历权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(EKEventStore);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 判断备忘录权限是否开启
|
||||
function judgeIosPermissionMemo() {
|
||||
var result = false;
|
||||
var EKEventStore = plus.ios.import("EKEventStore");
|
||||
var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);
|
||||
if (ekAuthStatus == 3) {
|
||||
result = true;
|
||||
console.log("备忘录权限已经开启");
|
||||
} else {
|
||||
console.log("备忘录权限没有开启");
|
||||
}
|
||||
plus.ios.deleteObject(EKEventStore);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Android权限查询
|
||||
function requestAndroidPermission(permissionID) {
|
||||
return new Promise((resolve, reject) => {
|
||||
plus.android.requestPermissions(
|
||||
[permissionID], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装
|
||||
function(resultObj) {
|
||||
var result = 0;
|
||||
for (var i = 0; i < resultObj.granted.length; i++) {
|
||||
var grantedPermission = resultObj.granted[i];
|
||||
console.log('已获取的权限:' + grantedPermission);
|
||||
result = 1
|
||||
}
|
||||
for (var i = 0; i < resultObj.deniedPresent.length; i++) {
|
||||
var deniedPresentPermission = resultObj.deniedPresent[i];
|
||||
console.log('拒绝本次申请的权限:' + deniedPresentPermission);
|
||||
result = 0
|
||||
}
|
||||
for (var i = 0; i < resultObj.deniedAlways.length; i++) {
|
||||
var deniedAlwaysPermission = resultObj.deniedAlways[i];
|
||||
console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
|
||||
result = -1
|
||||
}
|
||||
resolve(result);
|
||||
// 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限
|
||||
// if (result != 1) {
|
||||
// gotoAppPermissionSetting()
|
||||
// }
|
||||
},
|
||||
function(error) {
|
||||
console.log('申请权限错误:' + error.code + " = " + error.message);
|
||||
resolve({
|
||||
code: error.code,
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 使用一个方法,根据参数判断权限
|
||||
function judgeIosPermission(permissionID) {
|
||||
if (permissionID == "location") {
|
||||
return judgeIosPermissionLocation()
|
||||
} else if (permissionID == "camera") {
|
||||
return judgeIosPermissionCamera()
|
||||
} else if (permissionID == "photoLibrary") {
|
||||
return judgeIosPermissionPhotoLibrary()
|
||||
} else if (permissionID == "record") {
|
||||
return judgeIosPermissionRecord()
|
||||
} else if (permissionID == "push") {
|
||||
return judgeIosPermissionPush()
|
||||
} else if (permissionID == "contact") {
|
||||
return judgeIosPermissionContact()
|
||||
} else if (permissionID == "calendar") {
|
||||
return judgeIosPermissionCalendar()
|
||||
} else if (permissionID == "memo") {
|
||||
return judgeIosPermissionMemo()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 跳转到**应用**的权限页面
|
||||
function gotoAppPermissionSetting() {
|
||||
if (isIos) {
|
||||
var UIApplication = plus.ios.import("UIApplication");
|
||||
var application2 = UIApplication.sharedApplication();
|
||||
var NSURL2 = plus.ios.import("NSURL");
|
||||
// var setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
|
||||
var setting2 = NSURL2.URLWithString("app-settings:");
|
||||
application2.openURL(setting2);
|
||||
|
||||
plus.ios.deleteObject(setting2);
|
||||
plus.ios.deleteObject(NSURL2);
|
||||
plus.ios.deleteObject(application2);
|
||||
} else {
|
||||
// console.log(plus.device.vendor);
|
||||
var Intent = plus.android.importClass("android.content.Intent");
|
||||
var Settings = plus.android.importClass("android.provider.Settings");
|
||||
var Uri = plus.android.importClass("android.net.Uri");
|
||||
var mainActivity = plus.android.runtimeMainActivity();
|
||||
var intent = new Intent();
|
||||
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
var uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
|
||||
intent.setData(uri);
|
||||
mainActivity.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查系统的设备服务是否开启
|
||||
// var checkSystemEnableLocation = async function () {
|
||||
function checkSystemEnableLocation() {
|
||||
if (isIos) {
|
||||
var result = false;
|
||||
var cllocationManger = plus.ios.import("CLLocationManager");
|
||||
var result = cllocationManger.locationServicesEnabled();
|
||||
console.log("系统定位开启:" + result);
|
||||
plus.ios.deleteObject(cllocationManger);
|
||||
return result;
|
||||
} else {
|
||||
var context = plus.android.importClass("android.content.Context");
|
||||
var locationManager = plus.android.importClass("android.location.LocationManager");
|
||||
var main = plus.android.runtimeMainActivity();
|
||||
var mainSvr = main.getSystemService(context.LOCATION_SERVICE);
|
||||
var result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER);
|
||||
console.log("系统定位开启:" + result);
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
judgeIosPermission: judgeIosPermission,
|
||||
requestAndroidPermission: requestAndroidPermission,
|
||||
checkSystemEnableLocation: checkSystemEnableLocation,
|
||||
gotoAppPermissionSetting: gotoAppPermissionSetting
|
||||
}
|
82
uni_modules/all-speech/package.json
Normal file
82
uni_modules/all-speech/package.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"id": "all-speech",
|
||||
"displayName": "allspeech长按录音动画组件,多端权限判断,可监听开始、结束、取消事件",
|
||||
"version": "1.1.1",
|
||||
"description": "https://ext.dcloud.net.cn/plugin?id=9595这个是原项目,本人再次基础上兼容了H5",
|
||||
"keywords": [
|
||||
"长按,录音,动画组件"
|
||||
],
|
||||
"repository": "",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"type": "component-vue",
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": ""
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "https://gitee.com/imboya/nb-voice-record"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y",
|
||||
"alipay": "n"
|
||||
},
|
||||
"client": {
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "y"
|
||||
},
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "u",
|
||||
"Android Browser": "u",
|
||||
"微信浏览器(Android)": "n",
|
||||
"QQ浏览器(Android)": "n"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "y",
|
||||
"IE": "n",
|
||||
"Edge": "n",
|
||||
"Firefox": "y",
|
||||
"Safari": "n"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "u",
|
||||
"百度": "u",
|
||||
"字节跳动": "u",
|
||||
"QQ": "u",
|
||||
"钉钉": "u",
|
||||
"快手": "u",
|
||||
"飞书": "u",
|
||||
"京东": "u"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
89
uni_modules/all-speech/readme.md
Normal file
89
uni_modules/all-speech/readme.md
Normal file
@@ -0,0 +1,89 @@
|
||||
### nbVoiceRecord概述
|
||||
- 这是个基于uni-app 符合uni_modules 的插件
|
||||
- 无任何依赖、纯css动画
|
||||
- nb是NeverBug的意思
|
||||
|
||||
### 主要功能
|
||||
- 长按组件后弹出录音弹窗,松手完成录音,手指向上滑动可取消;
|
||||
- 支持各种自定义,如弹窗高度、宽度、各处文字甚至声纹波形的尺寸和颜色;
|
||||
- 已完成多端适配,自动根据授权情况提示完成授权、已获得授权才开始录音
|
||||
- endRecord回调事件附带录音文件
|
||||
|
||||
### 动画预览
|
||||
|
||||
- 默认样式
|
||||
|
||||

|
||||
|
||||
- 自定义按钮为圆形(红背景、白字)、弹窗为正方形
|
||||
|
||||

|
||||
|
||||
### 基本用法:
|
||||
|
||||
```
|
||||
<template>
|
||||
<view>
|
||||
<all-speech @startRecord="start" @endRecord="end" @cancelRecord="cancel"></all-speech>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
methods: {
|
||||
start() {
|
||||
// 开始录音
|
||||
},
|
||||
end(event) {
|
||||
// 结束录音并处理得到的录音文件
|
||||
// event中,app端仅有tempFilePath字段,微信小程序还有duration和fileSize两个字段
|
||||
},
|
||||
cancel() {
|
||||
// 用户取消录音
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
```
|
||||
|
||||
### 全部支持参数
|
||||
|
||||
| 参数名 | 类型 | 默认值 | 作用 | 注意事项 |
|
||||
| ----- | ----- | ------ | ------- | --- |
|
||||
| recordOptions | Object | {duration:60000} | 录音配置 |各端支持情况不同,请自行查看[官方说明](https://uniapp.dcloud.net.cn/api/media/record-manager.html#getrecordermanager) |
|
||||
| btnStyle | Object | 请查看源码 | 按钮样式 |对象格式 |
|
||||
| btnHoverFontcolor | String | #000 | 按钮长按时文字颜色 | |
|
||||
| btnHoverBgcolor | String | whitesmoke | 按钮长按时背景颜色 | |
|
||||
| btnDefaultText | String | 长按开始录音 | 初始按钮文字 | |
|
||||
| btnRecordingText | String | 录音中 | 录制时按钮文字 | |
|
||||
| vibrate | Boolean | true | 震动反馈 | 弹窗、滑动取消时 |
|
||||
| popupTitle | String | 正在录制音频 | 弹窗顶部文字 | |
|
||||
| popupDefaultTips | String | 松手完成录音 | 录制时弹窗底部提示 | |
|
||||
| popupCancelTips | String | 松手取消录音 | 滑动取消时弹窗底部提示 | |
|
||||
| popupMaxWidth | Number | 600 | 弹窗展开后宽度 |注意这里几个单位都是rpx |
|
||||
| popupMaxHeight | Number | 300 | 弹窗展开后高度 | |
|
||||
| popupFixBottom | Number | 200 | 弹窗展开后距底部高度 | |
|
||||
| popupBgColor | String | whitesmoke | 弹窗背景颜色 | |
|
||||
| lineHeight | Number | 50 | 声波高度 | |
|
||||
| lineStartColor | String | royalblue | 声波波谷时颜色色值 | 色值或者颜色名均可 |
|
||||
| lineEndColor | String | indianred | 声波波峰时颜色色值 | |
|
||||
|
||||
### 原作者其他插件
|
||||
|
||||
- [bwinBrand多端自适应企业官网、uniCloud云端一体【用户端】](https://ext.dcloud.net.cn/plugin?id=7821)
|
||||
- [bwinBrand多端自适应企业官网、uniCloud云端一体【管理端】](https://ext.dcloud.net.cn/plugin?id=7822)
|
||||
- [bwinAgent多端、多项目全民经纪人、uniCloud云端一体【经纪人端】](https://ext.dcloud.net.cn/plugin?id=8606)
|
||||
- [bwinAgent多端、多项目全民经纪人、uniCloud云端一体【管理员端】](https://ext.dcloud.net.cn/plugin?id=8607)
|
||||
- [必闻优学,教育培训机构模板(单校区版,纯模板)](https://ext.dcloud.net.cn/plugin?id=7709)
|
||||
|
||||
### 一个有趣的社区
|
||||
|
||||
- [NeverBug.cn 弹幕式互动社区](https://neverbug.cn)
|
||||
|
||||
### 原作者
|
||||
- QQ:123060128
|
||||
- Email:karma.zhao@gmail.com
|
||||
- 官网:https://brand.neverbug.cn
|
||||
|
||||
### 补充作者
|
||||
- QQ:27836407
|
||||
- Email:zgdabao.zhao@gmail.com
|
Reference in New Issue
Block a user