惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
WordPress大学
WordPress大学
B
Blog RSS Feed
T
The Blog of Author Tim Ferriss
F
Fortinet All Blogs
aimingoo的专栏
aimingoo的专栏
MongoDB | Blog
MongoDB | Blog
博客园 - Franky
C
Check Point Blog
P
Proofpoint News Feed
H
Help Net Security
月光博客
月光博客
博客园_首页
Stack Overflow Blog
Stack Overflow Blog
博客园 - 三生石上(FineUI控件)
Martin Fowler
Martin Fowler
Recent Announcements
Recent Announcements
人人都是产品经理
人人都是产品经理
U
Unit 42
美团技术团队
I
InfoQ
A
About on SuperTechFans

Hyde Blog

Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog Hyde Blog
Hyde Blog
Hyde · 2026-04-04 · via Hyde Blog

vue

<!-- 网易云迷你播放器 -->
<template>
  <!-- 创建播放器容器 -->
  <div class="netease-mini-player" data-playlist-id="14273792576" data-embed="false" data-position="bottom-left"
    data-lyric="true" data-theme="auto" data-default-minimized="true"></div>
</template>

<script setup>
import { onMounted, onUnmounted } from 'vue';

// 动态引入播放器的 CSS 和 JS 文件
onMounted(() => {
  // 引入 CSS
  const cssLink = document.createElement('link');
  cssLink.rel = 'stylesheet';
  // cssLink.href = 'https://api.hypcvgm.top/NeteaseMiniPlayer/netease-mini-player-v2.css';
  // document.head.appendChild(cssLink);

  // 引入 JS 并确保加载完成后初始化
  const script = document.createElement('script');
  script.src = 'https://api.hypcvgm.top/NeteaseMiniPlayer/netease-mini-player-v2.js';
  script.async = true;
  script.onload = () => {
    console.log('网易云迷你播放器资源加载完成');
  };
  script.onerror = () => {
    console.error('网易云迷你播放器资源加载失败');
  };
  document.body.appendChild(script);
});

// 组件卸载时清理动态引入的资源
onUnmounted(() => {
  // 移除 CSS
  // const cssLinks = document.head.querySelectorAll('link[href*="netease-mini-player-v2.css"]');
  // cssLinks.forEach(link => document.head.removeChild(link));

  // 移除 JS
  const scripts = document.body.querySelectorAll('script[src*="netease-mini-player-v2.js"]');
  scripts.forEach(script => document.body.removeChild(script));

  // 清理播放器实例(如果播放器提供了销毁方法)
  if (window.neteaseMiniPlayer?.destroy) {
    window.neteaseMiniPlayer.destroy();
  }
});
</script>

