【网页播放器】播放自己喜欢的音乐

javascript 复制代码
// 错误处理
window.onerror = function(message, source, lineno, colno, error) {
    console.error("An error occurred:", message, "at", source, ":", lineno);
    return true;
};

// 检查 particlesJS 是否已定义
if (typeof particlesJS !== 'undefined') {
    // 初始化粒子效果
    document.addEventListener('DOMContentLoaded', function() {
        particlesJS('particles-js', {
            particles: {
                number: { value: 80, density: { enable: true, value_area: 800 } },
                color: { value: '#ffffff' },
                shape: { type: 'circle', stroke: { width: 0, color: '#000000' } },
                opacity: { value: 0.5, random: true, anim: { enable: true, speed: 1, opacity_min: 0.1, sync: false } },
                size: { value: 3, random: true, anim: { enable: true, speed: 4, size_min: 0.3, sync: false } },
                line_linked: { enable: true, distance: 150, color: '#ffffff', opacity: 0.4, width: 1 },
                move: { enable: true, speed: 1, direction: 'none', random: true, straight: false, out_mode: 'out', bounce: false }
            },
            interactivity: {
                detect_on: 'canvas',
                events: { onhover: { enable: true, mode: 'repulse' }, onclick: { enable: true, mode: 'push' }, resize: true },
                modes: { repulse: { distance: 100, duration: 0.4 }, push: { particles_nb: 4 } }
            },
            retina_detect: true
        });
        console.log('Particles initialized');
    });
} else {
    console.error("particlesJS is not defined. The library may not have loaded correctly.");
}

// 初始化音频波形
let wavesurfer;

function initWaveSurfer() {
    wavesurfer = WaveSurfer.create({
        container: '#visualizer',
        waveColor: 'rgba(255, 255, 255, 0.5)',
        progressColor: 'rgba(29, 185, 84, 0.7)',
        cursorColor: 'transparent',
        barWidth: 2,
        barRadius: 3,
        cursorWidth: 0,
        height: 40,
        barGap: 2,
        responsive: true
    });

    // 绑定事件监听器
    wavesurfer.on('ready', onWavesurferReady);
    wavesurfer.on('audioprocess', updateProgress);
    wavesurfer.on('audioprocess', updateLyrics);

    console.log('WaveSurfer initialized');

    // 初始化后立即绑定播放/暂停按钮事件
    bindPlayPauseButton();
}

function bindPlayPauseButton() {
    playPauseButton.addEventListener('click', togglePlayPause);
}

function togglePlayPause() {
    if (wavesurfer) {
        wavesurfer.playPause();
        updatePlayPauseIcon();
    } else {
        console.error('WaveSurfer is not initialized');
    }
}

function updatePlayPauseIcon() {
    const icon = playPauseButton.querySelector('i');
    if (wavesurfer && wavesurfer.isPlaying()) {
        icon.classList.remove('fa-play');
        icon.classList.add('fa-pause');
    } else {
        icon.classList.remove('fa-pause');
        icon.classList.add('fa-play');
    }
}

// 在用户交互后初始化 WaveSurfer
document.addEventListener('click', function initAudioContext() {
    if (!wavesurfer) {
        initWaveSurfer();
    }
    document.removeEventListener('click', initAudioContext);
}, { once: true });

// 获取DOM元素
const playPauseButton = document.getElementById('play-pause');
const prevButton = document.getElementById('prev');
const nextButton = document.getElementById('next');
const shuffleButton = document.getElementById('shuffle');
const repeatButton = document.getElementById('repeat');
const fileInput = document.getElementById('file-input');
const songTitle = document.getElementById('song-title');
const artistName = document.getElementById('artist-name');
const albumCover = document.getElementById('album-cover');
const lyricsContainer = document.getElementById('lyrics');
const progressContainer = document.getElementById('progress-container');
const progressBar = document.getElementById('progress-bar');
const currentTime = document.getElementById('current-time');
const totalTime = document.getElementById('total-time');

