uniapp小程序實現錄音 uniapp小程序長按錄音 點擊播放等功能(CSS實現語音音階動畫效果)

鵬仔先生 2022-01-07 16:31:51 阅读数:844

uniapp 程序 uniapp 程序 播放


uniapp小程序實現錄音 uniapp小程序長按錄音 點擊播放等功能(CSS實現語音音階動畫效果)_uniapp

最近鵬仔剛接觸uniapp,項目使用uniapp開發微信小程序,需要實現一個長按時進行語音錄制,限制錄制時間最大為60秒,錄制完成後,可點擊播放,播放時再次點擊停止播放,錄制完成長按實現删除功能,删除後又可重新錄制(如上圖所示)。

視頻演示

 ​https://v.qq.com/x/page/b325884hmr3.html​

下放為整體代碼,可能不是很完美,沒有想象中的那麼流利,但是功能都有實現,歡迎借鑒(其中包含CSS實現語音音階動畫效果,自行複制即可獲取)。

HTML部分


<template>
<view class="record-layer">
<view class="record-box">
<view class="record-btn-layer" v-if="tempFilePath == ''">
<button class="record-btn" :class="longPress == '1' ? 'record-btn-1' : 'record-btn-2'" @longpress="longpressBtn()" @touchend="touchendBtn()">
<image src="../../static/img/record-ico.png"/>
<text>{{longPress == '1' ? '按住說話' : '說話中...'}}</text>
</button>
</view>
<view class="record-btn-layer" v-else>
<button class="record-btn" @longpress="delShow = true" @click="playBtn()" :class="playStatus == '1' ? 'record-btn-2' : 'record-btn-1'">
<image src="../../static/img/scale-ico.png"/>
<text>{{playStatus == '1' ? (count+'s') : '點擊播放'}}</text>
</button>
</view>
<!-- 語音音階動畫 -->
<view class="prompt-layer prompt-layer-1" v-if="longPress == '2'">
<view class="prompt-loader">
<view class="em" v-for="(item,index) in 15" :key="index"></view>
</view>
<text class="p">{{'剩餘:' + count + 's'}}</text>
<text class="span">松手結束錄音</text>
</view>
<!-- 删除 -->
<view class="prompt-layer prompt-layer-2" v-if="delShow" @click.stop="delBtn()">
<text>删除</text>
</view>
</view>
</view>
</template>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

JS部分

<script>
const recorderManager = uni.getRecorderManager()
const innerAudioContext = uni.createInnerAudioContext()
var init // 錄制時長計時器
var timer // 播放 錄制倒計時
export default {
data() {
return {
count: null, // 錄制倒計時
longPress: '1', // 1顯示 按住說話 2顯示 說話中
delShow: false, // 删除提示框顯示隱藏
time: 0, //錄音時長
duration: 60000, //錄音最大值ms 60000/1分鐘
tempFilePath: '', //音頻路徑
playStatus: 0, //錄音播放狀態 0:未播放 1:正在播放
}
},
methods: {
// 倒計時
countdown(val){
let _then = this;
_then.count = Number(val);
timer = setInterval(function() {
if(_then.count > 0){
_then.count--
} else {
_then.longPress = '1';
clearInterval(timer);
}
}, 1000);
},
// 長按錄音事件
longpressBtn(){
this.longPress = '2';
this.countdown(60); // 倒計時
clearInterval(init) // 清除定時器
recorderManager.onStop((res) => {
this.tempFilePath = res.tempFilePath;
this.recordingTimer(this.time);
})
const options = {
duration: this.duration, // 指定錄音的時長,單比特 ms
sampleRate: 16000, // 采樣率
numberOfChannels: 1, // 錄音通道數
encodeBitRate: 96000, // 編碼碼率
format: 'mp3', // 音頻格式,有效值 aac/mp3
frameSize: 10, // 指定幀大小,單比特 KB
}
this.recordingTimer();
recorderManager.start(options);
// 監聽音頻開始事件
recorderManager.onStart((res) => {
console.log(res)
})
},
// 長按松開錄音事件
touchendBtn(){
this.longPress = '1';
recorderManager.onStop((res) => {
this.tempFilePath = res.tempFilePath
})
this.recordingTimer(this.time)
recorderManager.stop()
},
recordingTimer(time){
var that = this;
if (time == undefined) {
// 將計時器賦值給init
init = setInterval(function() {
that.time++
}, 1000);
} else {
clearInterval(init)
}
},
// 删除錄音
delBtn(){
this.delShow = false;
this.time = 0
this.tempFilePath = ''
this.playStatus = 0
innerAudioContext.stop()
},
// 播放
playBtn(){
innerAudioContext.src = this.tempFilePath
//在ios下靜音時播放沒有聲音,默認為true,改為false就好了。
// innerAudioContext.obeyMuteSwitch = false
//點擊播放
if (this.playStatus == 0) {
this.playStatus = 1;
innerAudioContext.play();
this.countdown(this.time); // 倒計時
} else {
this.playStatus = 0;
innerAudioContext.pause()
}
// //播放結束
innerAudioContext.onEnded(() => {
this.playStatus = 0;
innerAudioContext.stop();
})
},
}
}
</script>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.

