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;
}