CD风格音乐播放器 - 带频谱显示

CD风格音乐播放器 - 带频谱显示


 <script>
        // 获取DOM元素
        const audioPlayer = document.getElementById('audioPlayer');
        const albumCover = document.getElementById('albumCover');
        const needle = document.getElementById('needle');
        const playPauseBtn = document.getElementById('playPauseBtn');
        const prevBtn = document.getElementById('prevBtn');
        const nextBtn = document.getElementById('nextBtn');
        const progressBar = document.getElementById('progressBar');
        const progress = document.getElementById('progress');
        const currentTimeEl = document.getElementById('currentTime');
        const totalTimeEl = document.getElementById('totalTime');
        const songTitle = document.getElementById('songTitle');
        const songArtist = document.getElementById('songArtist');
        const loadingText = document.getElementById('loadingText');
        const spectrumCanvas = document.getElementById('spectrumCanvas');
        const ctx = spectrumCanvas.getContext('2d');
        
        // 设置Canvas尺寸
        spectrumCanvas.width = spectrumCanvas.offsetWidth;
        spectrumCanvas.height = spectrumCanvas.offsetHeight;
        
        // 模拟音乐数据
        const playlist = [
            {
                title: "Andy-live",
                artist: "林小风",
                cover: "https://picsum.photos/400/400?random=1",
                src: "https://bgm.pw/upfile/mp3/Andy-live.mp3"
            },
            {
                title: "鲁冰花",
                artist: "夜光乐队",
                cover: "https://picsum.photos/400/400?random=2",
                src: "https://bgm.pw/upfile/mp3/lubhua.m4a"
            },
            {
                title: "城市节奏",
                artist: "都市行者",
                cover: "https://picsum.photos/400/400?random=3",
                src: "https://bgm.pw/upfile/mp3/First%20-%20Battle.m4a"
            }
        ];
        
        let currentSongIndex = 0;
        let audioContext, analyser, dataArray, bufferLength;
        let isPlaying = false;
        
        // 格式化时间(秒 -> 分:秒)[1,5](@ref)
        function formatTime(seconds) {
            if (isNaN(seconds) || seconds === Infinity) return "0:00";
            
            const mins = Math.floor(seconds / 60);
            const secs = Math.floor(seconds % 60);
            return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
        }
        
        // 初始化音频分析器 [6,7](@ref)
        function initAudioAnalyzer() {
            // 创建音频上下文
            audioContext = new (window.AudioContext || window.webkitAudioContext)();
            analyser = audioContext.createAnalyser();
            
            // 创建音频源节点
            const source = audioContext.createMediaElementSource(audioPlayer);
            
            // 连接分析器
            source.connect(analyser);
            analyser.connect(audioContext.destination);
            
            // 设置FFT大小
            analyser.fftSize = 256;
            bufferLength = analyser.frequencyBinCount;
            dataArray = new Uint8Array(bufferLength);
        }
        
        // 绘制频谱 [6,7](@ref)
        function drawSpectrum() {
            if (!isPlaying || !analyser) return;
            
            requestAnimationFrame(drawSpectrum);
            
            // 获取频率数据
            analyser.getByteFrequencyData(dataArray);
            
            // 清除画布
            ctx.clearRect(0, 0, spectrumCanvas.width, spectrumCanvas.height);
            
            // 计算条宽和间距
            const barWidth = (spectrumCanvas.width / bufferLength) * 2.5;
            let x = 0;
            
            for (let i = 0; i < bufferLength; i++) {
                // 计算条高
                const barHeight = (dataArray[i] / 255) * spectrumCanvas.height;
                
                // 创建渐变颜色
                const gradient = ctx.createLinearGradient(0, 0, 0, spectrumCanvas.height);
                gradient.addColorStop(0, '#4cc9f0');
                gradient.addColorStop(0.5, '#3aa8d0');
                gradient.addColorStop(1, '#1a8bb8');
                
                // 绘制频谱条
                ctx.fillStyle = gradient;
                ctx.fillRect(
                    x,
                    spectrumCanvas.height - barHeight,
                    barWidth - 1,
                    barHeight
                );
                
                x += barWidth + 1;
            }
        }
        
        // 更新播放器信息
        function updatePlayerInfo() {
            const song = playlist[currentSongIndex];
            songTitle.textContent = song.title;
            songArtist.textContent = song.artist;
            albumCover.src = song.cover;
            audioPlayer.src = song.src;
            
            loadingText.style.display = 'block';
            totalTimeEl.textContent = "0:00";
            currentTimeEl.textContent = "0:00";
            progress.style.width = "0%";
            currentTimeEl.style.left = "0px";
            totalTimeEl.style.left = "0px";
            
            // 清除Canvas
            ctx.clearRect(0, 0, spectrumCanvas.width, spectrumCanvas.height);
            
            // 等待音频加载完成
            audioPlayer.load();
            
            // 加载音频元数据 [1](@ref)
            audioPlayer.addEventListener('loadedmetadata', function() {
                totalTimeEl.textContent = formatTime(audioPlayer.duration);
                loadingText.style.display = 'none';
            });
        }
        
        // 播放/暂停音乐 [2,4](@ref)
        function togglePlayPause() {
            if (audioPlayer.paused) {
                // 初始化音频分析器
                if (!audioContext) {
                    initAudioAnalyzer();
                }
                
                // 恢复音频上下文(某些浏览器需要用户交互后)[6](@ref)
                if (audioContext.state === 'suspended') {
                    audioContext.resume();
                }
                
                audioPlayer.play().then(() => {
                    playPauseBtn.innerHTML = '⏸';
                    albumCover.classList.add('rotating');
                    needle.classList.add('playing');
                    isPlaying = true;
                    
                    // 开始绘制频谱
                    drawSpectrum();
                }).catch(error => {
                    console.error('播放失败:', error);
                    loadingText.textContent = '播放失败,请点击重试';
                });
            } else {
                audioPlayer.pause();
                playPauseBtn.innerHTML = '▶';
                albumCover.classList.remove('rotating');
                needle.classList.remove('playing');
                isPlaying = false;
            }
        }
        
        // 点击封面控制播放/暂停
        albumCover.addEventListener('click', togglePlayPause);
        
        // 播放/暂停按钮事件
        playPauseBtn.addEventListener('click', togglePlayPause);
        
        // 上一首 [4,5](@ref)
        prevBtn.addEventListener('click', function() {
            currentSongIndex = (currentSongIndex - 1 + playlist.length) % playlist.length;
            updatePlayerInfo();
            if (!audioPlayer.paused) {
                audioPlayer.play();
            }
        });
        
        // 下一首 [4,5](@ref)
        nextBtn.addEventListener('click', function() {
            currentSongIndex = (currentSongIndex + 1) % playlist.length;
            updatePlayerInfo();
            if (!audioPlayer.paused) {
                audioPlayer.play();
            }
        });
        
        // 更新进度条和时间显示 [1,2](@ref)
        audioPlayer.addEventListener('timeupdate', function() {
            const currentTime = audioPlayer.currentTime;
            const duration = audioPlayer.duration;
            
            if (duration && !isNaN(duration) && duration !== Infinity) {
                const progressPercent = (currentTime / duration) * 100;
                progress.style.width = `${progressPercent}%`;
                currentTimeEl.textContent = formatTime(currentTime);
                totalTimeEl.textContent = formatTime(duration);
                
                // 更新当前时间和总时间位置
                const progressBarWidth = progressBar.offsetWidth;
                const timePos = (progressPercent / 100) * progressBarWidth;
                currentTimeEl.style.left = `${timePos}px`;
                totalTimeEl.style.left = `${timePos}px`;
                
                // 边界检测,确保时间显示不重叠
                const minDistance = 30; // 最小间距
                if (timePos < progressBarWidth / 2) {
                    currentTimeEl.style.transform = 'translateX(-50%)';
                    totalTimeEl.style.transform = 'translateX(-50%)';
                    currentTimeEl.style.marginTop = '0px';
                    totalTimeEl.style.marginTop = '20px';
                } else {
                    currentTimeEl.style.transform = 'translateX(-50%)';
                    totalTimeEl.style.transform = 'translateX(-50%)';
                    currentTimeEl.style.marginTop = '20px';
                    totalTimeEl.style.marginTop = '0px';
                }
            }
        });
        
        // 点击进度条跳转 [2](@ref)
        progressBar.addEventListener('click', function(e) {
            const rect = progressBar.getBoundingClientRect();
            const clickX = e.clientX - rect.left;
            const width = rect.width;
            const duration = audioPlayer.duration;
            
            if (duration && !isNaN(duration) && duration !== Infinity) {
                audioPlayer.currentTime = (clickX / width) * duration;
            }
        });
        
        // 歌曲结束时自动下一首 [4](@ref)
        audioPlayer.addEventListener('ended', function() {
            currentSongIndex = (currentSongIndex + 1) % playlist.length;
            updatePlayerInfo();
            audioPlayer.play();
        });
        
        // 音频加载错误处理
        audioPlayer.addEventListener('error', function() {
            loadingText.textContent = '音频加载失败,请检查网络连接';
        });
        
        // 窗口大小变化时调整Canvas尺寸 [6](@ref)
        window.addEventListener('resize', function() {
            spectrumCanvas.width = spectrumCanvas.offsetWidth;
            spectrumCanvas.height = spectrumCanvas.offsetHeight;
        });
        
        // 初始化播放器
        updatePlayerInfo();
    </script>

 演示地址: https://bgm.pw/assets/file/zp/20251212144655.html

2025-12-12 14:47:41 通过 网页 浏览(35)

共有0条评论!

发表评论

更换一道题!