// 初始化控制按钮
function initializeControls() {
    playPauseButton.addEventListener('click', togglePlayPause);
    prevButton.addEventListener('click', playPreviousTrack);
    nextButton.addEventListener('click', playNextTrack);
    shuffleButton.addEventListener('click', toggleShuffle);
    repeatButton.addEventListener('click', toggleRepeat);
    progressContainer.addEventListener('click', seekToPosition);
}

// 文件上传功能
fileInput.addEventListener('change', (e) => {
    const file = e.target.files[0];
    if (file) {
        if (!wavesurfer) {
            initWaveSurfer();
        }
        const objectURL = URL.createObjectURL(file);
        wavesurfer.load(objectURL);
        updateSongInfo(file);
        console.log('File loaded:', file.name);
    }
});

function updateSongInfo(file) {
    songTitle.textContent = file.name.replace(/\.[^/.]+$/, "");
    artistName.textContent = "未知艺术家"; // 这里可以添加获取元数据的逻辑
    // 这里可以添加获取专辑封面的逻辑
    console.log('Song info updated'); // 添加日志
}

// 进度条更新
function updateProgress() {
    if (wavesurfer) {
        const progress = wavesurfer.getCurrentTime() / wavesurfer.getDuration();
        progressBar.style.width = `${progress * 100}%`;
        currentTime.textContent = formatTime(wavesurfer.getCurrentTime());
    }
}

progressContainer.addEventListener('click', function(e) {
    if (wavesurfer) {
        const clickPosition = (e.clientX - this.getBoundingClientRect().left) / this.offsetWidth;
        wavesurfer.seekTo(clickPosition);
    }
});

// 时间格式化
function formatTime(time) {
    const minutes = Math.floor(time / 60);
    const seconds = Math.floor(time % 60);
    return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}

// 音频加载完成后的操作
function onWavesurferReady() {
    playPauseButton.disabled = false;
    totalTime.textContent = formatTime(wavesurfer.getDuration());
    updatePlayPauseIcon();
    console.log('Wavesurfer ready');
    wavesurfer.play();
}

// 模拟歌词显示
function updateLyrics(time) {
    if (!showingLyrics) return;
    
    // 如果有解析的歌词,使用它们
    if (window.parsedLyrics && window.parsedLyrics.length > 0) {
        const currentLyric = window.parsedLyrics.find(lyric => lyric.time <= time);
        if (currentLyric) {
            lyricsContainer.textContent = currentLyric.text;
        }
    } else {
        // 否则显示默认的播放时间
        lyricsContainer.textContent = `正在播放: ${Math.floor(time)}秒`;
    }
    console.log('Lyrics updated:', lyricsContainer.textContent); // 添加日志
}

// 添加其他按钮的功能(这里只是占位,实际功能需要根据您的需求来实现)
prevButton.addEventListener('click', () => console.log('上一首'));
nextButton.addEventListener('click', () => console.log('下一首'));
shuffleButton.addEventListener('click', () => console.log('随机播放'));
repeatButton.addEventListener('click', () => console.log('重复播放'));

// 确保在 DOM 加载完成后初始化 WaveSurfer
document.addEventListener('DOMContentLoaded', () => {
    initializeControls();
    initWaveSurfer();
});

const coverLyricsContainer = document.getElementById('cover-lyrics-container');
const lyricsInput = document.getElementById('lyrics-input');
let showingLyrics = false;

coverLyricsContainer.addEventListener('click', toggleLyrics);

function toggleLyrics() {
    showingLyrics = !showingLyrics;
    albumCover.style.display = showingLyrics ? 'none' : 'block';
    lyricsContainer.style.display = showingLyrics ? 'flex' : 'none';
    console.log('Lyrics toggled:', showingLyrics); // 添加日志
}

lyricsInput.addEventListener('change', (e) => {
    const file = e.target.files[0];
    if (file) {
        const reader = new FileReader();
        reader.onload = function(e) {
            const cueContent = e.target.result;
            const lyrics = parseCueFile(cueContent);
            updateLyricsDisplay(lyrics);
        };
        reader.readAsText(file);
    }
});

