.visualizer-player {
max-width: 480px;
margin: 0 auto;
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2e 50%, #16213e 100%);
border-radius: 24px;
overflow: hidden;
position: relative;
box-shadow:
0 25px 50px rgba(0, 0, 0, 0.5),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
color: #ffffff;
}
.visualizer-background {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
#visualizer-canvas {
width: 100%;
height: 100%;
opacity: 0.6;
}
.particles-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
}
.particle {
position: absolute;
width: 3px;
height: 3px;
background: radial-gradient(circle, #00f5ff, transparent);
border-radius: 50%;
animation: float 4s ease-in-out infinite;
}
.player-content {
position: relative;
z-index: 10;
padding: 32px 24px;
backdrop-filter: blur(10px);
}
.album-section {
display: flex;
align-items: center;
gap: 20px;
margin-bottom: 32px;
}
.album-artwork {
position: relative;
}
.album-image {
width: 100px;
height: 100px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.pulse-ring {
position: absolute;
width: 100%;
height: 100%;
border: 2px solid rgba(0, 245, 255, 0.6);
border-radius: 50%;
animation: pulse 2s ease-out infinite;
}
.pulse-ring-2 {
animation-delay: 1s;
border-color: rgba(255, 0, 110, 0.6);
}
.album-cover-img {
width: 80px;
height: 80px;
background: linear-gradient(45deg, #ff006e, #8338ec, #3a86ff, #00f5ff);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 30px rgba(0, 245, 255, 0.4);
position: relative;
z-index: 2;
}
.now-playing-indicator {
position: absolute;
bottom: -10px;
right: -10px;
background: rgba(0, 0, 0, 0.8);
border-radius: 12px;
padding: 8px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.sound-wave {
display: flex;
align-items: end;
gap: 2px;
height: 16px;
}
.wave {
width: 3px;
background: linear-gradient(to top, #ff006e, #00f5ff);
border-radius: 2px;
animation: wave 1.5s ease-in-out infinite;
}
.wave:nth-child(1) { height: 40%; animation-delay: 0s; }
.wave:nth-child(2) { height: 80%; animation-delay: 0.1s; }
.wave:nth-child(3) { height: 60%; animation-delay: 0.2s; }
.wave:nth-child(4) { height: 90%; animation-delay: 0.3s; }
.track-metadata h2 {
font-size: 22px;
font-weight: 700;
margin: 0 0 6px 0;
background: linear-gradient(45deg, #00f5ff, #ff006e);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.track-metadata p {
font-size: 16px;
color: #a0a0a0;
margin: 0 0 12px 0;
}
.track-stats {
display: flex;
gap: 12px;
}
.genre, .bpm {
padding: 4px 10px;
background: rgba(0, 245, 255, 0.2);
border: 1px solid rgba(0, 245, 255, 0.3);
border-radius: 12px;
font-size: 11px;
color: #00f5ff;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.bpm {
background: rgba(255, 0, 110, 0.2);
border-color: rgba(255, 0, 110, 0.3);
color: #ff006e;
}
.spectrum-analyzer {
margin-bottom: 24px;
padding: 20px;
background: rgba(0, 0, 0, 0.4);
border-radius: 16px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.frequency-bars {
display: flex;
align-items: end;
justify-content: space-between;
height: 80px;
margin-bottom: 8px;
}
.freq-bar {
width: 8%;
background: linear-gradient(to top, #ff006e 0%, #8338ec 50%, #00f5ff 100%);
border-radius: 4px 4px 0 0;
min-height: 4px;
animation: frequency 2s ease-in-out infinite;
box-shadow: 0 0 10px rgba(0, 245, 255, 0.3);
}
.freq-bar:nth-child(1) { height: 20%; animation-delay: 0s; }
.freq-bar:nth-child(2) { height: 45%; animation-delay: 0.1s; }
.freq-bar:nth-child(3) { height: 30%; animation-delay: 0.2s; }
.freq-bar:nth-child(4) { height: 70%; animation-delay: 0.3s; }
.freq-bar:nth-child(5) { height: 85%; animation-delay: 0.4s; }
.freq-bar:nth-child(6) { height: 55%; animation-delay: 0.5s; }
.freq-bar:nth-child(7) { height: 40%; animation-delay: 0.6s; }
.freq-bar:nth-child(8) { height: 65%; animation-delay: 0.7s; }
.freq-bar:nth-child(9) { height: 35%; animation-delay: 0.8s; }
.freq-bar:nth-child(10) { height: 25%; animation-delay: 0.9s; }
.frequency-labels {
display: flex;
justify-content: space-between;
font-size: 10px;
color: #666;
text-transform: uppercase;
}
.progress-visualization {
margin-bottom: 24px;
}
.time-info {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
font-size: 14px;
color: #a0a0a0;
font-weight: 500;
}
.waveform-progress {
position: relative;
height: 60px;
cursor: pointer;
}
.waveform-svg {
width: 100%;
height: 100%;
}
.progress-thumb {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 16px;
height: 16px;
background: #00f5ff;
border-radius: 50%;
box-shadow: 0 0 20px rgba(0, 245, 255, 0.8);
opacity: 0;
transition: opacity 0.3s ease;
}
.waveform-progress:hover .progress-thumb {
opacity: 1;
}
.control-interface {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.playback-controls {
display: flex;
align-items: center;
gap: 16px;
}
.ctrl-btn {
width: 44px;
height: 44px;
border: none;
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.ctrl-btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: scale(1.1);
box-shadow: 0 0 25px rgba(0, 245, 255, 0.4);
}
.ctrl-btn.active {
background: linear-gradient(45deg, #ff006e, #00f5ff);
box-shadow: 0 0 25px rgba(255, 0, 110, 0.6);
}
.ctrl-btn svg {
width: 20px;
height: 20px;
}
.play-button {
width: 64px;
height: 64px;
border: none;
background: linear-gradient(45deg, #ff006e, #8338ec, #00f5ff);
color: white;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
box-shadow: 0 10px 30px rgba(255, 0, 110, 0.4);
position: relative;
overflow: hidden;
}
.play-button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
transition: left 0.6s ease;
}
.play-button:hover {
transform: scale(1.05);
box-shadow: 0 15px 40px rgba(255, 0, 110, 0.6);
}
.play-button:hover::before {
left: 100%;
}
.play-button svg {
width: 28px;
height: 28px;
}
.audio-controls {
display: flex;
align-items: center;
gap: 16px;
}
.volume-section {
display: flex;
align-items: center;
gap: 12px;
}
.volume-slider {
width: 80px;
position: relative;
}
.volume-slider input[type="range"] {
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
outline: none;
-webkit-appearance: none;
position: relative;
z-index: 2;
}
.volume-slider input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 14px;
height: 14px;
background: #00f5ff;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 0 15px rgba(0, 245, 255, 0.8);
}
.volume-fill {
position: absolute;
top: 50%;
left: 0;
height: 4px;
background: linear-gradient(90deg, #ff006e, #00f5ff);
border-radius: 2px;
transform: translateY(-50%);
width: 80%;
z-index: 1;
}
.playlist-visualizer {
background: rgba(0, 0, 0, 0.3);
border-radius: 16px;
padding: 20px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.playlist-visualizer h3 {
font-size: 16px;
font-weight: 600;
margin: 0 0 16px 0;
color: #a0a0a0;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.queue-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 8px;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
margin-bottom: 8px;
}
.queue-item:hover {
background: rgba(255, 255, 255, 0.1);
transform: translateX(4px);
}
.queue-item.active {
background: linear-gradient(90deg, rgba(255, 0, 110, 0.2), rgba(0, 245, 255, 0.2));
border: 1px solid rgba(255, 0, 110, 0.3);
}
.queue-visualizer {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.mini-bars {
display: flex;
align-items: end;
gap: 2px;
height: 12px;
}
.mini-bar {
width: 2px;
background: linear-gradient(to top, #ff006e, #00f5ff);
border-radius: 1px;
animation: miniWave 1.5s ease-in-out infinite;
}
.mini-bar:nth-child(1) { height: 40%; animation-delay: 0s; }
.mini-bar:nth-child(2) { height: 80%; animation-delay: 0.2s; }
.mini-bar:nth-child(3) { height: 60%; animation-delay: 0.4s; }
.queue-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 2px;
}
.queue-title {
font-size: 14px;
font-weight: 500;
color: #ffffff;
}
.queue-artist {
font-size: 12px;
color: #a0a0a0;
}
.queue-duration {
font-size: 12px;
color: #a0a0a0;
font-weight: 500;
}
/* Animaciones */
@keyframes pulse {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(1.4);
opacity: 0;
}
}
@keyframes wave {
0%, 100% { transform: scaleY(1); }
50% { transform: scaleY(1.5); }
}
@keyframes frequency {
0%, 100% { transform: scaleY(1); }
50% { transform: scaleY(1.3); }
}
@keyframes miniWave {
0%, 100% { transform: scaleY(1); }
50% { transform: scaleY(1.4); }
}
@keyframes float {
0%, 100% {
transform: translateY(0px) rotate(0deg);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(-100px) rotate(360deg);
opacity: 0;
}
}
/* Estado de reproducción */
.visualizer-player.playing .wave,
.visualizer-player.playing .freq-bar,
.visualizer-player.playing .mini-bar {
animation-play-state: running;
}
.visualizer-player:not(.playing) .wave,
.visualizer-player:not(.playing) .freq-bar,
.visualizer-player:not(.playing) .mini-bar {
animation-play-state: paused;
}
/* Diseño responsivo */
@media (max-width: 520px) {
.visualizer-player {
margin: 16px;
}
.player-content {
padding: 24px 20px;
}
.album-section {
flex-direction: column;
text-align: center;
gap: 16px;
}
.control-interface {
flex-direction: column;
gap: 20px;
}
.playback-controls {
order: 1;
}
.audio-controls {
order: 2;
}
}
document.addEventListener('DOMContentLoaded', () => {
// Elementos
const player = document.querySelector('.visualizer-player');
const canvas = document.getElementById('visualizer-canvas');
const ctx = canvas.getContext('2d');
const particlesContainer = document.getElementById('particles-container');
const playBtn = document.getElementById('play-btn');
const prevBtn = document.getElementById('prev-btn');
const nextBtn = document.getElementById('next-btn');
const shuffleBtn = document.getElementById('shuffle-btn');
const repeatBtn = document.getElementById('repeat-btn');
const volumeBtn = document.getElementById('volume-btn');
const volumeRange = document.getElementById('volume-range');
const eqBtn = document.getElementById('eq-btn');
const trackTitle = document.getElementById('track-title');
const trackArtist = document.getElementById('track-artist');
const currentTimeEl = document.getElementById('current-time');
const totalTimeEl = document.getElementById('total-time');
const progressThumb = document.getElementById('progress-thumb');
const waveformProgress = document.getElementById('waveform-progress');
const playIcon = document.querySelector('.play-icon');
const pauseIcon = document.querySelector('.pause-icon');
const queueItems = document.querySelectorAll('.queue-item');
// Estado
let currentTrackIndex = 0;
let isPlaying = false;
let isShuffled = false;
let repeatMode = 0;
let currentTime = 165; // 2:45
let duration = 332; // 5:32
let animationId;
let particles = [];
// Inicializar
setupCanvas();
loadTrack(currentTrackIndex);
updateProgress();
createParticles();
function setupCanvas() {
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * window.devicePixelRatio;
canvas.height = rect.height * window.devicePixelRatio;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
}
function loadTrack(index) {
const track = queueItems[index];
const title = track.dataset.title;
const artist = track.dataset.artist;
const trackDuration = track.dataset.duration;
trackTitle.textContent = title;
trackArtist.textContent = artist;
totalTimeEl.textContent = trackDuration;
// Actualizar estado activo
queueItems.forEach(item => item.classList.remove('active'));
track.classList.add('active');
// Parsear duración
const [mins, secs] = trackDuration.split(':').map(Number);
duration = mins * 60 + secs;
currentTime = 0;
updateProgress();
}
function togglePlay() {
isPlaying = !isPlaying;
if (isPlaying) {
playIcon.style.display = 'none';
pauseIcon.style.display = 'block';
player.classList.add('playing');
startVisualizer();
simulateProgress();
} else {
playIcon.style.display = 'block';
pauseIcon.style.display = 'none';
player.classList.remove('playing');
stopVisualizer();
}
}
function nextTrack() {
if (isShuffled) {
currentTrackIndex = Math.floor(Math.random() * queueItems.length);
} else {
currentTrackIndex = (currentTrackIndex + 1) % queueItems.length;
}
loadTrack(currentTrackIndex);
if (isPlaying) {
simulateProgress();
}
}
function prevTrack() {
if (currentTime > 3) {
currentTime = 0;
updateProgress();
return;
}
currentTrackIndex = (currentTrackIndex - 1 + queueItems.length) % queueItems.length;
loadTrack(currentTrackIndex);
if (isPlaying) {
simulateProgress();
}
}
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
function updateProgress() {
const progress = (currentTime / duration) * 100;
progressThumb.style.left = `${progress}%`;
currentTimeEl.textContent = formatTime(currentTime);
// Actualizar progreso de forma de onda
const pathLength = 400 * (progress / 100);
waveformProgress.style.strokeDasharray = `${pathLength} 400`;
}
function simulateProgress() {
if (!isPlaying) return;
const interval = setInterval(() => {
if (!isPlaying) {
clearInterval(interval);
return;
}
currentTime += 1;
updateProgress();
if (currentTime >= duration) {
clearInterval(interval);
if (repeatMode === 2) {
currentTime = 0;
simulateProgress();
} else if (repeatMode === 1 || currentTrackIndex < queueItems.length - 1) {
nextTrack();
} else {
isPlaying = false;
playIcon.style.display = 'block';
pauseIcon.style.display = 'none';
player.classList.remove('playing');
stopVisualizer();
}
}
}, 1000);
}
function createParticles() {
for (let i = 0; i < 20; i++) {
setTimeout(() => {
if (isPlaying) {
const particle = document.createElement('div');
particle.className = 'particle';
particle.style.left = Math.random() * 100 + '%';
particle.style.animationDuration = (Math.random() * 3 + 2) + 's';
particle.style.animationDelay = Math.random() * 2 + 's';
particlesContainer.appendChild(particle);
setTimeout(() => {
if (particle.parentNode) {
particle.parentNode.removeChild(particle);
}
}, 6000);
}
}, i * 200);
}
if (isPlaying) {
setTimeout(createParticles, 4000);
}
}
function startVisualizer() {
function animate() {
if (!isPlaying) return;
const rect = canvas.getBoundingClientRect();
ctx.clearRect(0, 0, rect.width, rect.height);
// Dibujar visualización de frecuencia
const barWidth = rect.width / 64;
for (let i = 0; i < 64; i++) {
const barHeight = Math.random() * rect.height * 0.8;
const hue = (i * 5 + Date.now() * 0.01) % 360;
ctx.fillStyle = `hsla(${hue}, 70%, 60%, 0.8)`;
ctx.fillRect(i * barWidth, rect.height - barHeight, barWidth - 1, barHeight);
}
// Dibujar visualizador circular
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const radius = Math.min(rect.width, rect.height) * 0.3;
ctx.strokeStyle = 'rgba(0, 245, 255, 0.6)';
ctx.lineWidth = 2;
ctx.beginPath();
for (let i = 0; i < 360; i += 5) {
const angle = (i * Math.PI) / 180;
const amplitude = Math.random() * 30 + 10;
const x = centerX + Math.cos(angle) * (radius + amplitude);
const y = centerY + Math.sin(angle) * (radius + amplitude);
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.closePath();
ctx.stroke();
animationId = requestAnimationFrame(animate);
}
animate();
createParticles();
}
function stopVisualizer() {
if (animationId) {
cancelAnimationFrame(animationId);
}
}
// Eventos
playBtn.addEventListener('click', togglePlay);
nextBtn.addEventListener('click', nextTrack);
prevBtn.addEventListener('click', prevTrack);
shuffleBtn.addEventListener('click', () => {
isShuffled = !isShuffled;
shuffleBtn.classList.toggle('active', isShuffled);
});
repeatBtn.addEventListener('click', () => {
repeatMode = (repeatMode + 1) % 3;
repeatBtn.classList.toggle('active', repeatMode > 0);
});
eqBtn.addEventListener('click', () => {
eqBtn.classList.toggle('active');
});
// Control de volumen
volumeRange.addEventListener('input', () => {
const volume = volumeRange.value;
document.querySelector('.volume-fill').style.width = volume + '%';
});
// Interacción con cola
queueItems.forEach((item, index) => {
item.addEventListener('click', () => {
currentTrackIndex = index;
loadTrack(currentTrackIndex);
if (!isPlaying) {
togglePlay();
}
});
});
// Atajos de teclado
document.addEventListener('keydown', (e) => {
if (e.target.tagName === 'INPUT') return;
switch(e.code) {
case 'Space':
e.preventDefault();
togglePlay();
break;
case 'ArrowRight':
nextTrack();
break;
case 'ArrowLeft':
prevTrack();
break;
}
});
// Manejador de redimensionamiento
window.addEventListener('resize', setupCanvas);
// Inicializar volumen
document.querySelector('.volume-fill').style.width = '80%';
});