项目初始化
This commit is contained in:
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>
|
Reference in New Issue
Block a user