<style>
/**
 * [NMPv2] NeteaseMiniPlayer v2 CSS 
 * Lightweight Player Component Based on NetEase Cloud Music API
 * 
 * Copyright 2025 BHCN STUDIO & 北海的佰川(ImBHCN[numakkiyu])
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
:root {
  --primary-bg: #fafafa;
  --secondary-bg: #f0f0f0;
  --bg-color: #fafafa;
  --accent-color: #FF6B35;
  --accent-color-2: #7A66FF;
  --accent-color-3: #00C2FF;
  --accent-gradient: linear-gradient(135deg, var(--accent-color) 0%, var(--accent-color-2) 50%, var(--accent-color-3) 100%);
  --text-primary: #333333;
  --text-secondary: #666666;
  --text-muted: #999999;
  --shadow-light: rgba(255, 255, 255, 0.9);
  --shadow-dark: rgba(0, 0, 0, 0.1);
  --shadow-inset: inset 3px 3px 6px var(--shadow-dark), inset -3px -3px 6px var(--shadow-light);
  --shadow-outset: 6px 6px 12px var(--shadow-dark), -6px -6px 12px var(--shadow-light);
  --border-radius: 12px;
  --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  --theme-transition: background-color 0.3s ease, color 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;

  --flow-color-1: rgba(255, 107, 53, 0.32);
  --flow-color-2: rgba(86, 151, 227, 0.26);
  --flow-color-3: rgba(255, 209, 102, 0.26);
  --flow-color-4: rgba(122, 102, 255, 0.28);
  --flow-color-5: rgba(0, 194, 255, 0.22);
  --flow-opacity: 0.45;
  --flow-speed: 10s;
  --volume-total-width: 60px;
  --playlist-flow-opacity: 0.25;
}

.netease-mini-player[data-theme="dark"] {
  --primary-bg: #1e1e1e;
  --secondary-bg: #2a2a2a;
  --bg-color: #1e1e1e;
  --accent-color: #FF8A50;
  --accent-color-2: #6C63FF;
  --accent-color-3: #00D4FF;
  --accent-gradient: linear-gradient(135deg, var(--accent-color) 0%, var(--accent-color-2) 50%, var(--accent-color-3) 100%);
  --text-primary: #e0e0e0;
  --text-secondary: #b0b0b0;
  --text-muted: #808080;
  --shadow-light: rgba(255, 255, 255, 0.05);
  --shadow-dark: rgba(0, 0, 0, 0.4);
  --shadow-inset: inset 3px 3px 6px var(--shadow-dark), inset -3px -3px 6px var(--shadow-light);
  --shadow-outset: 6px 6px 12px var(--shadow-dark), -6px -6px 12px var(--shadow-light);

  --flow-color-1: rgba(255, 138, 80, 0.28);
  --flow-color-2: rgba(86, 151, 227, 0.24);
  --flow-color-3: rgba(140, 100, 255, 0.26);
  --flow-color-4: rgba(124, 110, 255, 0.24);
  --flow-color-5: rgba(0, 212, 255, 0.22);
  --flow-opacity: 0.36;
  --flow-speed: 12s;
}

@media (prefers-color-scheme: dark) {
  .netease-mini-player[data-theme="auto"] {
    --primary-bg: #1e1e1e;
    --secondary-bg: #2a2a2a;
    --bg-color: #1e1e1e;
    --accent-color: #FF8A50;
    --accent-color-2: #6C63FF;
    --accent-color-3: #00D4FF;
    --accent-gradient: linear-gradient(135deg, var(--accent-color) 0%, var(--accent-color-2) 50%, var(--accent-color-3) 100%);
    --text-primary: #e0e0e0;
    --text-secondary: #b0b0b0;
    --text-muted: #808080;
    --shadow-light: rgba(255, 255, 255, 0.05);
    --shadow-dark: rgba(0, 0, 0, 0.4);
    --shadow-inset: inset 3px 3px 6px var(--shadow-dark), inset -3px -3px 6px var(--shadow-light);
    --shadow-outset: 6px 6px 12px var(--shadow-dark), -6px -6px 12px var(--shadow-light);

    --flow-color-1: rgba(255, 138, 80, 0.28);
    --flow-color-2: rgba(86, 151, 227, 0.24);
    --flow-color-3: rgba(140, 100, 255, 0.26);
    --flow-color-4: rgba(124, 110, 255, 0.24);
    --flow-color-5: rgba(0, 212, 255, 0.22);
    --flow-opacity: 0.36;
    --flow-speed: 12s;
  }
}

@media (prefers-color-scheme: light) {
  .netease-mini-player[data-theme="auto"] {
    --primary-bg: #fafafa;
    --secondary-bg: #f0f0f0;
    --bg-color: #fafafa;
    --accent-color: #FF6B35;
    --accent-color-2: #7A66FF;
    --accent-color-3: #00C2FF;
    --accent-gradient: linear-gradient(135deg, var(--accent-color) 0%, var(--accent-color-2) 50%, var(--accent-color-3) 100%);
    --text-primary: #333333;
    --text-secondary: #666666;
    --text-muted: #999999;
    --shadow-light: rgba(255, 255, 255, 0.9);
    --shadow-dark: rgba(0, 0, 0, 0.1);
    --shadow-inset: inset 3px 3px 6px var(--shadow-dark), inset -3px -3px 6px var(--shadow-light);
    --shadow-outset: 6px 6px 12px var(--shadow-dark), -6px -6px 12px var(--shadow-light);

    --flow-color-1: rgba(255, 107, 53, 0.32);
    --flow-color-2: rgba(86, 151, 227, 0.26);
    --flow-color-3: rgba(255, 209, 102, 0.26);
    --flow-color-4: rgba(122, 102, 255, 0.28);
    --flow-color-5: rgba(0, 194, 255, 0.22);
    --flow-opacity: 0.45;
    --flow-speed: 10s;
  }
}

.netease-mini-player {
  width: 400px;
  height: 120px;
  background: var(--bg-color);
  border-radius: 16px;
  padding: 12px;
  box-shadow: var(--shadow-outset);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  position: relative;
  overflow: visible;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 8px;
  transition:
    width 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    border-radius 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    padding 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    var(--theme-transition);
  background: 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

.netease-mini-player::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background:
    radial-gradient(circle at 15% 20%, var(--flow-color-1) 0%, transparent 60%),
    radial-gradient(circle at 80% 25%, var(--flow-color-2) 0%, transparent 60%),
    radial-gradient(circle at 30% 85%, var(--flow-color-3) 0%, transparent 60%),
    radial-gradient(circle at 10% 75%, var(--flow-color-4) 0%, transparent 55%),
    radial-gradient(circle at 85% 80%, var(--flow-color-5) 0%, transparent 55%);
  opacity: var(--flow-opacity);
  background-size: 220% 220%, 220% 220%, 220% 220%, 220% 220%, 220% 220%;
  background-position: 0% 0%, 100% 0%, 0% 100%, 20% 80%, 80% 20%;
  filter: saturate(1.02) brightness(1.01);
  transform: scale(1);
  animation: flow-breathe var(--flow-speed) ease-in-out infinite;
  animation-play-state: paused;
}

.netease-mini-player.player-playing::before {
  animation-play-state: running;
}

@keyframes flow-breathe {
  0% {
    background-position: 0% 0%, 100% 0%, 0% 100%, 20% 80%, 80% 20%;
    transform: scale(1);
    filter: saturate(1.02) brightness(1.01);
  }

  50% {
    background-position: 100% 50%, 0% 100%, 50% 0%, 35% 65%, 65% 35%;
    transform: scale(1.03);
    filter: saturate(1.15) brightness(1.06);
  }

  100% {
    background-position: 0% 100%, 100% 50%, 100% 0%, 20% 80%, 80% 20%;
    transform: scale(1);
    filter: saturate(1.02) brightness(1.01);
  }
}

.netease-mini-player.minimized {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  padding: 0;
  background: radial-gradient(120px 120px at 30% 30%, rgba(255, 138, 80, 0.25), transparent 60%),
    radial-gradient(120px 120px at 70% 70%, rgba(0, 210, 255, 0.22), transparent 60%),
    linear-gradient(135deg, #151515, #1e1e1e);
  box-shadow:
    4px 4px 12px rgba(0, 0, 0, 0.4),
    -2px -2px 8px rgba(255, 255, 255, 0.1),
    inset 0 0 20px rgba(0, 0, 0, 0.3);
}

.netease-mini-player.minimized .song-content,
.netease-mini-player.minimized .controls,
.netease-mini-player.minimized .playlist-container {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 0.3s ease,
    visibility 0.3s ease;
}

.netease-mini-player.minimized .player-bottom {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 0.3s ease,
    visibility 0.3s ease;
}

.netease-mini-player.minimized .minimize-btn {
  opacity: 1 !important;
  visibility: visible !important;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1000;
  background: rgba(0, 0, 0, 0.7);
  border-radius: 50%;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  transition: all 0.3s ease;
}

.netease-mini-player.minimized .minimize-btn:hover {
  background: rgba(0, 0, 0, 0.9);
  transform: translate(-50%, -50%) scale(1.1);
}

.netease-mini-player .song-content,
.netease-mini-player .controls,
.netease-mini-player .player-bottom {
  opacity: 1;
  visibility: visible;
  transition:
    opacity 0.3s ease 0.2s,
    visibility 0.3s ease 0.2s;
}

.netease-mini-player.minimized .album-cover-container {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  border-radius: 50%;
  box-shadow: none;
  transition:
    width 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    top 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    left 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

.netease-mini-player.minimized .album-cover {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  filter: brightness(0.8) contrast(1.1);
  transition:
    width 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    filter 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

.netease-mini-player.minimized .vinyl-overlay {
  background: radial-gradient(circle at center,
      transparent 15%,
      rgba(0, 0, 0, 0.1) 15%,
      rgba(0, 0, 0, 0.1) 16%,
      transparent 16%,
      transparent 25%,
      rgba(0, 0, 0, 0.15) 25%,
      rgba(0, 0, 0, 0.15) 26%,
      transparent 26%,
      transparent 35%,
      rgba(0, 0, 0, 0.1) 35%,
      rgba(0, 0, 0, 0.1) 36%,
      transparent 36%,
      transparent 45%,
      rgba(0, 0, 0, 0.15) 45%,
      rgba(0, 0, 0, 0.15) 46%,
      transparent 46%);
  box-shadow:
    inset 0 0 15px rgba(0, 0, 0, 0.5),
    inset 0 0 5px rgba(0, 0, 0, 0.8);
  transition:
    background 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

.netease-mini-player.minimized .vinyl-center {
  width: 16px;
  height: 16px;
  background: radial-gradient(circle at 30% 30%,
      #666 0%,
      #333 50%,
      #111 100%);
  box-shadow:
    0 0 3px rgba(0, 0, 0, 0.8),
    inset 1px 1px 2px rgba(255, 255, 255, 0.2);
  border: 1px solid #000;
  transition:
    width 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    background 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1),
    border 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}


.netease-mini-player[data-position="top-left"] {
  position: fixed;
  top: 20px;
  left: 20px;
  z-index: 1000;
}

.netease-mini-player[data-position="top-right"] {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 1000;
}

.netease-mini-player[data-position="bottom-left"] {
  position: fixed;
  bottom: 20px;
  left: 20px;
  z-index: 1000;
}

.netease-mini-player[data-position="bottom-right"] {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 1000;
}

.netease-mini-player[data-position="static"] {
  position: relative;
}

.player-main {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 1;
}

.album-cover-container {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  position: relative;
  flex-shrink: 0;
  background: var(--secondary-bg);
  box-shadow:
    inset 2px 2px 4px var(--shadow-dark),
    inset -2px -2px 4px var(--shadow-light),
    2px 2px 6px rgba(0, 0, 0, 0.2),
    -1px -1px 3px rgba(255, 255, 255, 0.1);
  overflow: hidden;
  cursor: pointer;
  border: 2px solid rgba(0, 0, 0, 0.1);
}

.album-cover-container::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  pointer-events: none;
  background: conic-gradient(from 0deg, var(--accent-color), var(--accent-color-2), var(--accent-color-3), var(--accent-color));
  opacity: 0.18;
  filter: blur(6px) saturate(1.05);
}

.album-cover {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  transition: var(--transition);
  filter: brightness(0.9) contrast(1.1);
}

.vinyl-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: radial-gradient(circle at center,
      transparent 12%,
      rgba(0, 0, 0, 0.8) 12%,
      rgba(0, 0, 0, 0.8) 15%,
      transparent 15%,
      transparent 20%,
      rgba(0, 0, 0, 0.15) 20%,
      rgba(0, 0, 0, 0.15) 20.5%,
      transparent 20.5%,
      transparent 25%,
      rgba(0, 0, 0, 0.1) 25%,
      rgba(0, 0, 0, 0.1) 25.5%,
      transparent 25.5%,
      transparent 30%,
      rgba(0, 0, 0, 0.08) 30%,
      rgba(0, 0, 0, 0.08) 30.5%,
      transparent 30.5%,
      transparent 35%,
      rgba(0, 0, 0, 0.06) 35%,
      rgba(0, 0, 0, 0.06) 35.5%,
      transparent 35.5%,
      transparent 40%,
      rgba(0, 0, 0, 0.05) 40%,
      rgba(0, 0, 0, 0.05) 40.5%,
      transparent 40.5%,
      transparent 85%,
      rgba(0, 0, 0, 0.1) 85%,
      rgba(0, 0, 0, 0.2) 100%);
  pointer-events: none;
  box-shadow:
    inset 0 0 10px rgba(0, 0, 0, 0.3),
    inset 0 0 20px rgba(0, 0, 0, 0.1);
}

.vinyl-center {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 12px;
  height: 12px;
  background: radial-gradient(circle at center,
      #1a1a1a 0%,
      #333 50%,
      #1a1a1a 100%);
  border-radius: 50%;
  transform: translate(-50%, -50%);
  box-shadow:
    0 0 4px rgba(0, 0, 0, 0.5),
    inset 0 0 3px rgba(0, 0, 0, 0.8),
    inset 0 0 1px rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.1);
}

@keyframes vinyl-spin {
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}

.album-cover {
  animation: vinyl-spin 6s linear infinite;
  animation-play-state: paused;
}

.album-cover.playing {
  animation-play-state: running;
}

.vinyl-overlay {
  animation: vinyl-spin 3s linear infinite;
  animation-play-state: paused;
}

.album-cover.playing+.vinyl-overlay {
  animation-play-state: running;
}

.song-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.song-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.song-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.2;
  transition: var(--theme-transition);
}

.song-artist {
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.2;
  transition: var(--theme-transition);
}

.lyrics-container {
  min-height: 20px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.lyrics-container.hidden {
  display: none;
}

.lyric-line {
  font-size: 11px;
  color: #5697e3;
  line-height: 1.4;
  transition: var(--transition);
  opacity: 0.8;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 100%;
  transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
}

.lyric-line.current {
  color: #5697e3;
  font-weight: 600;
  opacity: 1;
  text-shadow: 0 0 8px rgba(86, 151, 227, 0.3);
  animation: lyric-glow 2s ease-in-out infinite alternate;

  animation: none;
  opacity: 0;
  transform: translateY(10px);
  animation: lyric-fade-in 0.2s ease-out forwards;
}

.lyric-line.translation {
  font-size: 10px;
  color: var(--text-muted);
  opacity: 0.7;
  margin-top: 1px;
  transition: var(--theme-transition);
}

.lyric-line.translation.current {
  color: var(--text-secondary);
  opacity: 0.9;
  font-weight: 500;
  transition: var(--theme-transition);
}

@keyframes lyric-fade-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes lyric-glow {
  0% {
    text-shadow: 0 0 8px rgba(86, 151, 227, 0.3);
  }

  100% {
    text-shadow: 0 0 12px rgba(86, 151, 227, 0.5);
  }
}

.lyric-line.word-lyric {
  position: relative;
  display: inline-block;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
}

.lyric-word-line {
  position: relative;
  display: inline-block;
  transition: transform 0.1s ease-out;
}

.lyric-line.word-lyric-active {
  -webkit-mask: linear-gradient(90deg, #000 0%, #000 var(--line-progress, 0%), transparent var(--line-progress, 0%));
  mask: linear-gradient(90deg, #000 0%, #000 var(--line-progress, 0%), transparent var(--line-progress, 0%));
}

.netease-mini-player[data-theme="dark"] .lyric-line.word-lyric-active {
  text-shadow: 0 0 8px rgba(255, 165, 0, 0.7);
}

.lyric-line.word-lyric::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  width: var(--line-progress, 0%);
  height: 2px;
  background: var(--accent-gradient);
  border-radius: 1px;
  transition: width 0.1s ease;
}

.controls {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.control-btn {
  width: 32px;
  height: 32px;
  border: none;
  background: var(--primary-bg);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: var(--transition);
  box-shadow:
    2px 2px 4px var(--shadow-dark),
    -2px -2px 4px var(--shadow-light);
  font-size: 12px;
  color: var(--text-primary);
  position: relative;
}

.control-btn:hover {
  box-shadow:
    inset 1px 1px 2px var(--shadow-dark),
    inset -1px -1px 2px var(--shadow-light);
  background: var(--secondary-bg);
}

.play-btn {
  width: 30px;
  height: 30px;
  font-size: 14px;
  background: var(--primary-bg);
  box-shadow:
    2px 2px 4px var(--shadow-dark),
    -2px -2px 4px var(--shadow-light);
}

.play-icon,
.pause-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  color: var(--text-primary);
  transition: var(--transition);
}

.play-btn:hover {
  background: var(--secondary-bg);
  box-shadow:
    inset 1px 1px 2px var(--shadow-dark),
    inset -1px -1px 2px var(--shadow-light);
}

.control-btn:active {
  box-shadow:
    inset 2px 2px 4px var(--shadow-dark),
    inset -2px -2px 4px var(--shadow-light);
  background: var(--accent-gradient);
  color: white;
}

.control-btn:active span {
  color: white;
}

.play-icon {
  margin-left: 2px;
}

.volume-icon {
  font-size: 10px;
  color: var(--text-secondary);
  margin-right: 2px;
  flex-shrink: 0;
}

.player-bottom {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  width: 100%;
}

.progress-container {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1;
  max-width: var(--progress-max, 60%);
}

.netease-mini-player[data-embed="true"] .progress-container {
  max-width: calc(100% - 60px);
  flex: 1;
}

.time-display {
  font-size: 10px;
  color: var(--text-muted);
  white-space: nowrap;
  min-width: 35px;
  text-align: center;
}

.progress-bar-container {
  flex: 1;
  height: 4px;
  background: var(--secondary-bg);
  border-radius: 2px;
  position: relative;
  cursor: pointer;
  box-shadow:
    inset 1px 1px 2px var(--shadow-dark),
    inset -1px -1px 2px var(--shadow-light);
}

.progress-bar {
  height: 100%;
  background: var(--accent-gradient);
  border-radius: 2px;
  width: 0%;
  transition: width 0.1s ease;
  position: relative;
}

.progress-bar::after {
  content: '';
  position: absolute;
  right: -2px;
  top: 50%;
  width: 8px;
  /* 和顶部滚动样式冲突 ,暂时注释 */
  /* height: 8px;
  background: var(--accent-color-3); */
  border-radius: 50%;
  transform: translateY(-50%);
  box-shadow:
    0 0 8px rgba(0, 210, 255, 0.6),
    1px 1px 2px var(--shadow-dark),
    -1px -1px 2px var(--shadow-light);
}

