390 lines
9.9 KiB
Vue
390 lines
9.9 KiB
Vue
<template>
|
||
<view>
|
||
<!-- #ifdef APP-PLUS ||MP-WEIXIN -->
|
||
|
||
<view class="record-btn" @longpress="startRecord" @touchend="endRecord" hover-class="record-btn-hover"
|
||
hover-start-time="200" hover-stay-time="150" :style="[
|
||
btnStyle,
|
||
{
|
||
'--btn-hover-fontcolor': btnHoverFontcolor,
|
||
'--btn-hover-bgcolor': btnHoverBgcolor
|
||
}
|
||
]">
|
||
{{ btnTextContent }}
|
||
</view>
|
||
<!-- #endif -->
|
||
|
||
<view class="record-popup" :style="{
|
||
'--popup-height': popupHeight,
|
||
'--popup-width': upx2px(popupMaxWidth),
|
||
'--popup-bottom': upx2px(popupFixBottom),
|
||
'--popup-bg-color': popupBgColor
|
||
}">
|
||
<view class="inner-content" v-if="recordPopupShow">
|
||
<view class="title">{{ popupTitle }}</view>
|
||
<view class="voice-line-wrap" v-if="recording" :style="{
|
||
'--line-height': upx2px(lineHeight),
|
||
'--line-start-color': lineStartColor,
|
||
'--line-end-color': lineEndColor
|
||
}">
|
||
<view class="voice-line one"></view>
|
||
<view class="voice-line two"></view>
|
||
<view class="voice-line three"></view>
|
||
<view class="voice-line four"></view>
|
||
<view class="voice-line five"></view>
|
||
<view class="voice-line six"></view>
|
||
<view class="voice-line seven"></view>
|
||
<view class="voice-line six"></view>
|
||
<view class="voice-line five"></view>
|
||
<view class="voice-line four"></view>
|
||
<view class="voice-line three"></view>
|
||
<view class="voice-line two"></view>
|
||
<view class="voice-line one"></view>
|
||
</view>
|
||
<view class="cancel-icon" v-if="!recording">+</view>
|
||
<view class="tips">{{ recording ? popupDefaultTips : popupCancelTips }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
<script>
|
||
var that;
|
||
// #ifdef APP-PLUS || MP-WEIXIN
|
||
const recorderManager = uni.getRecorderManager();
|
||
// #endif
|
||
|
||
// #ifdef APP-PLUS
|
||
// 引入权限判断
|
||
import permision from '../../js_sdk/wa-permission/permission.js';
|
||
// #endif
|
||
export default {
|
||
name: 'nbVoiceRecord',
|
||
/**
|
||
* 录音交互动效组件
|
||
* @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: '#409EFF',
|
||
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: false
|
||
},
|
||
// #ifdef APP-PLUS || MP-WEIXIN
|
||
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进制色值
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
stopStatus: true, // 是否已被父页面通知主动结束录音
|
||
btnTextContent: this.btnDefaultText,
|
||
startTouchData: {},
|
||
popupHeight: '0px', // 这是初始的高度
|
||
recording: true, // 录音中
|
||
recordPopupShow: false,
|
||
recordTimeout: null, // 录音定时器
|
||
h5start: false
|
||
};
|
||
},
|
||
created() {
|
||
// 请求权限
|
||
this.checkPermission();
|
||
},
|
||
computed: {},
|
||
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 MP-WEIXIN
|
||
uni.authorize({
|
||
scope: 'scope.record',
|
||
success(e) {
|
||
that.permisionState = true;
|
||
// that.startRecord();
|
||
},
|
||
fail() {
|
||
uni.showToast({
|
||
title: '请授权使用录音',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
// #endif
|
||
},
|
||
|
||
startRecord() {
|
||
if (!this.permisionState) {
|
||
this.checkPermission();
|
||
return;
|
||
}
|
||
this.stopStatus = false;
|
||
setTimeout(() => {
|
||
this.popupHeight = this.upx2px(this.popupMaxHeight);
|
||
setTimeout(() => {
|
||
this.recordPopupShow = true;
|
||
this.btnTextContent = this.btnRecordingText;
|
||
// 开始录音
|
||
// recorderManager.start(this.recordOptions);
|
||
// 设置定时器
|
||
this.recordTimeout = setTimeout(
|
||
() => {
|
||
// 如果定时器先结束,则说明此时录音时间超限
|
||
this.stopRecord(); // 结束录音动画(实际上录音的end回调已经先执行)
|
||
this.recordTimeout = null; // 重置
|
||
},
|
||
this.recordOptions.duration ? this.recordOptions.duration : 600000
|
||
);
|
||
|
||
this.$emit('startRecord');
|
||
}, 100);
|
||
}, 200);
|
||
},
|
||
endRecord() {
|
||
var that = this;
|
||
if (this.stopStatus) {
|
||
return;
|
||
}
|
||
this.popupHeight = '0px';
|
||
this.recordPopupShow = false;
|
||
this.btnTextContent = this.btnDefaultText;
|
||
this.$emit('endRecord');
|
||
},
|
||
stopRecord() {
|
||
// 用法如你录音限制了时间,那么将在结束时强制停止组件的显示
|
||
this.endRecord();
|
||
this.stopStatus = true;
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
<style lang="scss">
|
||
.record-btn {
|
||
color: #fff;
|
||
font-size: 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: 0.25s all;
|
||
border: 1rpx solid whitesmoke;
|
||
}
|
||
|
||
.record-btn-hover {
|
||
color: var(--btn-hover-fontcolor) !important;
|
||
background-color: var(--btn-hover-bgcolor) !important;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
}
|
||
}
|
||
|
||
.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> |