function parseCueFile(cueContent) {
    const lines = cueContent.split('\n');
    const lyrics = [];
    let currentTime = 0;

    for (let line of lines) {
        if (line.startsWith('  TRACK')) {
            currentTime = 0;
        } else if (line.startsWith('    INDEX 01')) {
            const timeStr = line.split('INDEX 01')[1].trim();
            const [min, sec, frame] = timeStr.split(':').map(Number);
            currentTime = min * 60 + sec + frame / 75;
        } else if (line.startsWith('    TITLE')) {
            const lyric = line.split('TITLE')[1].trim().replace(/"/g, '');
            lyrics.push({ time: currentTime, text: lyric });
        }
    }

    return lyrics;
}

function updateLyricsDisplay(lyrics) {
    lyricsContainer.innerHTML = '';
    for (let lyric of lyrics) {
        const p = document.createElement('p');
        p.textContent = lyric.text;
        lyricsContainer.appendChild(p);
    }
}

// 拖动功能
const fileInputContainer = document.getElementById('file-input-container');
let isDragging = false;
let startX, startY, initialLeft, initialTop;

fileInputContainer.addEventListener('mousedown', startDragging);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDragging);

function startDragging(e) {
    isDragging = true;
    startX = e.clientX;
    startY = e.clientY;
    initialLeft = fileInputContainer.offsetLeft;
    initialTop = fileInputContainer.offsetTop;
}

function drag(e) {
    if (!isDragging) return;
    e.preventDefault();
    const dx = e.clientX - startX;
    const dy = e.clientY - startY;
    fileInputContainer.style.left = `${initialLeft + dx}px`;
    fileInputContainer.style.top = `${initialTop + dy}px`;
}

function stopDragging() {
    isDragging = false;
}
css 复制代码
body {
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    background: linear-gradient(45deg, #1a1a1a, #2a2a2a);
    font-family: 'Poppins', sans-serif;
    color: white;
}

#player {
    position: relative;
    width: 340px;
    height: 600px;
    background-color: rgba(255, 255, 255, 0.1);
    border-radius: 30px;
    overflow: hidden;
    box-shadow: 0 10px 30px rgba(0,0,0,0.3);
    transition: all 0.3s ease;
    padding: 20px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    z-index: 1;
}

#album-cover {
    width: 220px;
    height: 220px;
    border-radius: 50%;
    margin: 20px auto;
    display: block;
    box-shadow: 0 10px 30px rgba(0,0,0,0.3);
    animation: rotate 20s linear infinite;
    transition: all 0.3s ease;
}

#info-container {
    text-align: center;
    margin-bottom: 15px;
}

#song-title {
    font-size: 22px;
    margin: 0;
    font-weight: 600;
    text-shadow: 0 2px 4px rgba(0,0,0,0.3);
}

#artist-name {
    font-size: 16px;
    margin: 5px 0 0;
    opacity: 0.8;
}

#lyrics {
    height: 40px;
    overflow-y: auto;
    margin: 10px 0;
    text-align: center;
    font-size: 14px;
    opacity: 0.9;
}

#progress-container {
    position: relative;
    width: 100%;
    height: 40px;
    background-color: rgba(255,255,255,0.1);
    margin: 10px 0;
    border-radius: 20px;
    overflow: hidden;
    cursor: pointer;
}

#time-info {
    display: flex;
    justify-content: space-between;
    width: 100%;
    margin: 5px 0;
    font-size: 12px;
    opacity: 0.8;
}

#controls {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 15px;
}

.control-btn {
    background: none;
    border: none;
    color: white;
    font-size: 20px;
    cursor: pointer;
    transition: all 0.2s;
    opacity: 0.8;
}

.main-btn {
    font-size: 40px;
    opacity: 1;
}

#file-input-container {
    position: absolute;
    bottom: 20px;
    right: 20px;
    z-index: 10;
    cursor: move;
    display: flex;
    flex-direction: column;
    align-items: center;
}

#file-input {
    display: none;
}

