方案一:频谱可视化背景(推荐)

这种效果是最常见的,也是最能体现“炫酷”感的。我们需要用到 Web Audio API 和 Canvas。

  1. HTML 结构 (无需大改动):
<div class="mt-3 bg-white pt-2 pl-3 card" style="margin-top: 1px; border: 1px solid; padding: 2em; overflow: hidden; position: relative;">
    <canvas id="audioVisualizer" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 0;"></canvas>
    <div class="viny" style="text-align: center; position: relative; z-index: 1;">
        <dl>
            <dd id="songTitle" style="font-size: 18px; margin-top: 20px;">爱一点</dd>
            <dd id="songArtist" style="color: #888;">歌手:莫艳琳</dd>
            <dd id="songAlbum" style="color: #888;">专辑:宅情歌</dd>
            <dd style="margin-top: 20px;">
                <audio id="musicPlayer" controls style="width: 100%;" data-src="{% static 'music/aiyidian.mp3' %}">
                    Your browser does not support the audio element.
                </audio>

                <!-- 调整下一曲按钮样式,使其不那么显眼 -->
                <button id="playNextBtn" class="plyr__controls__item plyr__controls__item--custom" aria-label="播放" type="button" style="background-color: #f2f2f2; color: #666; border: 1px solid #ccc; border-radius: 3px; padding: 5px 10px;">
                    <i class="fas fa-play"></i>
                </button>
            </dd>
        </dl>
    </div>
</div>
  • 关键点:在原来的 .card 容器内,添加一个 <canvas> 元素,并将其 position 设为 absolute,使其覆盖整个容器作为背景。
  • 同时,给原有的 .viny 内容设置 position: relative; z-index: 1; 确保它显示在 Canvas 之上。
  1. JavaScript 代码 (核心):
<script>
document.addEventListener('DOMContentLoaded', () => {
    const player = document.getElementById('musicPlayer');
    const playNextBtn = document.getElementById('playNextBtn');
    const songTitle = document.getElementById('songTitle');
    const songArtist = document.getElementById('songArtist');
    const songAlbum = document.getElementById('songAlbum');
    const canvas = document.getElementById('audioVisualizer');
    const ctx = canvas.getContext('2d');

    let audioContext;
    let analyser;
    let source;
    let bufferLength;
    let dataArray;

    // 歌曲列表
    const songs = [
        {
            title: '爱一点',
            artist: '莫艳琳',
            album: '宅情歌',
            src: '{% static "music/aiyidian.mp3" %}'
        },
        {
            title: '我很快乐',
            artist: '刘惜君',
            album: '《我很快乐》',
            src: '{% static "music/我很快乐.mp3" %}'
        },
        {
            title: '晚安大小姐',
            artist: 'ASMRZ',
            album: '잘자요 아가씨',
            src: '{% static "music/晚安大小姐.m4a" %}'
        }
        // 添加更多歌曲...
    ];

    // 当前播放歌曲索引
    let currentSongIndex = 0;

    // 更新歌曲信息
    function updateSongInfo() {
        songTitle.textContent = songs[currentSongIndex].title;
        songArtist.textContent = '歌手:' + songs[currentSongIndex].artist;
        songAlbum.textContent = '专辑:' + songs[currentSongIndex].album;
    }

    // 播放状态标志
    let isPlaying = false;

    // 初始化音频上下文和分析器
    function initAudio() {
        if (!audioContext) {
            audioContext = new (window.AudioContext || window.webkitAudioContext)();
            analyser = audioContext.createAnalyser();
            analyser.fftSize = 2048; // 调整精度
            bufferLength = analyser.frequencyBinCount;
            dataArray = new Uint8Array(bufferLength);
            source = audioContext.createMediaElementSource(player);
            source.connect(analyser);
            analyser.connect(audioContext.destination);
        }
    }
    function setCanvasSize(){
            canvas.width = canvas.offsetWidth;  // 使用 offsetWidth 和 offsetHeight
             canvas.height = canvas.offsetHeight;

    }
    // 绘制频谱
    function drawSpectrum() {
         requestAnimationFrame(drawSpectrum);
         analyser.getByteFrequencyData(dataArray);

         ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除画布

         const barWidth = (canvas.width / bufferLength) * 2.5;
         let barHeight;
         let x = 0;

         for (let i = 0; i < bufferLength; i++) {
             barHeight = dataArray[i] * (canvas.height / 255); // 根据音量调整高度

             // 设置颜色渐变
             const r = barHeight + (25 * (i / bufferLength));
             const g = 250 * (i / bufferLength);
             const b = 50;
             ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;

             ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);

             x += barWidth + 1;
         }
     }

    // 添加播放/下一曲按钮的点击事件
    playNextBtn.addEventListener('click', () => {
         if (!audioContext) {
             initAudio(); // 首次点击时初始化
         }
 
        if (!isPlaying) {
            // 如果未播放,则播放当前歌曲
            player.src = songs[currentSongIndex].src;
            player.load();
            player.play();
            isPlaying = true;
            playNextBtn.innerHTML = '<i class="fas fa-forward"></i>'; // 播放时显示下一曲图标
        } else {
            // 如果已经在播放,则切换到下一首歌曲
            currentSongIndex = (currentSongIndex + 1) % songs.length;
            player.src = songs[currentSongIndex].src;
            player.load();
            player.play();
            updateSongInfo();
        }
        setCanvasSize()
    });

    // 添加播放事件监听器
    player.addEventListener('play', () => {
        isPlaying = true;
        playNextBtn.innerHTML = '<i class="fas fa-forward"></i>'; // 播放时显示下一曲图标
         if (!audioContext) { //确保音频初始化
                 initAudio();
         }
        drawSpectrum(); // 开始绘制频谱
        setCanvasSize()
    });

    // 添加暂停事件监听器
    player.addEventListener('pause', () => {
        isPlaying = false;
        playNextBtn.innerHTML = '<i class="fas fa-play"></i>'; // 暂停时显示播放图标
    });

    // 初始化第一首歌曲信息
    updateSongInfo();
    setCanvasSize()
    window.addEventListener('resize',setCanvasSize)
});
</script>
  • initAudio(): 创建 AudioContextAnalyserNode,并将音频源连接到分析器,再连接到输出。
  • drawSpectrum(): 使用 requestAnimationFrame 循环绘制。
    analyser.getByteFrequencyData(dataArray): 获取当前音频的频域数据。
    * 在循环中,根据 dataArray 中的值绘制不同高度、颜色的矩形,形成频谱效果。
    • setCanvasSize():设置canvas铺满容器

