uniapp 录音组件

  • Post author:
  • Post category:uniapp




原生uniapp 录音组件,复制就能用

效果图



代码部分

<template>
	<view class="box">
		<uni-popup ref="popup" :animation="true" :is-mask-click="false">
			<view class="popup_box" @tap.stop = "closePop">
				<view class="move">
					<view v-if="btnStatus!==0" :class="btnStatus==3? 'move_center_clear': 'move_center'">
						<view class="move_center_top">
							<view class="move_center_item" v-for="(item,index) in 15" :key="index">
							</view>
						</view>
						<view class="move_center_footer">
							{{btnStatus==3? '松开手指,取消发送':"正在录音..."}}
						</view>
					</view>
				</view>


				<view :class=" btnStatus==0? 'footerBtn': btnStatus==1?'footerBtn_star':'footerBtn_move'">
					<view class="footerBtn_chr" @touchend="end" @touchmove="move" @touchstart="start">
						{{btnStatus==0? "按住说话":btnStatus==1? "说话中...": btnStatus==2? "松开手指发送录音": "上划取消"}}
					</view>
				</view>
				<!-- <button @click="play(voicePath)">播放</button> -->
			</view>
		</uni-popup>
	</view>
</template>



js部分

<script>
	const recorderManager = uni.getRecorderManager()
	const innerAudioContext = uni.createInnerAudioContext()
	innerAudioContext.autoplay = true
	export default {
		name: "sound",
		props: {
			maskShow: false, //遮罩层
		},
		data() {
			return {
				recordFlg: true, //录音锁
				btnStatus: 0, //按钮状态
				audioDuration: "",
				voicePath: "", //音频本地路径
				timer: null, //定时器
				num: 0, //录音时长
			}
		},
		watch: {
			maskShow: {
				// 每个属性值发生变化就会调用这个函数
				handler(newVal, oldVal) {
					console.log(newVal, oldVal, "监听");
					if (newVal === true) {
						this.$refs.popup.open('center')
					} else {
						this.$refs.popup.close()
					}

				},
				// 立即处理 进入页面就触发
				// immediate: true,
				// 深度监听 属性的变化
				// deep: true
			},
		},
		created() {
			console.log("888");
			// 为了防止苹果手机静音无法播放
			uni.setInnerAudioOption({
				obeyMuteSwitch: false
			})
			let that = this;
			recorderManager.onStop(function(res) {
				// 解决第一次录音,挂实例挂载问题
				that.$nextTick(() => {
					console.log('recorder stop' + JSON.stringify(res));
					that.audioDuration = res.duration;
					that.voicePath = res.tempFilePath;
				})
			});

		},
		methods: {
			// 点击关闭遮罩层
			closePop() {
				this.$refs.popup.close()
				this.$emit("emitCheck", false)
			},
			start() {
				// 限制时长
				this.timer = setInterval(() => {
					this.num++
					console.log("num", this.num);
				}, 1000)

				console.log("手指按下");
				this.btnStatus = 1
				// 暂停播放
				// innerAudioContext.stop()
				recorderManager.start({
					//时长5分钟,单位毫秒
					duration: 300000
				});
				this.recordFlg = true
			},
			move(e) {
				if (Math.abs(e.touches[0].pageY) < 650 && Math.abs(e.touches[0].pageY) > 550) {
					this.btnStatus = 2
				} else if (Math.abs(e.touches[0].pageY) < 550) {
					this.btnStatus = 3

				} else {
					this.btnStatus = 1
				}
			},
			end() {

				clearInterval(this.timer)
				this.btnStatus = 0
				recorderManager.stop();
				console.log(this.voicePath, "888");
				// 异步,解决录音结束后,路径没赋值就传值,导致的路径为空
				setTimeout(() => {
					let obj = {
						status: false,
						path: [{
							src: this.voicePath,
							switchStatus: false,
							soundTime: this.num
						}]
					}
					this.$emit("emitCheck", obj)
					this.num = 0
				}, 500)

				console.log("手指松开", this.voicePath);
			},
			// play(value){
			// 	console.log("播放");
			// 	// innerAudioContext.src = value;
			// 	innerAudioContext.play();
			// },
			// 删除本地录音文件
			// del(){
			// 	uni.getSavedFileList({
			// 	  success: function (res) {
			// 	    if (res.fileList.length > 0) {
			// 	      uni.removeSavedFile({
			// 	        filePath: res.fileList[0].filePath,
			// 	        complete: function (res) {
			// 	          console.log(res);
			// 	        }
			// 	      });
			// 	    }
			// 	  }
			// 	});
			// }
		}
	}
</script>



css部分