CSS部分

<style>
/* 語音錄制開始--------------------------------------------------------------------- */
.record-layer{
width: 100%;
padding: 300px 0;
box-sizing: border-box;
}
.record-box{
width: 100%;
position: relative;
}
.record-btn-layer{
width: 100%;
}
.record-btn-layer button::after {
border: none;
}
.record-btn-layer button{
font-size: 14px;
line-height: 38px;
width: 100%;
height: 38px;
border-radius: 8px;
text-align: center;
background: #FFD300;
}
.record-btn-layer button image{
width: 16px;
height: 16px;
margin-right: 4px;
vertical-align: middle;
}
.record-btn-layer .record-btn-2{
background: rgba(255, 211, 0, 0.2);
}
/* 提示小彈窗 */
.prompt-layer{
border-radius: 8px;
background: #FFD300;
padding: 8px 16px;
box-sizing: border-box;
position: absolute;
left: 50%;
transform: translateX(-50%);
}
.prompt-layer::after{
content: '';
display: block;
border: 6px solid rgba(0,0,0,0);
border-top-color: rgba(255, 211, 0, 1);
position: absolute;
bottom: -10px;
left: 50%;
transform: translateX(-50%);
}
.prompt-layer-1{
font-size: 12px;
width: 128px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
top: -80px;
}
.prompt-layer-1 .p{
color: #000000;
}
.prompt-layer-1 .span{
color: rgba(0,0,0,.6);
}
.prompt-loader .em{

}
/* 語音音階------------- */
.prompt-loader {
width: 96px;
height: 20px;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.prompt-loader .em {
display: block;
background: #333333;
width: 1px;
height: 10%;
margin-right: 2.5px;
float: left;
}
.prompt-loader .em:last-child {
margin-right: 0px;
}
.prompt-loader .em:nth-child(1) {
animation: load 2.5s 1.4s infinite linear;
}
.prompt-loader .em:nth-child(2) {
animation: load 2.5s 1.2s infinite linear;
}
.prompt-loader .em:nth-child(3) {
animation: load 2.5s 1s infinite linear;
}
.prompt-loader .em:nth-child(4) {
animation: load 2.5s 0.8s infinite linear;
}
.prompt-loader .em:nth-child(5) {
animation: load 2.5s 0.6s infinite linear;
}
.prompt-loader .em:nth-child(6) {
animation: load 2.5s 0.4s infinite linear;
}
.prompt-loader .em:nth-child(7) {
animation: load 2.5s 0.2s infinite linear;
}
.prompt-loader .em:nth-child(8) {
animation: load 2.5s 0s infinite linear;
}
.prompt-loader .em:nth-child(9) {
animation: load 2.5s 0.2s infinite linear;
}
.prompt-loader .em:nth-child(10) {
animation: load 2.5s 0.4s infinite linear;
}
.prompt-loader .em:nth-child(11) {
animation: load 2.5s 0.6s infinite linear;
}
.prompt-loader .em:nth-child(12) {
animation: load 2.5s 0.8s infinite linear;
}
.prompt-loader .em:nth-child(13) {
animation: load 2.5s 1s infinite linear;
}
.prompt-loader .em:nth-child(14) {
animation: load 2.5s 1.2s infinite linear;
}
.prompt-loader .em:nth-child(15) {
animation: load 2.5s 1.4s infinite linear;
}
@keyframes load {
0% {
height: 10%;
}
50% {
height: 100%;
}
100% {
height: 10%;
}
}
/* 語音音階-------------------- */
.prompt-layer-2{
top: -40px;
}
.prompt-layer-2 .text{
color: rgba(0, 0, 0, 1);
font-size: 12px;
}
/* 語音錄制結束---------------------------------------------------------------- */
</style>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.

以上部分,請注明來源 鵬仔先生的博客 ​ ​http://www.sharedblog.cn/post/230.html​

下放網上找的案例,挺不錯的,推薦測試

<template>
<view class="sound-recording">
<view class="time">{{status==0?'錄音時長':(status==3?'錄音已完成':'正在錄音中')}}:{{time}} 秒</view>
<view class="btn">
<view :class="status==3?'show':'hide'" @click="reset" hover-class="jump-hover">重新錄制</view>
<view :class="status==3 && playStatus==0?'show':'hide'" @click="bofang" hover-class="jump-hover">{{playStatus==1?'錄音播放中':'播放錄音'}}</view>
</view>
<view class="progress">
<text class="txt">最大錄音時長({{duration/1000}}秒 = {{duration/60000}}分鐘)</text>
<progress :percent="time*(100/(duration/1000))" border-radius="10" color="green" stroke-width="10" backgroundColor="#fff" />
</view>
<view class="anniu">
<view :class="status==0?'row':'no-clicking'" @click="kaishi" hover-class="jump-hover">開始</view>
<view :class="status==1?'row':'no-clicking'" @click="zanting" hover-class="jump-hover">暫停</view>
<view :class="status==2?'row':'no-clicking'" @click="jixu" hover-class="jump-hover">繼續</view>
<view :class="status==1 || status==2?'row':'no-clicking'" @click="tingzhi" hover-class="jump-hover">停止</view>
</view>
</view>
</template>

<script>
const recorderManager = uni.getRecorderManager()
const innerAudioContext = uni.createInnerAudioContext()
var init
export default {
data() {
return {
time: 0, //錄音時長
duration: 600000, //錄音最大值ms 600000/10分鐘
tempFilePath: "", //音頻路徑
status: 0, //錄音狀態 0:未開始錄音 1:正在錄音 2:暫停錄音 3:已完成錄音
playStatus: 0, //錄音播放狀態 0:未播放 1:正在播放
}
},
methods: {
kaishi: function() {
clearInterval(init) //清除定時器
//監聽錄音自動結束事件(如果不加,錄音時間到最大值自動結束後,沒獲取到錄音路徑將無法正常進行播放)
recorderManager.onStop((res) => {
console.log('recorder stop', res)
this.tempFilePath = res.tempFilePath
this.status = 3
this.recordingTimer(this.time)
})

const options = {
duration: this.duration, //指定錄音的時長,單比特 ms
sampleRate: 16000, //采樣率
numberOfChannels: 1, //錄音通道數
encodeBitRate: 96000, //編碼碼率
format: 'mp3', //音頻格式,有效值 aac/mp3
frameSize: 10, //指定幀大小,單比特 KB
}
this.recordingTimer()
recorderManager.start(options)
// 監聽音頻開始事件
recorderManager.onStart((res) => {
console.log('recorder start')
this.status = 1
})
console.log(this.status);
},

/**
* 暫停錄音
*/
zanting: function() {
console.log('zanting');
recorderManager.onPause(() => {
console.log('recorder pause')
this.status = 2
})
this.recordingTimer(this.time)
recorderManager.pause()
},

/**
* 繼續錄音
*/
jixu: function() {
this.status = 1
this.recordingTimer()
recorderManager.resume()
},

/**
* 停止錄音
*/
tingzhi: function() {
debugger
recorderManager.onStop((res) => {
console.log('recorder stop', res)
this.tempFilePath = res.tempFilePath
this.status = 3
})
this.recordingTimer(this.time)
recorderManager.stop()

},

/**
* 播放錄音
*/
bofang: function() {
//音頻地址
console.log(this.tempFilePath);
innerAudioContext.src = this.tempFilePath
//在ios下靜音時播放沒有聲音,默認為true,改為false就好了。
// innerAudioContext.obeyMuteSwitch = false

//點擊播放
if (this.playStatus == 0) {
this.playStatus = 1
innerAudioContext.play()
}
// //播放結束
innerAudioContext.onEnded(() => {
innerAudioContext.stop()
this.playStatus = 0
})
},
recordingTimer: function(time) {
var that = this
if (time == undefined) {
//將計時器賦值給init
init = setInterval(function() {
var time = that.time + 1;
that.time = time
}, 1000);
} else {
clearInterval(init)
console.log("暫停計時")
}
},

/**
* 重新錄制
*/
reset: function() {
var that = this
wx.showModal({
title: "重新錄音",
content: "是否重新錄制?",
success(res) {
if (res.confirm) {
that.time = 0
that.tempFilePath = ''
that.status = 0
that.playStatus = 0
innerAudioContext.stop()
}
}
})
}
}
}
</script>

<style>
.sound-recording {
background-color: rgb(234, 234, 234);
margin: 10rpx 30rpx;
border-radius: 20rpx;
padding: 5rpx 0rpx;
}

.btn {
margin: 0rpx 100rpx;
display: flex;
justify-content: space-between;
align-items: center;
}

.btn .show {
padding: 10rpx;
width: 200rpx;
font-size: 25rpx;
display: flex;
justify-content: center;
align-items: center;
background-color: rgb(178, 228, 228);
border-radius: 20rpx;
border: 5rpx solid rgb(127, 204, 214);
}

.btn .hide {
padding: 10rpx;
width: 200rpx;
font-size: 25rpx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 20rpx;
border: 5rpx solid #eee;
pointer-events: none;
background-color: rgba(167, 162, 162, 0.445);
}

.time {
line-height: 70rpx;
text-align: center;
font-size: 30rpx;
}

.progress {
margin: 20rpx;
}

.play {
margin: 0rpx 20rpx;
}

.txt {
display: flex;
justify-content: center;
line-height: 60rpx;
font-size: 25rpx;
}

.anniu {
margin: 10rpx 50rpx;
display: flex;
justify-content: space-between;
}

.row {
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
font-size: 25rpx;
width: 80rpx;
height: 80rpx;
background-color: rgb(178, 228, 228);
border: 5rpx solid rgb(127, 204, 214);
}

.jump-hover {
transform: scale(0.9);
}

/*禁止點擊*/

.anniu .no-clicking {
pointer-events: none;
background-color: rgba(167, 162, 162, 0.445);
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
font-size: 25rpx;
width: 80rpx;
height: 80rpx;
border: 5rpx solid rgb(241, 244, 245);
}
</style>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.


版权声明:本文为[鵬仔先生]所创,转载请带上原文链接,感谢。 https://gsmany.com/2022/01/202201071631506523.html