.file-input-label, .lyrics-input-label {
    width: 40px;
    height: 40px;
    font-size: 18px;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: rgba(128, 128, 128, 0.8); /* 灰色 */
    color: white;
    border-radius: 50%;
    cursor: pointer;
    transition: all 0.2s;
    box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    margin-bottom: 10px;
}

.file-input-label:hover, .lyrics-input-label:hover {
    background-color: rgba(160, 160, 160, 0.9); /* 稍微亮一点的灰色 */
    transform: scale(1.1);
    box-shadow: 0 4px 8px rgba(0,0,0,0.3);
}

/* 如果需要,可以添加一个提示文本 */
.file-input-label::after, .lyrics-input-label::after {
    content: none;
}

.file-input-label:hover::after, .lyrics-input-label:hover::after {
    opacity: 1;
}

#particle-container, #blur-overlay, #visualizer {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

#blur-overlay {
    backdrop-filter: blur(5px);
    z-index: 1;
}

#visualizer {
    z-index: 2;
}

#album-cover:hover {
    transform: scale(1.05);
    box-shadow: 0 15px 35px rgba(0,0,0,0.4);
}

@keyframes rotate {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

#progress-bar {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    background-color: rgba(29, 185, 84, 0.5);
    z-index: 3;
    transition: width 0.1s linear;
}

#time-info {
    display: flex;
    justify-content: space-between;
    width: 100%;
    margin: 5px 0;
    font-size: 12px;
    opacity: 0.8;
}

#controls {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 15px;
}

.control-btn:hover {
    transform: scale(1.2);
    opacity: 1;
}

.main-btn {
    font-size: 40px;
    opacity: 1;
}

#file-input-container {
    position: absolute;
    bottom: 20px;
    right: 20px;
    z-index: 10;
}

#file-input {
    display: none;
}

.file-input-label {
    width: 40px;
    height: 40px;
    font-size: 18px;
}

/* 其他样式保持不变 */

#cover-lyrics-container {
    position: relative;
    width: 220px;
    height: 220px;
    margin: 20px auto;
    cursor: pointer;
}

#album-cover, #lyrics {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    transition: all 0.3s ease;
}

#lyrics {
    background-color: rgba(0, 0, 0, 0.7);
    color: white;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 10px;
    overflow-y: auto;
    font-size: 14px;
}

.lyrics-input-label {
    width: 40px;
    height: 40px;
    font-size: 18px;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: rgba(29, 185, 84, 0.8);
    color: white;
    border-radius: 50%;
    cursor: pointer;
    transition: all 0.2s;
    box-shadow: 0 2px 5px rgba(0,0,0,0.2);
    margin-top: 10px;
}

.lyrics-input-label:hover {
    background-color: rgba(30, 215, 96, 0.9);
    transform: scale(1.1);
    box-shadow: 0 4px 8px rgba(0,0,0,0.3);
}

/* ... 其他样式保持不变 ... */

#particles-js {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: -1;
}
相关推荐
西猫雷婶12 分钟前
python学opencv|读取图像(二十一)使用cv2.circle()绘制圆形进阶
开发语言·python·opencv
kiiila12 分钟前
【Qt】对象树(生命周期管理)和字符集(cout打印乱码问题)
开发语言·qt
小_太_阳38 分钟前
Scala_【2】变量和数据类型
开发语言·后端·scala·intellij-idea
直裾41 分钟前
scala借阅图书保存记录(三)
开发语言·后端·scala
唐 城1 小时前
curl 放弃对 Hyper Rust HTTP 后端的支持
开发语言·http·rust
噢,我明白了2 小时前
同源策略:为什么XMLHttpRequest不能跨域请求资源?
javascript·跨域
sanguine__2 小时前
APIs-day2
javascript·css·css3
关你西红柿子2 小时前
小程序app封装公用顶部筛选区uv-drop-down
前端·javascript·vue.js·小程序·uv
济南小草根3 小时前
把一个Vue项目的页面打包后再另一个项目中使用
前端·javascript·vue.js
码银3 小时前
【python】银行客户流失预测预处理部分,独热编码·标签编码·数据离散化处理·数据筛选·数据分割
开发语言·python