<style lang="scss" scoped>
	@keyframes load {
		0% {
			height: 10%;
		}

		50% {
			height: 100%;
		}

		100% {
			height: 10%;
		}
	}

	// 取消发送
	.move_center_footer {
		margin-top: 20rpx;
		font-size: 24rpx;
		color: #666;
	}

	.move_center_top {
		height: 40rpx;
		width: 100%;
		display: flex;
		justify-content: center;
		align-items: center;

		.move_center_item {
			display: block;
			background: #333333;
			width: 1px;
			height: 10%;
			margin: 0 6rpx;
			float: left;
		}

		.move_center_item:nth-child(1) {
			animation: load 2.5s 1.4s infinite linear;
		}

		.move_center_item:nth-child(2) {
			animation: load 2.5s 1.2s infinite linear;
		}

		.move_center_item:nth-child(3) {
			animation: load 2.5s 1s infinite linear;
		}

		.move_center_item:nth-child(4) {
			animation: load 2.5s 0.8s infinite linear;
		}

		.move_center_item:nth-child(5) {
			animation: load 2.5s 0.6s infinite linear;
		}

		.move_center_item:nth-child(6) {
			animation: load 2.5s 0.4s infinite linear;
		}

		.move_center_item:nth-child(7) {
			animation: load 2.5s 0.2s infinite linear;
		}

		.move_center_item:nth-child(8) {
			animation: load 2.5s 0s infinite linear;
		}

		.move_center_item:nth-child(9) {
			animation: load 2.5s 0.2s infinite linear;
		}

		.move_center_item:nth-child(10) {
			animation: load 2.5s 0.4s infinite linear;
		}

		.move_center_item:nth-child(11) {
			animation: load 2.5s 0.6s infinite linear;
		}

		.move_center_item:nth-child(12) {
			animation: load 2.5s 0.8s infinite linear;
		}

		.move_center_item:nth-child(13) {
			animation: load 2.5s 1s infinite linear;
		}

		.move_center_item:nth-child(14) {
			animation: load 2.5s 1.2s infinite linear;
		}

		.move_center_item:nth-child(15) {
			animation: load 2.5s 1.4s infinite linear;
		}

	}

	.box {

		.popup_box {
			height: 100vh;
			width: 100vw;
			position: relative;

			// 取消录音区域
			.move {
				width: 100%;
				position: absolute;
				left: 0;
				bottom: 450rpx;
				display: flex;
				justify-content: center;
				align-items: center;

				.move_center_clear {
					width: 40%;
					height: 200rpx;
					border-radius: 50rpx;
					display: flex;
					justify-content: center;
					align-items: center;
					flex-direction: column;
					background: rgba(250, 83, 83, .5);
				}

				.move_center {
					width: 40%;
					height: 200rpx;
					background: rgba(149, 234, 108, .5);
					border-radius: 50rpx;
					display: flex;
					justify-content: center;
					align-items: center;
					flex-direction: column;
				}
			}

			// 底部按钮
			.footerBtn {
				padding: 20rpx 20rpx;
				width: 100%;
				position: absolute;
				left: 0;
				bottom: 60rpx;

				.footerBtn_chr {
					width: 100%;
					height: 96rpx;
					display: flex;
					justify-content: center;
					align-items: center;
					font-size: 28rpx;
					border-radius: 16rpx;
					background-image: linear-gradient(to right, #dfbe9b 0%, #f3dfc9 100%);
					color: #986546;
					transition: all 0.3s;
				}
			}

			.footerBtn_star {
				width: 100%;
				position: absolute;
				left: 0;
				bottom: 0rpx;

				.footerBtn_chr {
					width: 100%;
					height: 250rpx;
					display: flex;
					justify-content: center;
					align-items: center;
					font-size: 28rpx;
					border-radius: 140rpx 140rpx 0 0;
					background: rgba(218, 227, 232, .8);
					color: #666;
					transition: all 0.3s;
				}
			}

			.footerBtn_move {
				width: 100%;
				position: absolute;
				left: 0;
				bottom: 0rpx;

				.footerBtn_chr {
					width: 100%;
					height: 250rpx;
					display: flex;
					justify-content: center;
					align-items: center;
					font-size: 28rpx;
					border-radius: 140rpx 140rpx 0 0;
					background: rgba(51, 51, 51, .5);
					color: #dae3e8;
					transition: all 0.3s;
				}
			}
		}
	}
</style>



使用

<!-- maskShow决定遮罩层是否显示 / @emitCheck自定义事件,用来接收组件返回值 -->
		<sound :maskShow="footerList[1].status" @emitCheck="footerCheck"></sound>



借鉴

本来找的是另一个博主的代码样式,但是发现他的代码会导致ios微信小程序死机,研究一下午没解决,索性仿着写了一个,

那个博主的文章地址



版权声明:本文为qq_43513271原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。