feat: 优化播放样式 优化歌曲背景色 优化 mv播放样式 添加循环播放 等控制功能

This commit is contained in:
alger
2024-11-23 22:42:23 +08:00
parent 3027a5f6ff
commit 0bb14902f2
9 changed files with 1137 additions and 92 deletions
+102 -60
View File
@@ -3,16 +3,16 @@
<div class="mv-list-title">
<h2>推荐MV</h2>
</div>
<n-scrollbar :size="100">
<div v-loading="loading" class="mv-list-content" :class="setAnimationClass('animate__bounceInLeft')">
<n-scrollbar :size="100" @scroll="handleScroll">
<div v-loading="initLoading" class="mv-list-content" :class="setAnimationClass('animate__bounceInLeft')">
<div
v-for="(item, index) in mvList"
:key="item.id"
class="mv-item"
:class="setAnimationClass('animate__bounceIn')"
:style="setAnimationDelay(index, 30)"
:style="setAnimationDelay(index, 10)"
>
<div class="mv-item-img" @click="handleShowMv(item)">
<div class="mv-item-img" @click="handleShowMv(item, index)">
<n-image
class="mv-item-img-img"
:src="getImgUrl(item.cover, '200y112')"
@@ -28,28 +28,28 @@
</div>
<div class="mv-item-title">{{ item.name }}</div>
</div>
<div v-if="loadingMore" class="loading-more">加载中...</div>
<div v-if="!hasMore && !initLoading" class="no-more">没有更多了</div>
</div>
</n-scrollbar>
<n-drawer :show="showMv" height="100vh" placement="bottom" :z-index="999999999">
<div v-loading="mvLoading" class="mv-detail">
<video :src="playMvUrl" controls autoplay></video>
<div class="mv-detail-title">
<div class="title">{{ playMvItem?.name }}</div>
<button @click="close">
<i class="iconfont icon-xiasanjiaoxing"></i>
</button>
</div>
</div>
</n-drawer>
<mv-player
v-model:show="showMv"
:current-mv="playMvItem"
:is-prev-disabled="isPrevDisabled"
@next="playNextMv"
@prev="playPrevMv"
/>
</div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue';
import { computed, onMounted, ref } from 'vue';
import { useStore } from 'vuex';
import { getMvUrl, getTopMv } from '@/api/mv';
import { getTopMv } from '@/api/mv';
import MvPlayer from '@/components/MvPlayer.vue';
import { IMvItem } from '@/type/mv';
import { formatNumber, getImgUrl, setAnimationClass, setAnimationDelay } from '@/utils';
@@ -60,35 +60,99 @@ defineOptions({
const showMv = ref(false);
const mvList = ref<Array<IMvItem>>([]);
const playMvItem = ref<IMvItem>();
const playMvUrl = ref<string>();
const store = useStore();
const loading = ref(false);
const initLoading = ref(false);
const loadingMore = ref(false);
const currentIndex = ref(0);
const offset = ref(0);
const limit = ref(30);
const hasMore = ref(true);
onMounted(async () => {
loading.value = true;
const res = await getTopMv(30);
mvList.value = res.data.data;
loading.value = false;
await loadMvList();
});
const mvLoading = ref(false);
const handleShowMv = async (item: IMvItem) => {
mvLoading.value = true;
const handleShowMv = async (item: IMvItem, index: number) => {
store.commit('setIsPlay', false);
store.commit('setPlayMusic', false);
showMv.value = true;
const res = await getMvUrl(item.id);
currentIndex.value = index;
playMvItem.value = item;
playMvUrl.value = res.data.data.url;
mvLoading.value = false;
};
const close = () => {
showMv.value = false;
if (store.state.playMusicUrl) {
store.commit('setIsPlay', true);
const playPrevMv = async (setLoading: (value: boolean) => void) => {
try {
if (currentIndex.value > 0) {
const prevItem = mvList.value[currentIndex.value - 1];
await handleShowMv(prevItem, currentIndex.value - 1);
}
} finally {
setLoading(false);
}
};
const playNextMv = async (setLoading: (value: boolean) => void) => {
try {
if (currentIndex.value < mvList.value.length - 1) {
const nextItem = mvList.value[currentIndex.value + 1];
await handleShowMv(nextItem, currentIndex.value + 1);
} else if (hasMore.value) {
await loadMvList();
if (mvList.value.length > currentIndex.value + 1) {
const nextItem = mvList.value[currentIndex.value + 1];
await handleShowMv(nextItem, currentIndex.value + 1);
} else {
showMv.value = false;
}
} else {
showMv.value = false;
}
} catch (error) {
console.error('加载更多MV失败:', error);
showMv.value = false;
} finally {
setLoading(false);
}
};
const loadMvList = async () => {
if (!hasMore.value || loadingMore.value) return;
if (offset.value === 0) {
initLoading.value = true;
} else {
loadingMore.value = true;
}
try {
const res = await getTopMv(limit.value, offset.value);
if (offset.value === 0) {
mvList.value = res.data.data;
} else {
mvList.value.push(...res.data.data);
}
hasMore.value = res.data.data.length === limit.value;
offset.value += limit.value;
} catch (error) {
console.error('加载MV失败:', error);
} finally {
initLoading.value = false;
loadingMore.value = false;
}
};
const handleScroll = (e: Event) => {
const target = e.target as Element;
const { scrollTop, clientHeight, scrollHeight } = target;
const threshold = 100;
if (scrollHeight - (scrollTop + clientHeight) < threshold) {
loadMvList();
}
};
const isPrevDisabled = computed(() => currentIndex.value === 0);
</script>
<style scoped lang="scss">
@@ -153,36 +217,14 @@ const close = () => {
}
}
.mv-detail {
@apply w-full h-full bg-black relative;
&-title {
@apply absolute w-full left-0 flex justify-between h-16 px-6 py-2 text-xl font-bold items-center z-50 transition-all duration-300 ease-in-out -top-24;
background: linear-gradient(0, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 100%);
button .icon-xiasanjiaoxing {
@apply text-3xl;
}
button:hover {
@apply text-green-400;
}
}
video {
@apply w-full h-full;
}
video:hover + .mv-detail-title {
@apply top-0;
}
.mv-detail-title:hover {
@apply top-0;
}
}
.mobile {
.mv-list-content {
grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));
}
}
.loading-more,
.no-more {
@apply col-span-full text-center py-4 text-gray-400;
}
</style>