方案二:粒子背景

这种效果会更具动感,我们可以使用一些现成的粒子库,如 particles.js 或 tsParticles,也可以自己用 Canvas 手写。

  1. 引入粒子库 (以 tsParticles 为例):
<script src="https://cdn.jsdelivr.net/npm/tsparticles-engine"></script>
<script src="https://cdn.jsdelivr.net/npm/tsparticles"></script>
 <!--如果需要特定的形状或交互,还需要引入对应的 preset-->
<script src="https://cdn.jsdelivr.net/npm/tsparticles-preset-fireworks@2/tsparticles-preset-fireworks.min.js"></script>
  1. HTML 结构 (类似于方案一):
   <div class="mt-3 bg-white pt-2 pl-3 card" style="margin-top: 1px; border: 1px solid; padding: 2em; overflow: hidden; position: relative;">
    <div id="tsparticles" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 0;"></div>
    <div class="viny" style="text-align: center; position: relative; z-index: 1;">
         <!--...其余部分相同...-->
    </div>
  1. 初始化 tsParticles:
<script>
// ... (之前的歌曲播放逻辑) ...

 tsParticles.load("tsparticles", {
   preset: "fireworks", // 使用 fireworks 预设,可以换成其他的
     fullScreen:{
       enable:false, //避免遮挡页面其他部分
     }

 });
</script>
*    tsParticles的配置项有很多,可以去官网查看文档:[https://particles.js.org/docs/](https://particles.js.org/docs/)

方案三:动态渐变背景

这是一种相对简单但也很美观的效果。

  1. CSS:
.card { /* 你的 .card 样式 */
    /* 添加以下内容 */
    background: linear-gradient(to right, #ff00cc, #333399); /* 初始渐变 */
    animation: gradientAnimation 10s ease infinite; /* 添加动画 */
}

@keyframes gradientAnimation {
    0% {
        background-position: 0% 50%;
    }
    50% {
        background-position: 100% 50%;
    }
    100% {
        background-position: 0% 50%;
    }
}
  • linear-gradient: 设置一个初始的渐变背景。
  • animation: 应用一个名为 gradientAnimation 的动画。
  • @keyframes gradientAnimation: 定义动画的关键帧,这里让渐变背景的位置循环变化。
  1. JavaScript (可选):
    如果要让背景渐变与音乐节奏同步,可以使用 Web Audio API 获取音频的响度或频率数据,然后用 JavaScript 动态修改 CSS 变量或直接修改 background 属性。 但这实现起来会比较复杂,如果你只是需要一个动态的渐变背景,CSS 方案就足够了。

注意事项:

  • 性能: 频谱可视化和粒子效果可能会对性能有一定影响,尤其是在低端设备上。如果出现卡顿,可以尝试降低频谱的精度 (analyser.fftSize) 或减少粒子的数量。
  • 兼容性: Web Audio API 的兼容性很好,但如果需要支持非常老的浏览器,可能需要做一些兼容处理。
  • 音频跨域: 如果你的音频文件和网页不在同一个域下,可能会遇到跨域问题,导致无法分析音频数据。你需要确保音频文件设置了正确的 CORS 头。