277 lines
5.7 KiB
Vue
277 lines
5.7 KiB
Vue
<!-- 语音 -->
|
||
<template>
|
||
<view class="voice_box" @touchstart="sv.touchstartVoice" @touchmove.stop.prevent="sv.touchmoveVoice" @touchend="sv.touchendVoice" @touchcancel="sv.touchcancelVoice">
|
||
<text class="voice_text c000">{{ voiceText }}</text>
|
||
</view>
|
||
|
||
<!-- 语音状态显示 -->
|
||
<template v-if="voiceFlg">
|
||
<!-- 录音UI效果 -->
|
||
<view class="record" :class="voiceFlg ? '' : 'hidden'">
|
||
<uni-icons :class="[voiceStop ? 'cancel' : 'ing']" :type="voiceStop ? 'micoff' : 'mic'" size="100" />
|
||
<view class="tis" :class="voiceStop ? 'change' : ''">{{ voiceTis }}</view>
|
||
</view>
|
||
</template>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
ref,
|
||
defineEmits
|
||
} from 'vue'
|
||
// 腾讯云聊天
|
||
import TencentCloudChat from '@tencentcloud/chat';
|
||
const props = defineProps({
|
||
msg: Object,
|
||
})
|
||
const emit = defineEmits(['send'])
|
||
//录音
|
||
// #ifdef APP-PLUS
|
||
const recorderManager = uni.getRecorderManager();
|
||
// #endif
|
||
// 录音时长
|
||
const voiceLength = ref(0);
|
||
// 录音定时器
|
||
const voiceTimer = ref(null);
|
||
// 录音文本
|
||
const voiceText = ref('按住 说话');
|
||
// 录音提示
|
||
const voiceTis = ref('手指上滑 取消发送');
|
||
// 录音图标显示
|
||
const voiceFlg = ref(false);
|
||
// 录音开始Y坐标
|
||
const voicePageY = ref(0);
|
||
// 录音结束
|
||
const voiceStop = ref(false);
|
||
const str = '';
|
||
// 录音相关
|
||
const sv = {
|
||
// 按下触发
|
||
touchstartVoice: (e) => {
|
||
voicePageY.value = (e.changedTouches[0].pageY).toFixed(2);
|
||
recorderManager.start({
|
||
duration: 60000, // 录音的时长,单位 ms,最大值 600000(10 分钟)
|
||
sampleRate: 44100, // 采样率
|
||
numberOfChannels: 1, // 录音通道数
|
||
encodeBitRate: 192000, // 编码码率
|
||
format: "mp3"
|
||
});
|
||
voiceLength.value = 0;
|
||
voiceFlg.value = true
|
||
console.log('recorder start success');
|
||
//录音开始,
|
||
voiceTimer.value = setInterval(() => {
|
||
voiceLength.value += 0.1;
|
||
}, 100);
|
||
|
||
console.log('touchstartVoice', voicePageY.value);
|
||
},
|
||
// 滑动触发
|
||
touchmoveVoice: (e) => {
|
||
// 没有展示UI不触发
|
||
if (!voiceFlg.value) {
|
||
return;
|
||
}
|
||
let numTemp = voicePageY.value - ((e.changedTouches[0].pageY).toFixed(2));
|
||
if (numTemp >= 60) {
|
||
console.log('取消发送');
|
||
voiceStop.value = true
|
||
voiceTis.value = '松开手指 取消发送'
|
||
} else {
|
||
console.log('继续发送');
|
||
voiceStop.value = false
|
||
voiceTis.value = '手指上滑 取消发送'
|
||
}
|
||
},
|
||
// 松开触发
|
||
touchendVoice: () => {
|
||
// 没有展示UI不触发
|
||
if (!voiceFlg.value) {
|
||
return;
|
||
}
|
||
clearInterval(voiceTimer.value);
|
||
voiceText.value = '按住 说话'
|
||
voiceTis.value = "手指上滑 取消发送"
|
||
console.log('touchendVoice');
|
||
sv.stop();
|
||
},
|
||
// 打断触发
|
||
touchcancelVoice: () => {
|
||
clearInterval(voiceTimer.value);
|
||
// 关闭UI
|
||
voiceText.value = '按住 说话'
|
||
voiceTis.value = "手指上滑 取消发送"
|
||
// 不发送语音
|
||
voiceStop.value = true
|
||
console.log('touchcancelVoice');
|
||
sv.stop();
|
||
},
|
||
stop: () => {
|
||
voiceTimer.value = null;
|
||
voiceFlg.value = false
|
||
recorderManager.stop(); // 录音结束
|
||
console.log('录音结束');
|
||
}
|
||
};
|
||
// #ifdef APP-PLUS
|
||
// 监听录音停止事件
|
||
recorderManager.onStop((res) => {
|
||
// 被打断等情况不发送
|
||
if (voiceStop.value) {
|
||
return
|
||
}
|
||
// 正常情况
|
||
if (voiceStop.value) {
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: "取消发送",
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
if (voiceLength.value < 1) {
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: "语音时长过短",
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
if (voiceLength.value > 60) {
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: "语音时长过长",
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
console.log('file', res)
|
||
|
||
try {
|
||
let message = uni.$chat.createAudioMessage({
|
||
to: props.msg.id,
|
||
conversationType: props.msg.type,
|
||
payload: {
|
||
file: res
|
||
},
|
||
// 音频上传进度回调
|
||
onProgress: function(event) {
|
||
console.log('file uploading:', event)
|
||
}
|
||
})
|
||
emit('send', message)
|
||
} catch (e) {
|
||
console.log('message catch', e)
|
||
}
|
||
//
|
||
|
||
})
|
||
// #endif
|
||
</script>
|
||
<style scoped lang="scss">
|
||
.voice_box {
|
||
padding: 20rpx 0;
|
||
margin: 0 20rpx;
|
||
border-radius: 50rpx;
|
||
background: #fff;
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.hidden {
|
||
display: none !important;
|
||
}
|
||
|
||
.record {
|
||
width: 40vw;
|
||
height: 40vw;
|
||
position: fixed;
|
||
top: 55%;
|
||
left: 30%;
|
||
background-color: rgba(0, 0, 0, .6);
|
||
border-radius: 20rpx;
|
||
|
||
.ing {
|
||
width: 100%;
|
||
height: 30vw;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
// 模拟录音音效动画
|
||
@keyframes volatility {
|
||
0% {
|
||
background-position: 0% 130%;
|
||
}
|
||
|
||
20% {
|
||
background-position: 0% 150%;
|
||
}
|
||
|
||
30% {
|
||
background-position: 0% 155%;
|
||
}
|
||
|
||
40% {
|
||
background-position: 0% 150%;
|
||
}
|
||
|
||
50% {
|
||
background-position: 0% 145%;
|
||
}
|
||
|
||
70% {
|
||
background-position: 0% 150%;
|
||
}
|
||
|
||
80% {
|
||
background-position: 0% 155%;
|
||
}
|
||
|
||
90% {
|
||
background-position: 0% 140%;
|
||
}
|
||
|
||
100% {
|
||
background-position: 0% 135%;
|
||
}
|
||
}
|
||
|
||
background-image: linear-gradient(to bottom, #f09b37, #fff 50%);
|
||
background-size: 100% 200%;
|
||
animation: volatility 1.5s ease-in-out -1.5s infinite alternate;
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
font-size: 150rpx;
|
||
color: #f09b37;
|
||
|
||
}
|
||
|
||
.cancel {
|
||
width: 100%;
|
||
height: 30vw;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
color: #fff;
|
||
font-size: 150rpx;
|
||
|
||
}
|
||
|
||
.tis {
|
||
width: 100%;
|
||
height: 10vw;
|
||
display: flex;
|
||
justify-content: center;
|
||
font-size: 28rpx;
|
||
color: #fff;
|
||
|
||
&.change {
|
||
color: #f09b37;
|
||
}
|
||
}
|
||
}
|
||
</style> |