uni-app音频播放小程序

1.样式代码和HTML

html 复制代码
<view class="audio-player" v-for="(item2, index) in item.fieldValueUrl" :key="index">
	<!-- 圆形播放按钮 -->
	<uv-icon :name="(audioNum === index) ? 'pause-circle' : 'play-circle'" size="20" color="primary"
		@click="audioNum === index ? closeAudio(index) : openAudio(item2, index)"
		:label="`${index + 1}.`"></uv-icon>
	<!-- 当前播放时间 -->
	<text class="current-time">{{ audioList[index].currentTimeStr }}</text>
	<!-- 进度条容器 -->
	<view class="progress-container" @click="clickSeek" @touchmove.stop.prevent>
		<!-- 底层轨道 -->
		<view class="progress-bg"></view>
		<!-- 已播放轨道 -->
		<view class="progress-active" :style="{ width: audioList[index].progressPercent + '%' }"></view>
		<!-- 圆形滑块 -->
		<view class="progress-dot" :style="{ left: audioList[index].progressPercent + '%' }"></view>
	</view>
	<!-- 总时长 -->
	<text class="total-time">{{ audioList[index].totalTimeStr }}</text>
</view>
css 复制代码
    .audio-player {
		display: flex;
		align-items: center;
		gap: 20rpx;
		width: 100%;
		padding: 40rpx 20rpx;
		margin-bottom: 20rpx;
		box-sizing: border-box;
		background: #ffffff;

		/* 时间文字 */
		.current-time,
		.total-time {
			font-size: 30rpx;
			color: #222;
			flex-shrink: 0;
		}

		/* 进度条外层容器 */
		.progress-container {
			flex: 1;
			height: 8rpx;
			position: relative;
			margin: 0 10rpx;
		}

		/* 底层轨道 */
		.progress-bg {
			width: 100%;
			height: 100%;
			background: #bee5ff;
			border-radius: 999rpx;
		}

		/* 已播放轨道 */
		.progress-active {
			position: absolute;
			left: 0;
			top: 0;
			height: 100%;
			background: #1C90FF;
			border-radius: 999rpx;
		}

		/* 圆形滑块 */
		.progress-dot {
			position: absolute;
			top: 50%;
			width: 30rpx;
			height: 30rpx;
			border-radius: 50%;
			background: #1C90FF;
			transform: translate(-50%, -50%);
		}
	}

2.逻辑代码

javascript 复制代码
//在从后端获取音频数据时,循环创建空数组和创建临时实列,获取总时长
list.forEach((item, index) => {
	audioList.value.push({
		'currentTime': 0,
		'totalTime': 0,
		currentTimeStr: '00:00',
		totalTimeStr: '00:00',
		"progressPercent": 0,
		audioUrl: item
	})
})
// 循环每条音频,异步预取总时长
for (let i = 0; i < audioList.value.length; i++) {
	const item = audioList.value[i]
	const dur = await getAudioDuration(item.audioUrl)
	// 赋值总时长,并格式化显示文字
	item.totalTime = dur
	item.totalTimeStr = formatTime(dur)
}
// 秒格式化 mm:ss
	const formatTime = (sec) => {
		const min = Math.floor(sec / 60)
		const s = Math.floor(sec % 60)
		return `${min.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
	}
//	音频播放start
	// 获取音频总时长(静默加载,无声音)
	const getAudioDuration = (url) => {
		return new Promise((resolve) => {
			// 创建临时实例,只用来读时长
			const tempAudio = uni.createInnerAudioContext()
			tempAudio.muted = true // 静音,不会出声
			tempAudio.src = url

			// 加载成功,返回时长并销毁
			tempAudio.onCanplay(() => {
				const dur = tempAudio.duration || 0
				tempAudio.destroy()
				resolve(dur)
			})

			// 加载失败返回0
			tempAudio.onError(() => {
				tempAudio.destroy()
				resolve(0)
			})
		})
	}
	let innerAudioContext = null // 音频实例
	// 创建音频实例(统一管理)--点击播放时创建
	const createAudio = (index) => {
		// 销毁之前的实例,避免冲突
		if (innerAudioContext) {
			try {
				innerAudioContext.stop()
				innerAudioContext.destroy()
			} catch (e) {}
		}
		innerAudioContext = uni.createInnerAudioContext()
		audioNum.value = index

		innerAudioContext.onCanplay(() => {
			audioList.value[index].totalTime = innerAudioContext.duration || 0
		})
		// 实时监听播放进度,每秒自动更新 currentTime
		innerAudioContext.onTimeUpdate(() => {
			audioList.value[index].currentTime = innerAudioContext.currentTime
		})
		audioList.value[index].progressPercent = computed(() => {
			if (audioList.value[index].totalTime === 0) return 0
			return (audioList.value[index].currentTime / audioList.value[index].totalTime) * 100
		})
		audioList.value[index].currentTimeStr = computed(() => formatTime(audioList.value[index].currentTime))
		audioList.value[index].totalTimeStr = computed(() => formatTime(audioList.value[index].totalTime))
	}

	// 播放音频
	const openAudio = (url, index) => {
		// 停止上一个
		if (audioNum.value !== '') {
			closeAudio(audioNum.value)
		}

		createAudio(index) // 每次播放都创建新实例,稳定不报错

		innerAudioContext.autoplay = true
		innerAudioContext.src = url // 音频地址

		// 播放成功
		innerAudioContext.onPlay(() => {
			audioNum.value = index
		})

		// 播放完毕
		innerAudioContext.onEnded(() => {
			audioNum.value = '' // 图标复原
		})

		// 报错处理
		innerAudioContext.onError((err) => {
			console.log('播放报错', err)
			audioNum.value = ''
		})
	}

	// 关闭音频
	const closeAudio = (index) => {
		audioNum.value = ''
		if (innerAudioContext) {
			try {
				innerAudioContext.stop()
				innerAudioContext.destroy()
				innerAudioContext = null
			} catch (e) {}
		}
	}
	//	音频播放end

3.结果展示