2021-11-09 11:34:09 +08:00
|
|
|
<template>
|
2024-11-23 22:42:23 +08:00
|
|
|
<n-drawer
|
|
|
|
|
:show="musicFull"
|
|
|
|
|
height="100vh"
|
|
|
|
|
placement="bottom"
|
|
|
|
|
:style="{ background: currentBackground || background }"
|
|
|
|
|
>
|
2023-12-17 14:48:21 +08:00
|
|
|
<div id="drawer-target">
|
2024-10-18 18:37:53 +08:00
|
|
|
<div class="drawer-back"></div>
|
2023-12-17 14:48:21 +08:00
|
|
|
<div class="music-img">
|
2024-05-16 18:54:30 +08:00
|
|
|
<n-image ref="PicImgRef" :src="getImgUrl(playMusic?.picUrl, '300y300')" class="img" lazy preview-disabled />
|
2024-10-18 18:37:53 +08:00
|
|
|
<div>
|
|
|
|
|
<div class="music-content-name">{{ playMusic.name }}</div>
|
|
|
|
|
<div class="music-content-singer">
|
2024-12-05 21:29:13 +08:00
|
|
|
<span v-for="(item, index) in playMusic.ar || playMusic.song.artists" :key="index">
|
|
|
|
|
{{ item.name }}{{ index < (playMusic.ar || playMusic.song.artists).length - 1 ? ' / ' : '' }}
|
2024-10-18 18:37:53 +08:00
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2023-12-17 14:48:21 +08:00
|
|
|
</div>
|
|
|
|
|
<div class="music-content">
|
|
|
|
|
<n-layout
|
2024-05-16 18:54:30 +08:00
|
|
|
ref="lrcSider"
|
2023-12-17 14:48:21 +08:00
|
|
|
class="music-lrc"
|
2024-10-18 18:37:53 +08:00
|
|
|
style="height: 60vh"
|
2023-12-17 14:48:21 +08:00
|
|
|
:native-scrollbar="false"
|
|
|
|
|
@mouseover="mouseOverLayout"
|
|
|
|
|
@mouseleave="mouseLeaveLayout"
|
|
|
|
|
>
|
2024-10-18 18:37:53 +08:00
|
|
|
<div ref="lrcContainer">
|
2023-12-17 14:48:21 +08:00
|
|
|
<div
|
2024-10-18 18:37:53 +08:00
|
|
|
v-for="(item, index) in lrcArray"
|
|
|
|
|
:id="`music-lrc-text-${index}`"
|
|
|
|
|
:key="index"
|
2023-12-17 14:48:21 +08:00
|
|
|
class="music-lrc-text"
|
2024-11-28 08:12:37 +08:00
|
|
|
:class="{ 'now-text': index === nowIndex, 'hover-text': item.text }"
|
2023-12-18 15:24:57 +08:00
|
|
|
@click="setAudioTime(index, audio)"
|
2023-12-17 14:48:21 +08:00
|
|
|
>
|
2024-10-18 18:37:53 +08:00
|
|
|
<span :style="getLrcStyle(index)">{{ item.text }}</span>
|
2024-05-16 18:54:30 +08:00
|
|
|
<div class="music-lrc-text-tr">{{ item.trText }}</div>
|
2023-12-17 14:48:21 +08:00
|
|
|
</div>
|
2024-10-18 18:37:53 +08:00
|
|
|
</div>
|
2023-12-17 14:48:21 +08:00
|
|
|
</n-layout>
|
|
|
|
|
<!-- 时间矫正 -->
|
2024-10-18 18:37:53 +08:00
|
|
|
<!-- <div class="music-content-time">
|
2023-12-27 14:39:52 +08:00
|
|
|
<n-button @click="reduceCorrectionTime(0.2)">-</n-button>
|
|
|
|
|
<n-button @click="addCorrectionTime(0.2)">+</n-button>
|
2024-10-18 18:37:53 +08:00
|
|
|
</div> -->
|
2021-11-09 11:34:09 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2023-12-18 15:24:57 +08:00
|
|
|
</n-drawer>
|
2021-11-09 11:34:09 +08:00
|
|
|
</template>
|
2023-12-17 14:48:21 +08:00
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2024-10-18 18:37:53 +08:00
|
|
|
import { useDebounceFn } from '@vueuse/core';
|
2024-11-23 22:42:23 +08:00
|
|
|
import { onBeforeUnmount, ref, watch } from 'vue';
|
2024-05-16 18:54:30 +08:00
|
|
|
|
2024-11-23 22:42:23 +08:00
|
|
|
import { lrcArray, nowIndex, playMusic, setAudioTime, useLyricProgress } from '@/hooks/MusicHook';
|
2024-05-16 18:54:30 +08:00
|
|
|
import { getImgUrl } from '@/utils';
|
2024-11-23 22:42:23 +08:00
|
|
|
import { animateGradient, getHoverBackgroundColor, getTextColors } from '@/utils/linearColor';
|
2023-12-17 14:48:21 +08:00
|
|
|
|
2024-11-23 22:42:23 +08:00
|
|
|
// 定义 refs
|
2024-09-14 18:22:56 +08:00
|
|
|
const lrcSider = ref<any>(null);
|
|
|
|
|
const isMouse = ref(false);
|
2024-10-18 18:37:53 +08:00
|
|
|
const lrcContainer = ref<HTMLElement | null>(null);
|
2024-11-23 22:42:23 +08:00
|
|
|
const currentBackground = ref('');
|
|
|
|
|
const animationFrame = ref<number | null>(null);
|
|
|
|
|
const isDark = ref(false);
|
|
|
|
|
|
|
|
|
|
// 初始化 textColors
|
|
|
|
|
const textColors = ref(getTextColors());
|
2024-09-14 18:22:56 +08:00
|
|
|
|
2021-11-09 11:34:09 +08:00
|
|
|
const props = defineProps({
|
2023-12-17 14:48:21 +08:00
|
|
|
musicFull: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false,
|
2021-11-09 11:34:09 +08:00
|
|
|
},
|
|
|
|
|
audio: {
|
2023-12-17 14:48:21 +08:00
|
|
|
type: HTMLAudioElement,
|
|
|
|
|
default: null,
|
|
|
|
|
},
|
2024-09-14 18:22:56 +08:00
|
|
|
background: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: '',
|
|
|
|
|
},
|
2024-05-16 18:54:30 +08:00
|
|
|
});
|
2021-11-09 11:34:09 +08:00
|
|
|
|
|
|
|
|
// 歌词滚动方法
|
2024-10-18 18:37:53 +08:00
|
|
|
const lrcScroll = (behavior = 'smooth') => {
|
|
|
|
|
const nowEl = document.querySelector(`#music-lrc-text-${nowIndex.value}`);
|
|
|
|
|
if (props.musicFull && !isMouse.value && nowEl && lrcContainer.value) {
|
|
|
|
|
const containerRect = lrcContainer.value.getBoundingClientRect();
|
|
|
|
|
const nowElRect = nowEl.getBoundingClientRect();
|
|
|
|
|
const relativeTop = nowElRect.top - containerRect.top;
|
|
|
|
|
const scrollTop = relativeTop - lrcSider.value.$el.getBoundingClientRect().height / 2;
|
|
|
|
|
lrcSider.value.scrollTo({ top: scrollTop, behavior });
|
2021-11-09 11:34:09 +08:00
|
|
|
}
|
2024-05-16 18:54:30 +08:00
|
|
|
};
|
2024-10-18 18:37:53 +08:00
|
|
|
|
|
|
|
|
const debouncedLrcScroll = useDebounceFn(lrcScroll, 200);
|
|
|
|
|
|
2021-11-09 11:34:09 +08:00
|
|
|
const mouseOverLayout = () => {
|
2024-05-16 18:54:30 +08:00
|
|
|
isMouse.value = true;
|
|
|
|
|
};
|
2021-11-09 11:34:09 +08:00
|
|
|
const mouseLeaveLayout = () => {
|
|
|
|
|
setTimeout(() => {
|
2024-05-16 18:54:30 +08:00
|
|
|
isMouse.value = false;
|
2024-10-18 18:37:53 +08:00
|
|
|
lrcScroll();
|
|
|
|
|
}, 2000);
|
2024-05-16 18:54:30 +08:00
|
|
|
};
|
2023-12-17 14:48:21 +08:00
|
|
|
|
2024-10-18 18:37:53 +08:00
|
|
|
watch(nowIndex, () => {
|
|
|
|
|
debouncedLrcScroll();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => props.musicFull,
|
|
|
|
|
() => {
|
|
|
|
|
if (props.musicFull) {
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
lrcScroll('instant');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
2024-11-23 22:42:23 +08:00
|
|
|
// 监听背景变化
|
|
|
|
|
watch(
|
|
|
|
|
() => props.background,
|
|
|
|
|
(newBg) => {
|
|
|
|
|
if (!newBg) {
|
|
|
|
|
textColors.value = getTextColors();
|
|
|
|
|
document.documentElement.style.setProperty('--hover-bg-color', getHoverBackgroundColor(false));
|
|
|
|
|
document.documentElement.style.setProperty('--text-color-primary', textColors.value.primary);
|
|
|
|
|
document.documentElement.style.setProperty('--text-color-active', textColors.value.active);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currentBackground.value) {
|
|
|
|
|
if (animationFrame.value) {
|
|
|
|
|
cancelAnimationFrame(animationFrame.value);
|
|
|
|
|
}
|
|
|
|
|
animationFrame.value = animateGradient(currentBackground.value, newBg, (gradient) => {
|
|
|
|
|
currentBackground.value = gradient;
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
currentBackground.value = newBg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
textColors.value = getTextColors(newBg);
|
|
|
|
|
isDark.value = textColors.value.active === '#000000';
|
|
|
|
|
|
|
|
|
|
document.documentElement.style.setProperty('--hover-bg-color', getHoverBackgroundColor(isDark.value));
|
|
|
|
|
document.documentElement.style.setProperty('--text-color-primary', textColors.value.primary);
|
|
|
|
|
document.documentElement.style.setProperty('--text-color-active', textColors.value.active);
|
|
|
|
|
},
|
|
|
|
|
{ immediate: true },
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 修改 useLyricProgress 的使用方式
|
|
|
|
|
const { getLrcStyle: originalLrcStyle } = useLyricProgress();
|
|
|
|
|
|
|
|
|
|
// 修改 getLrcStyle 函数
|
|
|
|
|
const getLrcStyle = (index: number) => {
|
|
|
|
|
const colors = textColors.value || getTextColors;
|
|
|
|
|
const originalStyle = originalLrcStyle(index);
|
|
|
|
|
|
|
|
|
|
if (index === nowIndex.value) {
|
|
|
|
|
// 当前播放的歌词,使用渐变效果
|
|
|
|
|
return {
|
|
|
|
|
...originalStyle,
|
|
|
|
|
backgroundImage: originalStyle.backgroundImage
|
|
|
|
|
?.replace(/#ffffff/g, colors.active)
|
|
|
|
|
.replace(/#ffffff8a/g, `${colors.primary}`),
|
|
|
|
|
backgroundClip: 'text',
|
|
|
|
|
WebkitBackgroundClip: 'text',
|
|
|
|
|
color: 'transparent',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 非当前播放的歌词,使用普通颜色
|
|
|
|
|
return {
|
|
|
|
|
color: colors.primary,
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 组件卸载时清理动画
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
if (animationFrame.value) {
|
|
|
|
|
cancelAnimationFrame(animationFrame.value);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2023-12-17 14:48:21 +08:00
|
|
|
defineExpose({
|
2023-12-18 15:24:57 +08:00
|
|
|
lrcScroll,
|
2024-05-16 18:54:30 +08:00
|
|
|
});
|
2021-11-09 11:34:09 +08:00
|
|
|
</script>
|
|
|
|
|
|
2023-12-17 14:48:21 +08:00
|
|
|
<style scoped lang="scss">
|
2023-12-22 09:50:03 +08:00
|
|
|
@keyframes round {
|
|
|
|
|
0% {
|
|
|
|
|
transform: rotate(0deg);
|
|
|
|
|
}
|
|
|
|
|
100% {
|
|
|
|
|
transform: rotate(360deg);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-16 18:54:30 +08:00
|
|
|
.drawer-back {
|
2024-09-14 18:22:56 +08:00
|
|
|
@apply absolute bg-cover bg-center;
|
2023-12-22 09:50:03 +08:00
|
|
|
z-index: -1;
|
|
|
|
|
width: 200%;
|
|
|
|
|
height: 200%;
|
|
|
|
|
top: -50%;
|
|
|
|
|
left: -50%;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-27 14:39:52 +08:00
|
|
|
.drawer-back.paused {
|
|
|
|
|
animation-play-state: paused;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-09 11:34:09 +08:00
|
|
|
#drawer-target {
|
2024-10-18 18:37:53 +08:00
|
|
|
@apply top-0 left-0 absolute overflow-hidden rounded px-24 flex items-center justify-center w-full h-full pb-8;
|
2021-11-09 11:34:09 +08:00
|
|
|
animation-duration: 300ms;
|
2023-12-17 14:48:21 +08:00
|
|
|
|
2021-11-09 11:34:09 +08:00
|
|
|
.music-img {
|
2024-10-18 18:37:53 +08:00
|
|
|
@apply flex-1 flex justify-center mr-16 flex-col;
|
|
|
|
|
max-width: 360px;
|
|
|
|
|
max-height: 360px;
|
2021-11-09 11:34:09 +08:00
|
|
|
.img {
|
2024-10-18 18:37:53 +08:00
|
|
|
@apply rounded-xl w-full h-full shadow-2xl;
|
2021-11-09 11:34:09 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.music-content {
|
2024-10-18 18:37:53 +08:00
|
|
|
@apply flex flex-col justify-center items-center relative;
|
2023-12-17 14:48:21 +08:00
|
|
|
|
2021-11-09 11:34:09 +08:00
|
|
|
&-name {
|
2024-10-18 18:37:53 +08:00
|
|
|
@apply font-bold text-xl pb-1 pt-4;
|
2021-11-09 11:34:09 +08:00
|
|
|
}
|
2023-12-17 14:48:21 +08:00
|
|
|
|
2021-11-09 11:34:09 +08:00
|
|
|
&-singer {
|
2024-10-18 18:37:53 +08:00
|
|
|
@apply text-base;
|
2021-11-09 11:34:09 +08:00
|
|
|
}
|
|
|
|
|
}
|
2023-12-17 14:48:21 +08:00
|
|
|
|
2024-05-16 18:54:30 +08:00
|
|
|
.music-content-time {
|
2023-12-27 14:39:52 +08:00
|
|
|
display: none;
|
2024-05-16 18:54:30 +08:00
|
|
|
@apply flex justify-center items-center;
|
2023-12-27 14:39:52 +08:00
|
|
|
}
|
2021-11-09 11:34:09 +08:00
|
|
|
.music-lrc {
|
2023-12-17 14:48:21 +08:00
|
|
|
background-color: inherit;
|
2023-12-19 14:42:53 +08:00
|
|
|
width: 500px;
|
2021-11-09 11:34:09 +08:00
|
|
|
height: 550px;
|
2024-12-06 23:50:44 +08:00
|
|
|
mask-image: linear-gradient(to bottom, transparent 0%, black 10%, black 90%, transparent 100%);
|
2021-11-09 11:34:09 +08:00
|
|
|
&-text {
|
2024-10-18 18:37:53 +08:00
|
|
|
@apply text-2xl cursor-pointer font-bold px-2 py-4;
|
2024-11-23 22:42:23 +08:00
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
|
2024-10-18 18:37:53 +08:00
|
|
|
span {
|
|
|
|
|
padding-right: 100px;
|
2024-11-28 08:12:37 +08:00
|
|
|
// display: inline-block;
|
2024-11-23 22:42:23 +08:00
|
|
|
background-clip: text !important;
|
|
|
|
|
-webkit-background-clip: text !important;
|
2024-10-18 18:37:53 +08:00
|
|
|
}
|
2024-11-23 22:42:23 +08:00
|
|
|
|
2024-11-28 08:12:37 +08:00
|
|
|
&-tr {
|
|
|
|
|
@apply font-normal;
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
color: var(--text-color-primary);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.hover-text {
|
2021-11-09 11:34:09 +08:00
|
|
|
&:hover {
|
2024-10-18 18:37:53 +08:00
|
|
|
@apply font-bold opacity-100 rounded-xl;
|
2024-11-23 22:42:23 +08:00
|
|
|
background-color: var(--hover-bg-color);
|
|
|
|
|
|
|
|
|
|
span {
|
|
|
|
|
color: var(--text-color-active) !important;
|
|
|
|
|
}
|
2021-11-09 11:34:09 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-23 17:12:35 +08:00
|
|
|
|
|
|
|
|
.mobile {
|
|
|
|
|
#drawer-target {
|
|
|
|
|
@apply flex-col p-4 pt-8;
|
|
|
|
|
.music-img {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
.music-lrc {
|
|
|
|
|
height: calc(100vh - 260px) !important;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-11-23 22:42:23 +08:00
|
|
|
|
|
|
|
|
.music-drawer {
|
|
|
|
|
transition: none; // 移除之前的过渡效果,现在使用 JS 动画
|
|
|
|
|
}
|
2023-12-18 15:24:57 +08:00
|
|
|
</style>
|