9 changed files with 497 additions and 52 deletions
@ -0,0 +1,62 @@ |
|||
import UNI_APP from '@/.env.js'; |
|||
|
|||
const rc = uni.getRecorderManager(); |
|||
// 录音开始时间
|
|||
let startTime = null; |
|||
|
|||
let start = () => { |
|||
return new Promise((resolve, reject) => { |
|||
rc.onStart(() => { |
|||
startTime = new Date(); |
|||
resolve() |
|||
}); |
|||
rc.onError((e) => { |
|||
console.log(e); |
|||
reject(e) |
|||
}) |
|||
rc.start({ |
|||
format: 'mp3' // 录音格式,可选值:aac/mp3
|
|||
}); |
|||
}) |
|||
} |
|||
|
|||
let pause = () => { |
|||
rc.stop(); |
|||
} |
|||
|
|||
let close = () => { |
|||
rc.stop(); |
|||
} |
|||
|
|||
let upload = () => { |
|||
return new Promise((resolve, reject) => { |
|||
rc.onStop((wavFile, a, b) => { |
|||
uni.uploadFile({ |
|||
url: UNI_APP.BASE_URL + '/file/upload', |
|||
header: { |
|||
accessToken: uni.getStorageSync("loginInfo").accessToken |
|||
}, |
|||
filePath: wavFile.tempFilePath, |
|||
name: 'file', |
|||
success: (res) => { |
|||
const duration = (new Date().getTime() - startTime.getTime()) / 1000 |
|||
const data = { |
|||
duration: Math.round(duration), |
|||
url: JSON.parse(res.data).data |
|||
} |
|||
resolve(data); |
|||
}, |
|||
fail: (e) => { |
|||
reject(e); |
|||
} |
|||
}) |
|||
}); |
|||
}) |
|||
} |
|||
|
|||
export { |
|||
start, |
|||
pause, |
|||
close, |
|||
upload |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
import Recorder from 'js-audio-recorder'; |
|||
import UNI_APP from '@/.env.js'; |
|||
|
|||
let rc = null; |
|||
let start = () => { |
|||
if(rc != null){ |
|||
close(); |
|||
} |
|||
rc = new Recorder(); |
|||
return rc.start(); |
|||
} |
|||
|
|||
let pause = () => { |
|||
rc.pause(); |
|||
} |
|||
|
|||
let close = () => { |
|||
rc.destroy(); |
|||
rc = null; |
|||
} |
|||
|
|||
let upload = () => { |
|||
return new Promise((resolve, reject) => { |
|||
const wavBlob = rc.getWAVBlob(); |
|||
const newbolb = new Blob([wavBlob], { type: 'audio/wav'}) |
|||
const name = new Date().getDate() + '.wav'; |
|||
const file = new File([newbolb], name) |
|||
uni.uploadFile({ |
|||
url: UNI_APP.BASE_URL + '/file/upload', |
|||
header: { |
|||
accessToken: uni.getStorageSync("loginInfo").accessToken |
|||
}, |
|||
file: file, |
|||
name: 'file', |
|||
success: (res) => { |
|||
const data = { |
|||
duration: parseInt(rc.duration), |
|||
url: JSON.parse(res.data).data |
|||
} |
|||
resolve(data); |
|||
}, |
|||
fail: (e) => { |
|||
reject(e); |
|||
} |
|||
}) |
|||
}) |
|||
} |
|||
|
|||
export { |
|||
start, |
|||
pause, |
|||
close, |
|||
upload |
|||
} |
|||
@ -0,0 +1,227 @@ |
|||
<template> |
|||
<view class="chat-record"> |
|||
<view class="chat-record-bar" id="chat-record-bar" :style="recordBarStyle" @touchstart="onStartRecord" |
|||
@touchmove="onTouchMove" @touchend.prevent="onEndRecord">{{recording?'正在录音':'长按 说话'}}</view> |
|||
<view v-if="recording" class="chat-record-window" :style="recordWindowStyle"> |
|||
<view class="rc-wave"> |
|||
<text class="note" style="--d: 0"></text> |
|||
<text class="note" style="--d: 1"></text> |
|||
<text class="note" style="--d: 2"></text> |
|||
<text class="note" style="--d: 3"></text> |
|||
<text class="note" style="--d: 4"></text> |
|||
<text class="note" style="--d: 5"></text> |
|||
<text class="note" style="--d: 6"></text> |
|||
</view> |
|||
<view class="rc-tip">{{recordTip}}</view> |
|||
<view class="cancel-btn"> |
|||
<uni-icons :class="moveToCancel?'red':'black'" type="clear" |
|||
:size="moveToCancel?45:40"></uni-icons> |
|||
</view> |
|||
<view class="opt-tip" :class="moveToCancel?'red':'black'">{{moveToCancel? '松手取消':'松手发送,上划取消'}}</view> |
|||
</view> |
|||
|
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: "chat-record", |
|||
data() { |
|||
return { |
|||
recording: false, |
|||
moveToCancel: false, |
|||
recordBarTop: 0, |
|||
druation: 0, |
|||
rcTimer: null |
|||
} |
|||
}, |
|||
methods: { |
|||
onTouchMove(e) { |
|||
const moveY = e.touches[0].clientY; |
|||
this.moveToCancel = moveY < this.recordBarTop-40; |
|||
}, |
|||
onStartRecord() { |
|||
console.log("开始录音") |
|||
this.moveToCancel = false; |
|||
this.initRecordBar(); |
|||
this.$rc.start().then(() => { |
|||
this.recording = true; |
|||
console.log("开始录音成功") |
|||
// 开始计时 |
|||
this.startTimer(); |
|||
}).catch((e) => { |
|||
console.log("录音失败"+JSON.stringify(e)) |
|||
uni.showToast({ |
|||
title: "录音失败", |
|||
icon: "none" |
|||
}); |
|||
}); |
|||
}, |
|||
onEndRecord() { |
|||
this.recording = false; |
|||
// 停止录音 |
|||
this.$rc.pause(); |
|||
// 停止计时 |
|||
this.StopTimer(); |
|||
// 触屏位置是否移动到了取消区域 |
|||
if (this.moveToCancel) { |
|||
this.$rc.close(); |
|||
console.log("录音取消") |
|||
return; |
|||
} |
|||
// 小于1秒不发送 |
|||
if (this.druation == 0) { |
|||
uni.showToast({ |
|||
title: "说话时间太短", |
|||
icon: 'none' |
|||
}) |
|||
this.$rc.close(); |
|||
return; |
|||
} |
|||
this.$rc.upload().then((data) => { |
|||
this.$emit("send", data); |
|||
}).catch((e) => { |
|||
uni.showToast({ |
|||
title: e, |
|||
icon: 'none' |
|||
}) |
|||
}).finally(() => { |
|||
this.$rc.close(); |
|||
console.log("录音完成") |
|||
}) |
|||
}, |
|||
startTimer() { |
|||
this.druation = 0; |
|||
this.StopTimer(); |
|||
this.rcTimer = setInterval(() => { |
|||
this.druation++; |
|||
// 大于60s,直接结束 |
|||
if(this.druation >= 60 ){ |
|||
this.onEndRecord(); |
|||
} |
|||
}, 1000) |
|||
}, |
|||
StopTimer() { |
|||
this.rcTimer && clearInterval(this.rcTimer); |
|||
this.rcTimer = null; |
|||
}, |
|||
initRecordBar() { |
|||
const query = uni.createSelectorQuery().in(this); |
|||
query.select('#chat-record-bar').boundingClientRect((rect) => { |
|||
// 顶部高度位置 |
|||
this.recordBarTop = rect.top; |
|||
}).exec() |
|||
} |
|||
}, |
|||
computed: { |
|||
recordWindowStyle() { |
|||
const windowHeight = uni.getSystemInfoSync().windowHeight; |
|||
const bottom = windowHeight - this.recordBarTop + 12; |
|||
return `bottom:${bottom}px;` |
|||
}, |
|||
recordBarStyle() { |
|||
const bgColor = this.recording ? "royalblue" : "#f8f8f8"; |
|||
return `background-color:${bgColor};` |
|||
}, |
|||
recordTip(){ |
|||
if(this.druation > 50){ |
|||
return `${60-this.druation}s后将停止录音`; |
|||
} |
|||
return `录音时长:${this.druation}s`; |
|||
} |
|||
} |
|||
|
|||
} |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.chat-record { |
|||
.rc-wave { |
|||
display: flex; |
|||
align-items: flex-end; |
|||
justify-content: center; |
|||
position: relative; |
|||
height: 80rpx; |
|||
|
|||
.note { |
|||
background: linear-gradient(to top, #395ff3 0%, #89aff3 100%); |
|||
width: 4px; |
|||
height: 50%; |
|||
border-radius: 5rpx; |
|||
margin-right: 4px; |
|||
animation: loading 0.5s infinite linear; |
|||
animation-delay: calc(0.1s * var(--d)); |
|||
|
|||
@keyframes loading { |
|||
0% { |
|||
background-image: linear-gradient(to right, #395ff3 0%, #89aff3 100%); |
|||
height: 20%; |
|||
border-radius: 5rpx; |
|||
} |
|||
|
|||
50% { |
|||
background-image: linear-gradient(to top, #395ff3 0%, #a9cff3 100%); |
|||
height: 80%; |
|||
border-radius: 5rpx; |
|||
} |
|||
|
|||
100% { |
|||
background-image: linear-gradient(to top, #395ff3 0%, #a9cff3 100%); |
|||
height: 20%; |
|||
border-radius: 5rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.chat-record-bar { |
|||
padding: 10rpx; |
|||
margin: 10rpx; |
|||
border-radius: 10rpx; |
|||
text-align: center; |
|||
} |
|||
|
|||
.chat-record-window { |
|||
position: fixed; |
|||
left: 0; |
|||
height: 360rpx; |
|||
width: 100%; |
|||
background-color: rgba(255, 255, 255, 0.95); |
|||
padding: 30rpx; |
|||
|
|||
.icon-microphone { |
|||
text-align: center; |
|||
font-size: 80rpx; |
|||
padding: 10rpx; |
|||
|
|||
} |
|||
|
|||
.rc-tip { |
|||
text-align: center; |
|||
font-size: 30rpx; |
|||
margin-top: 20rpx; |
|||
} |
|||
|
|||
.cancel-btn { |
|||
text-align: center; |
|||
margin-top: 40rpx; |
|||
height: 80rpx; |
|||
|
|||
} |
|||
|
|||
.opt-tip { |
|||
text-align: center; |
|||
font-size: 30rpx; |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.red { |
|||
color: red !important; |
|||
} |
|||
|
|||
.black { |
|||
color: gray; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -1,6 +1,10 @@ |
|||
{ |
|||
"name": "盒子IM", |
|||
"uni-app": { |
|||
"scripts": { |
|||
} |
|||
"scripts": {} |
|||
}, |
|||
"dependencies": { |
|||
"js-audio-recorder": "^1.0.7", |
|||
"recorder-core": "^1.3.23122400" |
|||
} |
|||
} |
|||
} |
|||
|
|||
Binary file not shown.
Loading…
Reference in new issue