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

推荐订阅源

奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园_首页
大猫的无限游戏
大猫的无限游戏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog
B
Blog RSS Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
P
Proofpoint News Feed
MyScale Blog
MyScale Blog
Engineering at Meta
Engineering at Meta
量子位
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Tailwind CSS Blog
Stack Overflow Blog
Stack Overflow Blog
N
Netflix TechBlog - Medium
T
The Blog of Author Tim Ferriss
U
Unit 42
aimingoo的专栏
aimingoo的专栏
博客园 - 叶小钗
博客园 - 【当耐特】
云风的 BLOG
云风的 BLOG
博客园 - Franky
博客园 - 聂微东

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>