hookehuyr

✨ feat: 新增详情页音频播放列表组件

......@@ -11,6 +11,7 @@ declare module '@vue/runtime-core' {
export interface GlobalComponents {
AudioBackground: typeof import('./src/components/audioBackground.vue')['default']
AudioBackground1: typeof import('./src/components/audioBackground1.vue')['default']
AudioList: typeof import('./src/components/audioList.vue')['default']
Floor: typeof import('./src/components/Floor/index.vue')['default']
InfoPopup: typeof import('./src/components/InfoPopup.vue')['default']
InfoPopupLite: typeof import('./src/components/InfoPopupLite.vue')['default']
......
<template>
<div class="audio-list-page">
<van-config-provider :theme-vars="themeVars">
<van-floating-panel v-model:height="info_height" :anchors="anchors" @height-change="onHeightChange">
<template #header>
<div style="display: flex; align-items: center; justify-content: space-between; padding: 1.25rem 1rem 0.5rem 1rem;">
<van-icon name="https://cdn.ipadbiz.cn/bieyuan/map/icon/Group%2054@3x.png" color="#DD7850" size="1.25rem" />
<van-icon @click="onClose" name="cross" color="#DD7850" size="1.25rem" />
</div>
</template>
<!-- <page-info ref="pageInfo" :info="itemInfo" :height="info_height" @close-float="onCloseFloat" @route="onRoute"></page-info> -->
<!-- <div v-if="showClose" @click="closeFloatPanel" class="close-float-panel">
<van-icon name="arrow-left" color="#FFF" size="1.5rem" />
</div> -->
<div class="van-hairline--top" style="padding: 1rem;">
<div v-for="(item, index) in audio_list" :key="index" class="van-hairline--bottom audio-item">
<div :class="['point', audio_index === index ? 'checked' : '']"></div>
<div :class="['text', audio_index === index ? 'checked' : '', 'van-ellipsis']">
{{ index + 1 }}. {{ item.title }}<span v-if="item.play" class="text-center">正在播放</span>
</div>
<!-- <div style="border: 1px solid #DD7850; border-radius: 50%; padding: 0.25rem;">
<van-icon v-if="audio_index === index" name="https://cdn.ipadbiz.cn/xys/map/%E6%92%AD%E6%94%BE@2x.png" size="1.75rem" />
<van-icon v-else name="https://cdn.ipadbiz.cn/xys/map/%E6%92%AD%E6%94%BE%E6%9A%82%E5%81%9C@2x.png" size="1.75rem" />
</div> -->
<div class="progress-ring">
<div class="circle">
<div class="pause-icon">
<van-icon @click="handleAudioPause(item, index)" v-if="item.play" name="https://cdn.ipadbiz.cn/xys/map/%E6%92%AD%E6%94%BE@2x.png" size="2.25rem" />
<van-icon @click="handleAudioPlay(item, index)" v-else name="https://cdn.ipadbiz.cn/xys/map/%E6%92%AD%E6%94%BE%E6%9A%82%E5%81%9C@2x.png" size="2.25rem" />
</div>
<div :class="['progress', item.play ? 'checked' : '']"></div>
</div>
</div>
</div>
</div>
</van-floating-panel>
</van-config-provider>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRoute, useRouter } from 'vue-router';
import $ from 'jquery';
const props = defineProps({
height: Number,
});
const themeVars = ref({
floatingPanelHeaderHeight: 0,
floatingPanelBorderRadius: '1.25rem'
})
const anchors = ref([0, (0.2 * window.innerHeight), (0.5 * window.innerHeight)]);
const onHeightChange = ({ height }) => {
if (!height) {
onClose()
}
}
const info_height = ref(0);
watch(
() => props.height,
(v) => {
info_height.value = v;
}
)
const onClose = () => {
audio.value.pause();
pauseProgress();
// 播放进度条
$('.progress').css('background', 'conic-gradient(#f07142 0%, transparent 0%)');
audio_list.value.forEach(item => item.play = false);
emit('close');
}
const emit = defineEmits(['close']);
const audio_index = ref();
const audio_list = ref([]);
onMounted(() => {
audio_list.value = [
{
title: '3',
src: 'https://img.tukuppt.com/newpreview_music/01/62/01/63b515415b482633.mp3',
play: false
},
{
title: '2',
src: 'https://img.tukuppt.com/newpreview_music/01/66/20/63c0c3db3f8de739.mp3',
play: false
},
{
title: '风纺声(测试)(风的声音)风纺声(测试)(风的声音)',
src: 'https://img.tukuppt.com/newpreview_music/01/65/86/63c0264040bd4441.mp3',
play: false
},
];
let audioDuration = 60; // 音频总时长 (秒)
let elapsedTime = 0; // 已播放的时间
// nextTick(() => {
// setPlayProgress(30)
// })
});
const audio = ref(new Audio());
const duration = ref(0); // 音频总时长
const currentTime = ref(0); // 当前播放时间
// let audioDuration = 30; // 音频总时长 (秒)
let elapsedTime = 0; // 已播放的时间
let intervalId = null; // 保存setInterval的ID
// 函数:更新进度条
function updateProgress(audioDuration, index) {
let progressPercent = (elapsedTime / audioDuration) * 100;
document.querySelector('.progress.checked').style.background =
`conic-gradient(#f07142 ${progressPercent}%, transparent ${progressPercent}%)`;
if (elapsedTime >= audioDuration) {
clearInterval(intervalId); // 音频播放完成,停止更新
let idx = index + 1;
if (idx >= audio_list.value.length) {
idx = 0;
}
handleAudioPlay(audio_list.value[idx], idx)
}
}
// 函数:启动进度更新
function startProgress(audioDuration, index) {
intervalId = setInterval(() => {
elapsedTime++;
updateProgress(audioDuration, index);
}, 1000);
}
// 函数:暂停进度更新
function pauseProgress() {
clearInterval(intervalId);
}
// 初始化进度条
// startProgress();
// const handleAudio = (item, index) => {
// audio_index.value = index;
// document.querySelector('.progress').style.background =
// `conic-gradient(#f07142 0%, transparent 0%)`;
// clearInterval(progressInterval); // 音频播放完成
// setPlayProgress(item.duration);
// }
const handleAudioPause = (item, index) => {
item.play = false;
audio.value.pause();
pauseProgress();
}
const updateDuration = () => {
duration.value = Math.floor(audio.value.duration); // 获取音频时长
};
const updateTime = () => {
currentTime.value = Math.floor(audio.value.currentTime); // 获取当前播放时间
};
// 监听 'loadedmetadata' 事件,确保在音频元数据加载后获取时长
audio.value.addEventListener('loadedmetadata', updateDuration);
// 监听 'timeupdate' 事件,实时更新当前播放时间
audio.value.addEventListener('timeupdate', updateTime);
const handleAudioPlay = (item, index) => {
// 播放状态
audio_list.value.forEach(item => item.play = false);
if (audio_index.value !== index) {
audio.value.src = item.src;
}
// 播放进度条
$('.progress').css('background', 'conic-gradient(#f07142 0%, transparent 0%)')
clearInterval(intervalId);
let play_status = audio.value.play() // 播放
if (play_status) {
play_status.then(() => {
item.play = true;
// 存放到pinia里面控制
// store.changeAudio(audio.value);
// store.changeAudioSrc(audio.value.src);
// store.changeAudioStatus('play');
//
startProgress(duration.value, index);
if (audio_index.value === index) { // 点击同一音频
} else {
audio_index.value = index;
elapsedTime = 0
}
}).catch((e) => {
// 失败
console.log('Operation is too fast, audio play fails')
})
}
}
</script>
<style lang="less" scoped>
.audio-list-page {
.audio-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem 0;
.point {
padding: 0.25rem;
width: 1rem;
height: 1rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 1rem;
&.checked {
border: 1px solid rgba(221, 120, 80, 0.5);
}
&::after {
content: '';
width: 1rem;
height: 1rem;
border-radius: 50%;
background-color: #DD7850;
}
}
.text {
flex: 1;
// display: flex;
align-items: center;
color: #47525F99;
&.checked {
color: #47525F;
}
.text-center {
color: #DD7850;
font-size: 0.85rem;
margin-left: 1rem;
line-height: 1.25;
}
}
}
.progress-ring {
position: relative;
width: 2.5rem;
height: 2.5rem;
}
.circle {
position: relative;
width: 100%;
height: 100%;
border-radius: 50%;
// background-color: lightgray;
}
.pause-icon {
z-index: 10;
position: absolute;
left:0.15rem;
top: 0.15rem;
}
.progress {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
border-radius: 50%;
background: conic-gradient(#f07142 0%, #f07142 0%, transparent 0%);
z-index: 5;
transform: rotate(0deg); /* 让进度条从顶部开始 */
}
}
</style>
<!--
* @Date: 2024-09-15 22:08:49
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2024-10-08 10:30:17
* @LastEditTime: 2024-10-08 17:37:42
* @FilePath: /map-demo/src/views/bieyuan/info.vue
* @Description: 文件描述
-->
......@@ -24,7 +24,13 @@
<div class="info-header">
<div style="display: flex; justify-content: space-between;">
<p class="info-title">{{ page_details.name }}</p>
<div v-if="page_details.path?.length > 1" @click="goTo()" class="info-btn">前往</div>
<div style="display: flex;">
<div @click="onClickAudioList" style="margin-right: 0.75rem;">
<van-icon v-if="!audio_list_height" name="https://cdn.ipadbiz.cn/bieyuan/map/icon/%E8%AF%AD%E9%9F%B31@3x.png" size="1.65rem" />
<van-icon v-else name="https://cdn.ipadbiz.cn/bieyuan/map/icon/%E8%AF%AD%E9%9F%B32@3x.png" size="1.65rem" />
</div>
<div v-if="page_details.path?.length > 1" @click="goTo()" class="info-btn">前往</div>
</div>
</div>
<div class="info-sub-title">{{ page_details.note }}</div>
</div>
......@@ -73,6 +79,8 @@
</van-image-preview>
<van-back-top />
<audio-play-list :height="audio_list_height" @close="onCloseAudioList"></audio-play-list>
</div>
</template>
......@@ -82,6 +90,7 @@ import { useRoute, useRouter } from 'vue-router'
import { showImagePreview } from 'vant';
import { storeToRefs } from 'pinia'
import { mainStore } from '@/store';
import audioPlayList from '@/components/audioList.vue'
import $ from 'jquery';
......@@ -350,7 +359,17 @@ const show_shrink = computed(() => {
return true;
}
return false;
})
});
const audio_list_height = ref(0);
const onClickAudioList = () => {
audio_list_height.value = (0.2 * window.innerHeight);
}
const onCloseAudioList = () => {
audio_list_height.value = 0;
}
</script>
<style lang="less">
......