En este tutorial vamos a crear un reproductor de música inspirado en los reproductores clásicos, pero con un toque más cálido y decorativo.
El gadget incluye:
- Reproducción de canciones desde YouTube.
- Una portada que gira mientras la música está sonando.
- Botones de reproducir, pausar, siguiente y anterior.
- Una playlist desplegable.
- Barra de progreso interactiva.
- Un diseño completamente personalizable.
Todo funciona directamente desde el blog sin necesidad de abrir YouTube en otra pestaña.
Dificultad: Avanzada
Tiempo aproximado: 35 minutos
Necesitas: Un blog en Blogger + imágenes para el fondo y las portadas de las canciones
Antes de empezar
Este gadget utiliza HTML, CSS, JavaScript y la API de YouTube.
Cada parte cumple una función distinta:
- HTML → crea la estructura del reproductor.
- CSS → diseña la interfaz, las animaciones y la portada giratoria.
- JavaScript → controla la reproducción, la playlist, la barra de progreso y la comunicación con YouTube.
Aunque el código es largo, está dividido en pequeños bloques independientes, lo que facilita entender cómo funciona cada parte.
Paso 1: Preparar el fondo del reproductor
El fondo se define aquí:
background-image:url("URL-DE-TU-IMAGEN");
Solo debes sustituir esa dirección por tu propia imagen.
Algunas ideas:
- madera
- papel antiguo
- flores
- naturaleza
- una textura vintage
También puedes cambiar:
background-size:cover; background-position:center;
si quieres otro tipo de encuadre.
Paso 2: Cambiar la portada del disco
Cada canción utiliza una imagen diferente. El proceso de la imágen es el mismo que se menciona en entradas anteriores.
Dentro de la playlist encontrarás algo como:
cover:"URL-DE-LA-PORTADA"
Solo debes cambiar esa dirección por la imagen que quieras mostrar.
Lo ideal es utilizar imágenes cuadradas para que el efecto circular se vea correctamente.
Paso 3: Entender la portada giratoria
La animación se consigue mediante:
@keyframes spin
Cuando la música comienza:
cover.classList.add("playing");
La imagen empieza a girar.
Cuando la música se pausa:
cover.classList.remove("playing");
La animación también se detiene.
Puedes modificar la velocidad aquí:
animation:spin 8s linear infinite;
Por ejemplo:
Más rápida:
animation:spin 5s linear infinite;
Más lenta:
animation:spin 15s linear infinite;
Paso 4: Añadir canciones
Toda la música se encuentra en este bloque:
const songs = [
Cada canción tiene cuatro datos:
{ name:"name", artist:"artist", videoId:"URLvideo", cover:"URLimage" }
¿Qué significa cada uno?
name
El nombre de la canción.
artist
El nombre del artista.
videoId
El identificador del vídeo de YouTube.
Por ejemplo, si el enlace es:
https://youtu.be/LAbZfjMVU0A?si=3h_LsPX-ZhG2QXJh
el videoId es únicamente:
LAbZfjMVU0A
cover
La imagen que aparecerá como portada.
Para añadir una nueva canción basta con copiar uno de los bloques y cambiar esos cuatro valores. Se pueden añadir cuantos bloques quieras.
Paso 5: Personalizar los botones
Los controles se crean aquí:
<button>⏮</button> <button>▶ / ⏸</button> <button>⏭</button>
Puedes sustituir los símbolos por:
- texto
- otros emojis
- iconos personalizados
También puedes modificar su aspecto desde:
.btn
Cambiando:
- color
- bordes
- tamaño
- tipografía
Paso 6: La playlist desplegable
La lista de canciones permanece oculta hasta pulsar:
togglePlaylist()
Cuando se abre:
.playlist.open
aparece un pequeño panel con desplazamiento.
Puedes cambiar la altura máxima:
max-height:130px;
Si tienes muchas canciones, puedes aumentarla.
Paso 7: La barra de progreso
La barra inferior refleja el avance de la canción.
Se actualiza automáticamente gracias a:
setInterval(...)
Cada medio segundo calcula cuánto ha avanzado el vídeo.
Además, el usuario puede mover la barra manualmente para saltar a otro momento de la canción.
Paso 8: Cambiar los textos del reproductor
Actualmente aparecen estados como:
READY PLAYING PAUSED
Puedes traducirlos.
Por ejemplo:
Listo Reproduciendo Pausado
Paso 9: Instalarlo en Blogger
- Entra al panel de Blogger.
- Ve a Diseño.
- Selecciona Añadir un gadget.
- Elige HTML/Javascript.
- Pega el código completo.
- Guarda los cambios.
El reproductor aparecerá donde hayas colocado el gadget.
Personalizaciones bonitas
Busca:
border-radius:50%;
Puedes convertir el disco en una imagen cuadrada:
border-radius:14px;
Busca:
.screen
Puedes modificar:
- transparencia
- colores
- bordes
Busca:
accent-color:#c86b7a;
Puedes sustituirlo por cualquier color que combine con tu blog.
Busca:
animation:spin 8s linear infinite;
Cuanto menor sea el número, más rápido girará.
Ideas para usar este gadget
Este reproductor puede servir para:
- ☕ compartir la música que acompaña tus entradas
- 📖 crear una banda sonora para tu blog
- 🌙 recomendar canciones del mes
- 🍂 hacer playlists según la estación del año
- 🎼 acompañar una sección de recomendaciones musicales
También puedes crear listas temáticas, como:
- música para estudiar
- canciones tranquilas
- clásicos favoritos
- descubrimientos del mes
La historia detrás del gadget
Muchas veces la música forma parte del recuerdo que tenemos de un lugar. Este reproductor nació con la idea de que un blog también pudiera tener su propia banda sonora, no solo como un complemento, sino como otra forma de contar quién eres y qué ambiente quieres transmitir. La portada girando lentamente recuerda a los discos de vinilo y a esos momentos en los que una canción sonaba de principio a fin sin prisas. La playlist invita a descubrir nuevas melodías y a quedarse un rato más, acompañando la lectura con música.
Porque, al igual que las palabras, las canciones también pueden hacer que un rincón digital se sienta más vivo.
Código:
<style>
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+JP:wght@300;400&display=swap');
.yt-player {
width: 100%;
max-width: 280px;
font-family: "Noto Serif JP", serif;
/* 🌸 FIX REAL: background-image (más compatible que shorthand) */
background-image: url("URLimage");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
border-radius: 18px;
padding: 14px;
box-shadow: 0 12px 30px rgba(0,0,0,0.18);
position: relative;
overflow: hidden;
}
/* 🌑 overlay seguro (no bloquea clicks) */
.yt-player::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.45);
border-radius: 18px;
z-index: 1;
pointer-events: none;
}
/* 🔝 capas correctas */
.yt-player > * {
position: relative;
z-index: 2;
}
/* YouTube por encima del overlay */
#ytPlayer {
position: relative;
z-index: 3;
}
/* portada */
.cover {
width: 100%;
height: 150px;
border-radius: 14px;
overflow: hidden;
margin-bottom: 10px;
}
.cover{
width:100%;
height:150px;
border-radius:50%;
overflow:hidden;
margin-bottom:10px;
display:flex;
align-items:center;
justify-content:center;
background:rgba(255,255,255,.12);
box-shadow:
inset 0 0 20px rgba(255,255,255,.12),
0 8px 18px rgba(0,0,0,.35);
position:relative;
}
/* centro del "vinilo" */
.cover::after{
content:"";
position:absolute;
width:18px;
height:18px;
background:#f5f5f5;
border:4px solid #555;
border-radius:50%;
z-index:2;
}
.cover img{
width:100%;
height:100%;
object-fit:cover;
border-radius:50%;
animation:spin 8s linear infinite;
animation-play-state:paused;
}
.cover img.playing{
animation-play-state:running;
}
@keyframes spin{
from{
transform:rotate(0deg);
}
to{
transform:rotate(360deg);
}
}
/* pantalla */
.screen {
background: rgba(255,255,255,0.45);
border-radius: 12px;
padding: 10px;
font-size: 12px;
color: #5a2a2a;
}
/* controles */
.controls {
display: flex;
gap: 4px;
margin-top: 10px;
}
.btn {
flex: 1;
border: none;
padding: 6px;
border-radius: 10px;
cursor: pointer;
background: rgba(255,255,255,0.6);
font-size: 11px;
}
/* progress */
#progress {
width: 100%;
margin-top: 8px;
accent-color: #c86b7a;
}
/* playlist toggle */
.playlist-toggle {
margin-top: 10px;
width: 100%;
border: none;
padding: 6px;
border-radius: 10px;
background: rgba(255,255,255,0.6);
cursor: pointer;
font-size: 11px;
}
/* playlist */
.playlist {
margin-top: 10px;
max-height: 0;
overflow: hidden;
font-size: 11px;
transition: max-height 0.4s ease;
}
/* scroll cuando abierta */
.playlist.open {
max-height: 130px;
overflow-y: auto;
scrollbar-width: thin;
}
.playlist.open::-webkit-scrollbar {
width: 4px;
}
.playlist.open::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.5);
border-radius: 10px;
}
/* canciones */
.song {
padding: 6px;
border-radius: 8px;
cursor: pointer;
}
.song:hover {
background: rgba(255,255,255,0.4);
}
.song.active {
background: rgba(255,255,255,0.7);
}
/* artista */
.artist {
font-size: 9px;
opacity: 0.7;
margin-top: 2px;
}
</style>
<div class="yt-player">
<div class="cover">
<img id="cover" src="" />
</div>
<div class="screen">
🎧 <b id="title">IGARI YOUTUBE</b><br />
<div class="artist" id="artist"></div>
<span id="status">READY</span>
</div>
<div class="controls">
<button class="btn" onclick="prev()">⏮</button>
<button class="btn" onclick="toggle()">▶ / ⏸</button>
<button class="btn" onclick="next()">⏭</button>
</div>
<input type="range" id="progress" min="0" max="100" value="0" />
<button class="playlist-toggle" onclick="togglePlaylist()">🎼 Playlist</button>
<div class="playlist" id="playlist"></div>
<div id="ytPlayer"></div>
</div>
<script src="https://www.youtube.com/iframe_api"></script>
<script>
let player;
let current = 0;
let isPlaying = false;
const songs = [
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" },
{ name: "Name", artist: "Artist", videoId: "VideoId", cover: "URLimage" }
];
const playlist = document.getElementById("playlist");
const title = document.getElementById("title");
const artist = document.getElementById("artist");
const status = document.getElementById("status");
const cover = document.getElementById("cover");
const progress = document.getElementById("progress");
songs.forEach((s, i) => {
const div = document.createElement("div");
div.className = "song";
div.innerHTML = s.name + `<div class="artist">${s.artist}</div>`;
div.onclick = () => load(i);
playlist.appendChild(div);
});
function onYouTubeIframeAPIReady() {
player = new YT.Player('ytPlayer', {
height: '0',
width: '0',
videoId: songs[0].videoId,
events: { onReady: () => load(0) }
});
}
function load(i) {
current = i;
cover.classList.remove("playing");
player.loadVideoById(songs[i].videoId);
player.pauseVideo();
title.innerText = songs[i].name;
artist.innerText = songs[i].artist;
cover.src = songs[i].cover;
document.querySelectorAll(".song").forEach((el, idx) => {
el.classList.toggle("active", idx === i);
});
status.innerText = "READY";
isPlaying = false;
}
function toggle() {
if (isPlaying) {
player.pauseVideo();
status.innerText = "PAUSED";
cover.classList.remove("playing");
} else {
player.playVideo();
status.innerText = "PLAYING";
cover.classList.add("playing");
}
isPlaying = !isPlaying;
}
function next() {
current = (current + 1) % songs.length;
load(current);
player.playVideo();
cover.classList.add("playing");
status.innerText = "PLAYING";
isPlaying = true;
}
function prev() {
current = (current - 1 + songs.length) % songs.length;
load(current);
player.playVideo();
cover.classList.add("playing");
status.innerText = "PLAYING";
isPlaying = true;
}
function togglePlaylist() {
playlist.classList.toggle("open");
}
setInterval(() => {
if (player && player.getCurrentTime && player.getDuration) {
progress.value = (player.getCurrentTime() / player.getDuration()) * 100 || 0;
}
}, 500);
progress.addEventListener("input", () => {
if (player) {
player.seekTo((progress.value / 100) * player.getDuration(), true);
}
});
</script>

No hay comentarios.:
Publicar un comentario