Files
blog/src/components/aplayer.vue
T
youzi 925ff669df chore: 调整样式细节并移除节假日功能
- 恢复 aplayer 初始变换为 translateY(0) 和 translateX(0),并自动触发点击以初始化状态
- 为 aplayer 列表项添加字体大小 12px 样式
- 简化 homeSide 组件模板格式,统一代码风格
- 移除节假日获取与显示相关逻辑(getHoli 函数及对应模板)
2026-08-07 18:15:24 +08:00

235 lines
6.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<!-- 准备一个容器用来存放音乐播放器 -->
<div ref="containerRef" id="aplayer"></div>
</template>
<script setup lang="ts">
// https://aplayer.js.org/#/zh-Hans/
import APlayer from "aplayer"; // 引入音乐插件
import "aplayer/dist/APlayer.min.css"; // 引入音乐插件的样式
/** 歌单接口 */
interface MusicItem {
title: string;
author: string;
url: string;
pic: string;
lrc: string;
}
/** 歌单来源配置 */
const PLAYLIST_SERVER = "tencent";
const PLAYLIST_ID = "7681108669";
/** 歌单拉取接口(公共 meting API */
const METING_API = `https://meting.mikus.ink/api?server=${PLAYLIST_SERVER}&type=playlist&id=${PLAYLIST_ID}`;
/** APlayer 基础配置 */
const APLAYER_INFO = {
fixed: true, // 开启吸底模式
loop: "all", // 音频循环播放, 可选值: 'all', 'one', 'none'
order: "list", // 音频循环顺序, 可选值: 'list', 'random'
preload: "metadata", // 预加载,可选值: 'none', 'metadata', 'auto'
volume: 0.5, // 默认音量,可选值: 0-1
mutex: true, // 互斥,默认为true,即不允许多个音频同时播放,当前播放的音频会暂停其他音频
lrcType: 3, // 歌词类型,可选值: 1, 2, 3
};
const containerRef = ref<HTMLDivElement | null>(null);
let ap: InstanceType<typeof APlayer> | null = null;
let miniswitcherEl: HTMLElement | null = null;
let listScrollEl: HTMLElement | null = null;
let onClickMiniswitcher: (() => void) | null = null;
let onListWheel: ((e: Event) => void) | null = null;
/** 拉取歌单并解析为 MusicItem[] */
async function fetchPlaylist(): Promise<MusicItem[]> {
const res = await fetch(METING_API);
if (!res.ok) throw new Error(`歌单请求失败: ${res.status}`);
const mlist: MusicItem[] = await res.json();
return mlist;
}
/** 初始化迷你切换按钮的显隐动画(自定义展开/收起效果) */
function setupMiniswitcher() {
miniswitcherEl = document.querySelector("#aplayer > div.aplayer-body > div.aplayer-miniswitcher > button");
const app = document.querySelector<HTMLElement>("#aplayer > div.aplayer-lrc");
const apic = document.querySelector<HTMLElement>("#aplayer div.aplayer-body");
if (!miniswitcherEl || !app || !apic) return;
let expanded = false;
app.style.display = "block";
app.style.transform = "translateY(0)";
apic.style.transform = "translateX(0)";
onClickMiniswitcher = () => {
if (expanded) {
app.style.transform = "translateY(50px)";
ap.lrc?.hide();
ap.list.hide();
apic.style.transform = "translateX(-66px)";
expanded = false;
} else {
app.style.transform = "translateY(0)";
ap.lrc?.show();
apic.style.transform = "translateX(0)";
expanded = true;
}
};
miniswitcherEl.addEventListener("click", onClickMiniswitcher);
miniswitcherEl?.click()
}
/** 阻止歌单列表滚轮事件冒泡,避免联动外层滚动 */
function setupListWheelGuard() {
listScrollEl = document.querySelector(".aplayer-list > ol");
if (!listScrollEl) return;
onListWheel = (e: Event) => e.stopPropagation();
listScrollEl.addEventListener("mousewheel", onListWheel);
}
/** 移动端(宽度 < 767)自动收起播放器为迷你模式 */
function applyMobileCollapse() {
const width = document.documentElement.clientWidth;
if (width < 767) miniswitcherEl?.click();
}
onMounted(async () => {
try {
const audio = await fetchPlaylist();
ap = new APlayer({
container: containerRef.value ?? undefined,
audio, // 音乐信息
...APLAYER_INFO, // 其他配置信息
});
setupMiniswitcher();
setupListWheelGuard();
applyMobileCollapse();
} catch (error) {
console.error("音乐列表加载失败:", error);
}
});
onUnmounted(() => {
// 清理事件监听,避免内存泄漏
if (miniswitcherEl && onClickMiniswitcher) {
miniswitcherEl.removeEventListener("click", onClickMiniswitcher);
}
if (listScrollEl && onListWheel) {
listScrollEl.removeEventListener("mousewheel", onListWheel);
}
// 销毁 APlayer 实例
ap?.destroy();
ap = null;
});
</script>
<style lang="less" scoped>
#aplayer {
width: 480px; // 定个宽度
color: var(--n-text-color); // 设置颜色
background-color: #f5f5f8; // 设置背景色
:deep(.aplayer-body) {
color: var(--n-text-color); // 设置颜色
background-color: #f5f5f8; // 设置背景色
// bottom: 10px; // 设置底部距离
.aplayer-icon-lrc {
display: none;
}
}
:deep(.aplayer-icon):hover svg path {
fill: @blue; // 设置颜色
}
:deep(.aplayer-list) {
width: 418px !important;
background-color: #f5f5f8;
border: none;
}
:deep(.aplayer-list-light) {
font-size: 12px !important;
background: inherit;
color: @orange;
}
:deep(.aplayer-list) ol {
max-height: 160px !important;
// margin-bottom: 12px;
.aplayer-list-light .aplayer-list-author {
color: @orange !important;
}
}
:deep(.aplayer-list) ol li {
border: none;
}
:deep(.aplayer-list) ol li:hover {
background: inherit;
color: @blue;
border: none;
.aplayer-list-author {
color: @blue !important;
}
}
:deep(.aplayer-info) {
padding: 0 !important;
border-top: none;
}
:deep(.aplayer-icon) svg {
color: var(--n-text-color); // 设置颜色
background-color: var(--v-bgc); // 设置背景色
}
:deep(.aplayer-icon):hover svg {
color: @blue; // 设置颜色
}
:deep(.aplayer-miniswitcher) {
color: var(--n-text-color); // 设置颜色
background-color: var(--v-bgc); // 设置背景色
}
:deep(.aplayer-lrc) {
// text-align: right !important;
width: 368px !important;
transition: all 0.3s ease;
// left: unset;
bottom: 12px;
z-index: 999;
text-shadow: none !important;
}
:deep(.aplayer-lrc-contents) {
transition: all 0.5s ease !important;
p {
font-size: 12px !important;
color: @primary !important;
opacity: 0.8;
}
}
:deep(.aplayer-lrc-contents) {
.aplayer-lrc-current {
opacity: 1;
font-size: 13px !important;
color: @orange !important;
}
}
}
@media screen and (max-width: 800px) {
#aplayer {
width: 100vw !important;
}
}
</style>