.bottom-controls {
  display: flex;
  align-items: center;
  gap: 8px;
  justify-content: flex-end;
  flex-shrink: 0;
  min-width: 0;
}

.volume-container {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.volume-slider-container {
  display: flex;
  align-items: center;
  width: 40px;
}

.volume-slider {
  flex: 1;
  height: 3px;
  background: var(--secondary-bg);
  border-radius: 2px;
  position: relative;
  cursor: pointer;
  box-shadow:
    inset 1px 1px 2px var(--shadow-dark),
    inset -1px -1px 2px var(--shadow-light);
}

.volume-bar {
  height: 100%;
  background: var(--accent-color);
  border-radius: 2px;
  width: 70%;
  transition: width 0.1s ease;
}

.netease-mini-player svg {
  width: 100%;
  height: 100%;
  fill: currentColor;
  display: block;
}

.control-btn svg {
  width: 18px;
  height: 18px;
}

.feature-btn svg {
  width: 16px;
  height: 16px;
}

.volume-icon svg {
  width: 14px;
  height: 14px;
}

.play-btn svg {
  width: 22px;
  height: 22px;
  margin-left: 2px;
}

.play-btn .pause-icon svg {
  margin-left: 0;
}

.control-btn,
.feature-btn {
  font-size: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.minimize-btn svg {
  width: 18px;
  height: 18px;
}

.sr-visually-hidden {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0 0 0 0) !important;
  clip-path: inset(50%) !important;
  white-space: nowrap !important;
  border: 0 !important;
  padding: 0 !important;
}

.netease-mini-player.mobile-env .volume-container {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;
}

.netease-mini-player.mobile-env .progress-container {
  --progress-max: calc(60% + var(--volume-total-width, 60px));
}

.netease-mini-player.mobile-env[data-embed="true"] .progress-container {
  max-width: calc(100% - 60px + var(--volume-total-width, 60px));
}

@media (hover: none) and (pointer: coarse) {
  .netease-mini-player.mobile-env .bottom-controls {
    gap: 10px;
  }
}

.feature-btn {
  width: 20px;
  height: 20px;
  border: none;
  background: transparent;
  cursor: pointer;
  color: var(--text-muted);
  transition: var(--transition);
  font-size: 11px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.feature-btn:hover {
  color: var(--accent-color);
}

.feature-btn.active {
  color: var(--accent-color);
}

.netease-mini-player[data-embed="true"] .list-btn {
  display: none !important;
}

.netease-mini-player[data-embed="true"] .minimize-btn {
  display: none !important;
}

.netease-mini-player[data-position="static"] .minimize-btn {
  display: none !important;
}

.netease-mini-player[data-embed="true"] .prev-btn,
.netease-mini-player[data-embed="true"] .next-btn {
  display: none !important;
}

.netease-mini-player[data-embed="true"] .playlist-container {
  display: none !important;
}

.netease-mini-player-embed .list-btn,
.netease-mini-player-embed .minimize-btn,
.netease-mini-player-embed .prev-btn,
.netease-mini-player-embed .next-btn {
  display: none !important;
}

.netease-mini-player-embed .playlist-container {
  display: none !important;
}

.playlist-container {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  background: var(--primary-bg);
  border-radius: var(--border-radius);
  box-shadow:
    4px 4px 8px var(--shadow-dark),
    -4px -4px 8px var(--shadow-light);
  max-height: 200px;
  overflow: hidden;
  z-index: 1001;
  opacity: 0;
  transform: translateY(-10px);
  visibility: hidden;
  transition: all 0.3s ease;
  margin-top: 8px;
}

.playlist-container::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background:
    radial-gradient(circle at 20% 15%, var(--flow-color-1) 0%, transparent 60%),
    radial-gradient(circle at 75% 30%, var(--flow-color-2) 0%, transparent 55%),
    radial-gradient(circle at 35% 85%, var(--flow-color-3) 0%, transparent 55%),
    radial-gradient(circle at 25% 70%, var(--flow-color-4) 0%, transparent 50%),
    radial-gradient(circle at 85% 75%, var(--flow-color-5) 0%, transparent 50%);
  opacity: var(--playlist-flow-opacity);
  background-size: 220% 220%, 220% 220%, 220% 220%, 220% 220%, 220% 220%;
  background-position: 0% 0%, 100% 0%, 0% 100%, 20% 80%, 80% 20%;
  filter: saturate(1.02) brightness(1.01);
  transform: scale(1);
  animation: flow-breathe var(--flow-speed) ease-in-out infinite;
  animation-play-state: paused;
}

.netease-mini-player.player-playing .playlist-container::before {
  animation-play-state: running;
}

.playlist-container.expand-up {
  top: auto;
  bottom: 100%;
  margin-top: 0;
  margin-bottom: 8px;
  transform: translateY(10px);
}

.playlist-container.expand-up.show {
  transform: translateY(0);
}

.playlist-container.show {
  opacity: 1;
  transform: translateY(0);
  visibility: visible;
}

.playlist-content {
  max-height: 200px;
  overflow-y: auto;

  &::-webkit-scrollbar {
    display: none;
  }

  scrollbar-width: none;
}


.playlist-item {
  padding: 8px 12px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
  cursor: pointer;
  transition: var(--transition);
  display: flex;
  align-items: center;
  gap: 8px;
}

.playlist-item:last-child {
  border-bottom: none;
}

.playlist-item:hover {
  background: rgba(255, 107, 53, 0.08);
  box-shadow:
    inset 1px 1px 2px var(--shadow-dark),
    inset -1px -1px 2px var(--shadow-light);
  transform: translateX(2px);
  transition: all 0.2s ease;
}

.playlist-item.active {
  background: rgba(255, 107, 53, 0.15);
  color: var(--accent-color);
  font-weight: 600;
}

.playlist-item-index {
  font-size: 10px;
  color: var(--text-muted);
  min-width: 16px;
  text-align: center;
}

.playlist-item.active .playlist-item-index {
  color: var(--accent-color);
}

.playlist-item-info {
  flex: 1;
  min-width: 0;
}

.playlist-item-name {
  font-size: 11px;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.2;
}

.playlist-item-artist {
  font-size: 10px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 1px;
}

.playlist-item.active .playlist-item-name,
.playlist-item.active .playlist-item-artist {
  color: var(--accent-color);
}

.loading {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 12px;
}

.loading::after {
  content: '';
  width: 12px;
  height: 12px;
  border: 2px solid var(--secondary-bg);
  border-top: 2px solid var(--accent-color);
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-left: 8px;
}

.playlist-item-cover {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  object-fit: cover;
  flex-shrink: 0;
  background: var(--secondary-bg);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

.error {
  color: #ff4757;
  font-size: 12px;
  text-align: center;
}

@media (max-width: 480px) {
  .netease-mini-player {
    width: 100%;
    max-width: 360px;
    height: 100px;
    padding: 8px;
    margin: 0 auto;
  }

  .album-cover-container {
    width: 50px;
    height: 50px;
  }

  .song-title {
    font-size: 12px;
  }

  .song-artist {
    font-size: 10px;
  }

  .lyric-line {
    font-size: 10px;
  }

  .control-btn {
    width: 28px;
    height: 28px;
    font-size: 10px;
  }

  .play-btn {
    width: 32px;
    height: 32px;
    font-size: 12px;
  }

  .time-display {
    font-size: 9px;
  }

  .feature-btn {
    width: 20px;
    height: 20px;
    font-size: 10px;
  }

  .volume-slider-container {
    width: 50px;
  }
}

.netease-mini-player.dragging {
  cursor: grabbing;
  -webkit-user-select: none;
  user-select: none;
  pointer-events: none;
  opacity: 0.8;
  transform: scale(0.95);
  z-index: 9999;
  transition: none;
}

.netease-mini-player {
  opacity: 1;
}

.netease-mini-player.minimized.idle {
  opacity: 0.7;
}

.netease-mini-player.minimized.fading-out {
  animation: player-fade-out var(--opacity-duration-down, 0.6s) var(--opacity-ease-out, cubic-bezier(0.22, 1, 0.36, 1)) forwards;
}

.netease-mini-player.minimized.fading-in {
  animation: player-fade-in var(--opacity-duration-up, 0.25s) var(--opacity-ease-in, cubic-bezier(0.4, 0, 0.2, 1)) forwards;
}

@keyframes player-fade-out {
  from {
    opacity: 1;
  }

  to {
    opacity: 0.7;
  }
}

@keyframes player-fade-in {
  from {
    opacity: 0.7;
  }

  to {
    opacity: 1;
  }
}

.netease-mini-player.minimized.docked-right {
  transform: translateX(calc(50% + var(--dock-gap, 20px)));
}

.netease-mini-player.minimized.docked-left {
  transform: translateX(calc(-50% - var(--dock-gap, 20px)));
}

.netease-mini-player.minimized.fading-out.docked-right {
  animation:
    player-fade-out var(--opacity-duration-down, 0.6s) var(--opacity-ease-out, cubic-bezier(0.22, 1, 0.36, 1)) forwards,
    player-dock-right var(--dock-duration, 0.45s) var(--dock-ease-out, cubic-bezier(0.18, 0.9, 0.2, 1.0)) forwards;
}

.netease-mini-player.minimized.fading-out.docked-left {
  animation:
    player-fade-out var(--opacity-duration-down, 0.6s) var(--opacity-ease-out, cubic-bezier(0.22, 1, 0.36, 1)) forwards,
    player-dock-left var(--dock-duration, 0.45s) var(--dock-ease-out, cubic-bezier(0.18, 0.9, 0.2, 1.0)) forwards;
}

.netease-mini-player.minimized.popping-right {
  animation: player-popout-right var(--popout-duration, 0.28s) var(--popout-ease, cubic-bezier(0.4, 0, 0.2, 1)) forwards;
}

.netease-mini-player.minimized.popping-left {
  animation: player-popout-left var(--popout-duration, 0.28s) var(--popout-ease, cubic-bezier(0.4, 0, 0.2, 1)) forwards;
}

@keyframes player-dock-right {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(calc(50% + var(--dock-gap, 20px)));
  }
}

@keyframes player-dock-left {
  from {
    transform: translateX(0);
  }

  to {
    transform: translateX(calc(-50% - var(--dock-gap, 20px)));
  }
}

@keyframes player-popout-right {
  from {
    transform: translateX(calc(50% + var(--dock-gap, 20px)));
  }

  to {
    transform: translateX(0);
  }
}

@keyframes player-popout-left {
  from {
    transform: translateX(calc(-50% - var(--dock-gap, 20px)));
  }

  to {
    transform: translateX(0);
  }
}

.netease-mini-player .feature-btn,
.netease-mini-player .control-btn {
  font-size: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.netease-mini-player .bottom-controls .feature-btn,
.netease-mini-player .volume-icon {
  color: var(--text-primary);
}

.netease-mini-player .control-btn svg {
  width: 18px;
  height: 18px;
  display: block;
  fill: currentColor;
}

.netease-mini-player .play-btn svg {
  width: 20px;
  height: 20px;
}

.netease-mini-player .play-icon {
  margin-left: 0;
}

.netease-mini-player .bottom-controls .feature-btn svg,
.netease-mini-player .volume-icon svg {
  width: 16px;
  height: 16px;
  display: block;
  fill: currentColor;
}

.netease-mini-player .volume-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  font-size: 0;
}

.netease-mini-player .feature-btn {
  width: auto;
  height: auto;
}

.netease-mini-player .bottom-controls {
  gap: 6px;
}

.netease-mini-player .volume-icon svg {
  width: 18px;
  height: 18px;
}

.netease-mini-player.minimized .minimize-btn {
  font-size: 0;
}
</style>

vue

<script setup>
import NeteaseMusicPlayer from "./NeteaseMusicPlayer.vue";
</script>

<template>
  <Teek.Layout>
    <template #layout-top>
      <!-- 全局网易云音乐播放器组件 -->
        <template #layout-bottom>
            <NeteaseMusicPlayer />
        </template>
    </template>
  </Teek.Layout>
</template>