Hướng Dẫn Tạo Trình Phát Nhạc Cho Blogger Đẹp - Giao Diện Đẹp, Tương Thích Mobile

Hướng dẫn tích hợp trình phát nhạc nhẹ, đẹp và tối ưu SEO cho Blogger. Tương thích mọi thiết bị, dễ tùy biến, tải nhanh và không cần plugin phức tạp.
Please wait 0 seconds...
Scroll Down and click on Go to Link for destination
Congratulations! The link has been created
Chào các bạn !

Nếu bạn muốn làm cho blog của mình thêm sinh động và cá nhân hóa bằng cách thêm một trình phát nhạc nhỏ gọn, có hiển thị lời bài hát, thì đây là hướng dẫn dành cho bạn. Chúng ta sẽ sử dụng mã HTML, CSS và JavaScript sẵn có để tích hợp vào nền tảng blog của bạn.

Chia sẻ về trình phát nhạc đa tính năng

Trình phát nhạc này là một thiết kế tối ưu với giao diện đẹp mắt và nhiều tính năng hữu ích dành cho người yêu âm nhạc. Mình đã dành thời gian để cân chỉnh từng chi tiết nhỏ, từ hiệu ứng album art xoay tròn cho đến phần lời bài hát (lyrics) hiển thị theo thời gian thực.

Những điểm nổi bật:
  • Giao diện tinh tế: Thiết kế responsive, phù hợp với mọi thiết bị từ máy tính đến điện thoại.
  • Hiệu ứng hình ảnh: Album art xoay khi phát nhạc và dừng khi tạm dừng, tạo cảm giác sống động.
  • Lyrics sync: Lời bài hát hiển thị theo thời gian thực, tự động cuộn mượt mà và highlight câu đang phát.
  • Điều khiển linh hoạt: Có đầy đủ các nút play/pause, next/prev, volume slider, chế độ lặp (repeat/shuffle).
  • Danh sách nhạc: Hiển thị danh sách bài hát với thời lượng, có thể click để chuyển bài ngay lập tức.

Hướng Dẫn Tạo Trình Phát Nhạc Cho Blogger


Mở trình chỉnh sửa template blog của bạn (chuyển sang chế độ chỉnh sửa HTML) hoặc phần bố cục .
Tìm vị trí bạn muốn đặt trình phát nhạc.
Sao chép toàn bộ mã HTML dưới đây và dán mã HTML đã sao chép vào vị trí đó.
{html} <div class='music-box'> <div class='song-details'> <span class='name'></span> <span class='artist'></span> </div> <div class='img-area'> <img alt='' loading='lazy' src='' title='' /> </div> <div class="music-lyrics-bar"> <div class="lyrics-animate-list" id="lyricsList"></div> </div> <div class='progress-area'> <div class='progress-bar'> <audio id='main-audio' src=''></audio> </div> <div class='song-timer'> <span class='current-time'>0:00</span> <span class='max-duration'>0:00</span> </div> </div> <div class='controls'> <span class="svg-icon music-repeat" id="repeat-plist" title="Playlist looped"> <svg id="icon-repeat" fill="#eb8721" xmlns="http://www.w3.org/2000/svg" height="23" width="23" viewbox="0 0 24 24"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"/><path d="M0 0h24v24H0z" fill="none"/></path></path></svg> <svg id="icon-repeatone" style="display:none" fill="#eb8721" xmlns="http://www.w3.org/2000/svg" height="23" width="23" viewbox="0 0 24 24"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4zm-4-2V9h-1l-2 1v1h1.5v4H13z"/><path d="M0 0h24v24H0z" fill="none"/></path></path></svg> <svg id="icon-shuffle" style="display:none" fill="#eb8721" xmlns="http://www.w3.org/2000/svg" height="23" width="23" viewbox="0 0 24 24"><path d="M10.59 9.17 5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"/><path d="M0 0h24v24H0z" fill="none"/></path></path></svg> <span style="display:none">repeat</span> </span> <span class="svg-icon" id="prev"> <svg fill="#eb8721" xmlns="http://www.w3.org/2000/svg" height="23" width="23" viewbox="0 0 24 24"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/><path d="M0 0h24v24H0z" fill="none"/></path></path></svg> </span> <div class='play-pause'> <span class="svg-icon" id="icon-play"> <svg fill="#fff" xmlns="http://www.w3.org/2000/svg" height="30" width="30" viewbox="0 0 24 24"><path d="M8 5v14l11-7z"/><path d="M0 0h24v24H0z" fill="none"/></path></path></svg> </span> <span class="svg-icon" id="icon-pause" style="display:none"> <svg fill="#fff" xmlns="http://www.w3.org/2000/svg" height="30" width="30" viewbox="0 0 24 24"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/><path d="M0 0h24v24H0z" fill="none"/></path></path></svg> </span> </div> <span class="svg-icon" id="next"> <svg fill="#eb8721" xmlns="http://www.w3.org/2000/svg" height="23" width="23" viewbox="0 0 24 24"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/><path d="M0 0h24v24H0z" fill="none"/></path></path></svg> </span> <span class="svg-icon" id="more-music"> <svg fill="#eb8721" xmlns="http://www.w3.org/2000/svg" height="23" width="23" viewbox="0 0 24 24"><g><rect fill="none" height="24" width="24"/><path d="M15,6H3v2h12V6z M15,10H3v2h12V10z M3,16h8v-2H3V16z M17,6v8.18C16.69,14.07,16.35,14,16,14c-1.66,0-3,1.34-3,3s1.34,3,3,3 s3-1.34,3-3V8h3V6H17z"/></path></rect></g></svg> </span> </div> <div class='slider_volumn'> <span class="svg-icon" id="min-volume"> <svg fill="#eb8721" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewbox="0 0 24 24"><path d="M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z"/><path d="M0 0h24v24H0z" fill="none"/></path></path></svg> </span> <input class='volume_slider' max='100' min='1' type='range' value='100' /> <span class="svg-icon" id="max-volume"> <svg fill="#eb8721" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewbox="0 0 24 24"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/><path d="M0 0h24v24H0z" fill="none"/></path></path></svg> </span> </div> <div class='music-list'> <div class='header-music'> <div class='row-music'> <span class="svg-icon"> <svg fill="#666666" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewbox="0 0 24 24"><g><rect fill="none" height="24" width="24"/><path d="M15,6H3v2h12V6z M15,10H3v2h12V10z M3,16h8v-2H3V16z M17,6v8.18C16.69,14.07,16.35,14,16,14c-1.66,0-3,1.34-3,3s1.34,3,3,3 s3-1.34,3-3V8h3V6H17z"/></path></rect></g></svg> </span> <span>Music list</span> </div> <span class="svg-icon" id="close"> <svg fill="#666666" xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewbox="0 0 24 24"><path d="M24 20.188l-8.315-8.209 8.2-8.282-3.697-3.697-8.212 8.318-8.31-8.203-3.666 3.666 8.321 8.24-8.206 8.313 3.666 3.666 8.237-8.318 8.285 8.203z"/></path></svg> </span> </div> <ul> </ul> </div> </div>
Sao chép toàn bộ mã CSS dưới vào phần <head>.
{css} <style> .music-box{width:100%;padding:10px;overflow:hidden;position:relative;background:#fff; box-shadow:0 6px 15px 0 12px 25px 0 rgb(30 30 30 / 4%); font-family: system-ui,sans-serif; } .music-box .svg-icon{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;} .top-bar,.progress-area .song-timer,.music-list .header-music,.music-list ul li{display:flex;align-items:center;justify-content:space-between} .controls{display:flex;align-items:center;justify-content:space-evenly} .top-bar span:not(.svg-icon){font-size:16px;margin-left:-3px;color:#555} .img-area{width:185px;height:185px;position:relative;overflow:hidden;margin:0px auto 0;border:5px solid #fff;border-radius:50%;box-shadow:0 6px 12px rgba(0,0,0,0.09)} .img-area img{width:100%;height:100%;border-radius:50%;object-fit:cover} .music-box.paused .img-area img{animation: rotateAlbumArt 3s linear 0s infinite forwards} .song-details{ display: flex; flex-direction: column; align-items: center;} .song-details .name{font-size:16px;font-weight:600;color:#222} .song-details .artist{font-size:13px;opacity:.9;line-height:1.5;color:#888} .progress-area{height:10px;width:100%;border-radius:50px;background:#f0f0f0;cursor:pointer;margin-top:10px;position:relative;} .progress-area .progress-bar{height:inherit;width:0;position:relative;border-radius:inherit;background:linear-gradient(90deg,#eb8721 0%,#eb8721 100%)} .progress-area .song-timer{margin-top:2px} .song-timer span{font-size:13px;color:#666} .controls{margin:25px 0 14px} .controls .play-pause{height:44px;width:44px;display:flex;cursor:pointer;align-items:center;justify-content:center;border-radius:50%;background:#eb8721;box-shadow:0 2px 5px #7772} .play-pause .svg-icon{width:30px;height:30px;} .music-list{ position:absolute;background:#fff;width:100%;left:0;bottom:-55%;opacity:0;pointer-events:none;z-index:5;padding:15px 12px 12px 12px; box-shadow:0 -5px 10px rgba(0,0,0,0.05);transition:all .20s cubic-bezier(.4,0,.2,1); border-radius: 0 0 16px 16px; } .music-list.show{bottom:0;opacity:1;pointer-events:auto} .header-music .row-music{display:flex;align-items:center;font-size:19px} .header-music .row-music .svg-icon{cursor:default} .header-music .row-music span{margin-left:5px;font-size:15px;color:#666666} .music-list ul{margin:10px 0 0 0 !important;max-height:260px;overflow:auto;padding:0;} .music-list ul::-webkit-scrollbar{width:4px;background:#f0f0f0;} .music-list ul::-webkit-scrollbar-thumb{background:#ddd;border-radius:8px;} .music-list ul li{ list-style:none;display:flex;align-items:center;justify-content:space-between; cursor:pointer;padding:12px 0 12px 0;margin-bottom:0; color:#666;border-bottom:1px solid #E5E5E5;gap:10px;transition:background .15s; } .music-list ul li:last-child{border-bottom:0} .music-list ul li .row{flex: 1; display: flex; flex-direction: column; margin: 0;} .music-list ul li .row span{font-size:15px;font-weight:500;display:block;} .music-list ul li .row p{opacity:.9;font-size:13px;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} ul li .audio-duration{font-size:13px;min-width:42px;text-align:right;opacity:.7;} ul li.playing{pointer-events:none;color:#eb8721;background:#f6f7ff;} ul li.playing .audio-duration{color:#eb8721;opacity:1;} @keyframes rotateAlbumArt{0%{ transform: rotateZ(0)}100%{ transform: rotateZ(360deg)}} ::-webkit-input-placeholder { color:#bbb; } ::-moz-placeholder { color:#bbb; } :-ms-input-placeholder { color:#bbb; } ::placeholder { color:#bbb; } /* --- Volume Slider Section: Modern, Precise, and Lyrics Integration --- */ .slider_volumn{ display:flex; justify-content:center; align-items:center; gap:10px; margin-bottom:8px; padding:8px 0 6px 0; background: #f7f7fb; border-radius: 10px; position: relative; } .volume_slider{ height:7px; width:110px; border-radius:50px; background:linear-gradient(90deg,#bfc1e7 0,#eb8721 100%); cursor:pointer; padding:0;overflow:hidden;transition: opacity .2s;-webkit-appearance:none;outline:none; margin:0 2px; } .volume_slider::-webkit-slider-thumb{ -webkit-appearance:none;appearance:none; width: 13px;height: 13px; background:#eb8721;cursor: pointer; border-radius:50%; border:2px solid #fff; box-shadow:0 2px 5px #9992; transition:background .2s; } .volume_slider:active::-webkit-slider-thumb{background:#eb8721;} .volume_slider::-moz-range-thumb { width:13px;height:13px;background:#eb8721;cursor:pointer;border-radius:50%;border:2px solid #fff;box-shadow:0 2px 5px #9992; } .volume_slider:focus{box-shadow:0 0 0 2px #bfc1e7;} #min-volume,#max-volume{opacity:.85;transition:.2s;} #min-volume:hover,#max-volume:hover{opacity:1;transform:scale(1.08);} .music-lyrics-bar { width: 100%; height: 80px; /* Chiều cao cố định */ overflow: hidden; position: relative; display: flex; flex-direction: column; justify-content: flex-start; align-items: center; background: rgba(245, 245, 255, 0.85); border-radius: 8px; box-shadow: 0 2px 5px #eb872113; } #lyricsList { display: flex; flex-direction: column; align-items: center; justify-content: flex-start; transform: translateY(0); /* Điểm khởi đầu */ transition: transform 0.4s ease-in-out; /* Hiệu ứng mượt khi trượt */ } .lyric-line { font-size: 15px; color: #888; font-weight: 500; line-height: 1.5; padding: 5px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: color 0.2s, font-weight 0.2s; } .lyric-line.active { color: #eb8721; font-weight: 600; } .music-lyrics-bar span{ display:inline-block; padding:3px 8px; max-width:90%; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-weight:500; position:relative; z-index:1; } .music-lyrics-bar .lyrics-highlight{ color:#eb8721; background:linear-gradient(90deg,#e3e4fa 30%,#fff 70%); border-radius:6px; padding:3px 9px; font-weight:600; box-shadow:0 1px 3px #eb872122; transition:background .25s,color .22s; /* hiệu ứng trượt từ dưới lên */ opacity: 1; transform: translateY(24px); animation: lyricsSlideUp .45s cubic-bezier(.4,0,.2,1); will-change: transform, opacity; } @keyframes lyricsSlideUp{ from{ opacity:0; transform: translateY(24px); } to{ opacity:1; transform: translateY(0); } } @media (max-width: 400px){ .music-box{max-width:100vw;} .img-area{width:120px;height:120px;} .music-lyrics-bar{font-size:13px;} } .music-lyrics-bar { width: 100%; min-height: 48px; margin: 0 auto 8px auto; background: rgba(245,245,255,0.85); border-radius: 8px; box-shadow: 0 2px 5px #eb872113; position: relative; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; overflow: hidden; padding: 0; } .lyrics-animate-list { width: 100%; height: 70px; position: relative; overflow: hidden; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; transition: background .2s; } .lyric-line { font-size: 15px; color: #888; font-weight: 500; line-height: 1.5; padding: 0 8px; max-width: 90%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; opacity: 0.5; transition: opacity .2s, color .2s, font-weight .2s; } .lyric-line.active { color: #eb8721; border-radius: 6px; font-weight: 700; opacity: 1;padding: 8px 0px 8px 0px; box-shadow:0 1px 3px #eb872122; animation: zingLyricsUp .5s cubic-bezier(.4,0,.2,1); } @keyframes zingLyricsUp { from { opacity: 0; transform: translateY(20px); /* Giảm khoảng cách để hiệu ứng tự nhiên hơn */ } to { opacity: 1; transform: translateY(0); } } @media (max-width: 400px){ .music-lyrics-bar{font-size:13px;} .lyric-line {font-size:13px;} } </style>

Bạn cần dán mã JavaScript này vào trước thẻ đóng </body> trong template blog của mình. 
Mở trình chỉnh sửa template blog của bạn (chuyển sang chế độ chỉnh sửa HTML).
Dán mã JavaScript đã sao chép vào ngay trước thẻ </body>.

{js} <script> // --- Music Data with Lyrics (timed) --- let allMusic = [ { name: "Tái Sinh", artist: "Tùng Dương", img: "https://photo-resize-zmp3.zmdcdn.me/w240_r1x1_jpeg/cover/0/5/0/1/050158601068bfb3b08fd8da7276a32d.jpg", src: "https://raw.githubusercontent.com/higenz/cdn/refs/heads/main/T%C3%A1i%20Sinh-T%C3%B9ng%20D%C6%B0%C6%A1ng.mp3", id: "m1", lyrics: [ { time: "00:00", text: "Tái Sinh" }, { time: "00:08", text: "Tùng Dương" }, { time: "00:19", text: "Cơn mưa rơi tưới mát tâm hồn" }, { time: "00:22", text: "Nhẹ nhàng và cơn mưa như đang tái sinh ta" }, { time: "00:29", text: "Cơn mưa mang em đến bên đời" }, { time: "00:33", text: "Dịu dàng và sau cơn mưa tim cũng nở hoa" }, { time: "00:39", text: "Bầu trời xanh đến lạ nắng tưới lên hàng lá non" }, { time: "00:44", text: "Ban mai ôm lấy tâm hồn từ khi em ghé qua" }, { time: "00:40", text: "Và tình yêu bắt đầu khi ta nhìn vào mắt nhau" }, { time: "00:45", text: "Anh thấy tim mình tái sinh sau nhiều cơn đau" }, { time: "00:50", text: "Chìm vào trong ánh mắt đắm đuối anh ngỡ mình như đang đôi mươi" }, { time: "00:55", text: "Người làm anh biết yêu đắm say như tình đầu" }, { time: "01:00", text: "Một vì sao sáng giữa bóng tối ban mai về sau cơn mưa rơi" }, { time: "01:05", text: "Tái sinh anh từ đống tro tàn tình yêu" }, { time: "01:10", text: "Chỉ cần một ánh mắt mọi muộn phiền chợt biến mất" }, { time: "01:15", text: "Dịu dàng và ngây thơ như tái sinh linh hồn" }, { time: "01:20", text: "Thì thầm vào bên tai chẳng buồn nào là mãi mãi" }, { time: "01:25", text: "Cũng sẽ đến khi mờ phai" }, { time: "01:30", text: "Bầu trời xanh đến lạ nắng tưới lên hàng lá non" }, { time: "01:35", text: "Ban mai ôm lấy tâm hồn từ khi em ghé qua" }, { time: "01:40", text: "Và tình yêu bắt đầu khi ta nhìn vào mắt nhau" }, { time: "01:45", text: "Anh thấy tim mình tái sinh sau nhiều cơn đau" }, { time: "01:50", text: "Chìm vào trong ánh mắt đắm đuối anh ngỡ mình như đang đôi mươi" }, { time: "01:55", text: "Người làm anh biết yêu đắm say như tình đầu" }, { time: "02:00", text: "Một vì sao sáng giữa bóng tối ban mai về sau cơn mưa rơi" }, { time: "02:05", text: "Tái sinh anh từ đống tro tàn tình yêu" }, { time: "02:10", text: "Chìm vào trong ánh mắt đắm đuối anh ngỡ mình như đang đôi mươi" }, { time: "02:15", text: "Người làm anh biết yêu đắm say như tình đầu" }, { time: "02:20", text: "Một vì sao sáng giữa bóng tối ban mai về sau cơn mưa rơi" }, { time: "02:25", text: "Tái sinh anh từ đống tro tàn tình yêu" }, { time: "02:30", text: "Cơn mưa rơi tưới mát tâm hồn" }, { time: "02:35", text: "Nhẹ nhàng và cơn mưa như đang tái sinh ta" }, { time: "02:40", text: "Cơn mưa mang em đến bên đời" }, { time: "02:45", text: "Dịu dàng và sau cơn mưa tim cũng nở hoa" } ] }, { name: "Bắc Bling (Bắc Ninh)", artist: "Hòa Minzy, Xuân Hinh, Tuấn Cry, Masew", img: "https://photo-resize-zmp3.zmdcdn.me/w240_r1x1_jpeg/cover/e/3/3/9/e3399633d5289da5cb7c4292a41f0f67.jpg", src: "https://raw.githubusercontent.com/higenz/cdn/refs/heads/main/B%E1%BA%AFc%20Bling%20(B%E1%BA%AFc%20Ninh)-Nhi%E1%BB%81u%20ngh%E1%BB%87%20s%C4%A9.mp3", id: "m2", lyrics: [ { time: "00:12", text: "Ta về ta tắm ao ta" }, { time: "00:14", text: "Dù trong dù đục ao nhà vẫn hơn" }, { time: "00:17", text: "Tình quê son sắt keo sơn" }, { time: "00:20", text: "Hương đồng gió nội cây rơm đợi chờ" }, { time: "00:24", text: "Mời bà con về Bắc Ninh em chơi nào" }, { time: "00:33", text: "Bắc Ninh vốn trọng chữ tình" }, { time: "00:36", text: "Nón quai thao em đợi ở sân đình" }, { time: "00:38", text: "Mấy anh hai quay đầu nhìn cũng đỉnh" }, { time: "00:41", text: "Các dân chơi gọi là Bắc Bling Bling" }, { time: "00:43", text: "Tấm lòng son sắt" }, { time: "00:45", text: "Ta ngân nga câu quan họ" }, { time: "00:46", text: "Chào mừng về miền đất" }, { time: "00:47", text: "Bến nước và con đò" }, { time: "00:49", text: "Tinh hoa không thể nào mất" }, { time: "00:50", text: "Chất ở local" }, { time: "00:51", text: "Sửa soạn áo the khăn xếp" }, { time: "00:52", text: "Anh em cùng lên đồ" }, { time: "00:54", text: "Lướt trên sông như là lướt trên mây" }, { time: "00:58", text: "Ngước bên đông" }, { time: "00:58", text: "Xong rồi lại ngước bên tây" }, { time: "01:00", text: "Miếng trầu này" }, { time: "01:01", text: "Em đã ướp thêm say" }, { time: "01:02", text: "Mang lời hứa trao duyên" }, { time: "01:03", text: "Như bao hẹn ước đêm nay" }, { time: "01:05", text: "Sáng như đêm trăng rằm hội Lim" }, { time: "01:07", text: "Biết em đâu mà tìm" }, { time: "01:11", text: "Trót thương em đâu thể ngồi im" }, { time: "01:14", text: "Cầm khăn í a làm tin" }, { time: "01:16", text: "Sáng như đêm trăng rằm hội Lim" }, { time: "01:18", text: "Biết em đâu mà tìm" }, { time: "01:21", text: "Trót thương em đâu thể ngồi im" }, { time: "01:24", text: "Cầm khăn í a í a làm tin" }, { time: "01:40", text: "Ăn một miếng trầu" }, { time: "01:45", text: "Ăn một miếng trầu" }, { time: "01:50", text: "Ăn vào cho đỏ" }, { time: "01:55", text: "Môi mình môi ta" }, { time: "01:00", text: "Ăn một miếng trầu" }, { time: "01:05", text: "Bắc một chiếc cầu" }, { time: "03:10", text: "Bao lời chưa ngỏ" }, { time: "03:15", text: "Duyên mình duyên ta" }, { time: "03:20", text: "Mời anh về Bắc Ninh em chơi thăm" }, { time: "03:25", text: "Lễ hội nô nức đông vui quanh năm" }, { time: "03:30", text: "Qua đền Bà Chúa cầu lộc cầu tài" }, { time: "03:35", text: "Sang rằm tháng Tám ung dung chơi trăng" }, { time: "03:40", text: "Đua thuyền rẽ sóng trên sông Như Nguyệt" }, { time: "03:45", text: "Bao sử sách địa linh nhân kiệt" }, { time: "03:50", text: "Tam Phủ chốn bồng lai linh thiêng" }, { time: "03:55", text: "Nương nhờ cửa thánh con xin cúi đầu" }, { time: "04:00", text: "Người ơ í ơi ì a í a" }, { time: "04:05", text: "Người ở đừng về" }, { time: "04:10", text: "Để em ngày đêm ngóng trông" }, { time: "04:15", text: "Hình bóng anh khắp bốn bề" }, { time: "04:20", text: "Anh có thương thì qua" }, { time: "04:25", text: "Chứ anh đừng hứa đừng thề" }, { time: "04:30", text: "Về đây Bắc Ninh cùng em" }, { time: "04:35", text: "Thắm nồng tình quê" }, { time: "04:40", text: "Ăn một miếng trầu" }, { time: "04:45", text: "Ăn một miếng trầu" }, { time: "04:50", text: "Ăn vào cho đỏ" }, { time: "04:55", text: "Môi mình môi ta" }, { time: "05:00", text: "Ăn một miếng trầu" }, { time: "05:05", text: "Bắc một chiếc cầu" }, { time: "05:10", text: "Bao lời chưa ngỏ" }, { time: "05:15", text: "Duyên mình duyên ta" }, { time: "05:20", text: "Mời anh về Bắc Ninh em chơi thăm" }, { time: "05:25", text: "Lễ hội nô nức đông vui quanh năm" }, { time: "03:30", text: "Qua đền Bà Chúa cầu lộc cầu tài" }, { time: "03:35", text: "Sang rằm tháng Tám ung dung chơi trăng" }, { time: "03:40", text: "Đua thuyền rẽ sóng trên sông Như Nguyệt" }, { time: "03:45", text: "Bao sử sách địa linh nhân kiệt" }, { time: "03:50", text: "Tam Phủ chốn bồng lai linh thiêng" }, { time: "03:55", text: "Nương nhờ cửa thánh con xin cúi đầu" }, { time: "06:00", text: "Người ơ í ơi ì a í a" } ] }, { name: "Mạnh Bà (ZZ Remix)", artist: "Linh Hương Luz, Meme Media", img: "https://photo-resize-zmp3.zadn.vn/w240_r1x1_jpeg/cover/c/8/3/d/c83ded64795d6ce9a3842938e3f6e4af.jpg", src: "https://raw.githubusercontent.com/higenz/cdn/refs/heads/main/M%E1%BA%A1nh%20B%C3%A0%20(ZZ%20Remix)%20-%20Linh%20H%C6%B0%C6%A1ng%20Luz%2C%20Meme%20Media.mp3", id: "m3", lyrics: [ { time: 0, text: "Mạnh Bà (ZZ Remix)" }, { time: 4, text: "Linh Hương Luz, Meme Media" }, { time: 12, text: "Lời bài hát sẽ hiển thị ở đây..." }, { time: 24, text: "Cảm xúc dâng trào..." } ] } ]; // Helper: convert time string "mm:ss" or seconds to seconds function parseTime(t) { if (typeof t === "number") return t; let parts = t.split(":"); if (parts.length === 1) return parseFloat(parts[0]); return parseInt(parts[0], 10) * 60 + parseInt(parts[1], 10); } // Chuyển đổi lyrics {time: "...", text: "..."} sang [{time: float, text: "..."}] (chỉ làm 1 lần duy nhất) for (let song of allMusic) { if (song.lyrics && song.lyrics.length && typeof song.lyrics[0].time === "string") { song.lyrics = song.lyrics.map(l => ({ time: parseTime(l.time), text: l.text })); } } const wrapper = document.querySelector(".music-box"), musicImg = wrapper.querySelector(".img-area img"), musicName = wrapper.querySelector(".song-details .name"), musicArtist = wrapper.querySelector(".song-details .artist"), playPauseBtn = wrapper.querySelector(".play-pause"), prevBtn = wrapper.querySelector("#prev"), nextBtn = wrapper.querySelector("#next"), mainAudio = wrapper.querySelector("#main-audio"), progressArea = wrapper.querySelector(".progress-area"), progressBar = progressArea.querySelector(".progress-bar"), musicList = wrapper.querySelector(".music-list"), moreMusicBtn = wrapper.querySelector("#more-music"), closemoreMusic = musicList.querySelector("#close"), volumeslider = wrapper.querySelector(".volume_slider"), maxvolum = wrapper.querySelector("#max-volume"), minvolum = wrapper.querySelector("#min-volume"), iconPlay = document.getElementById("icon-play"), iconPause = document.getElementById("icon-pause"), iconRepeat = document.getElementById("icon-repeat"), iconRepeatOne = document.getElementById("icon-repeatone"), iconShuffle = document.getElementById("icon-shuffle"); // let lyricsLine = document.getElementById("lyricsLine"); // Không cần nữa let musicIndex = Math.floor(Math.random() * allMusic.length); let isMusicPaused = true; let curr_track = mainAudio; window.addEventListener("load", () => { loadMusic(musicIndex); playingSong(); }); function loadMusic(indexNumb) { const music = allMusic[indexNumb]; musicName.innerText = music.name; musicArtist.innerText = music.artist; musicImg.alt = music.artist; musicImg.title = music.artist; musicImg.src = music.img; mainAudio.src = music.src; updateLyrics(0, true); // reset lyrics } function playMusic() { wrapper.classList.add("paused"); iconPlay.style.display = "none"; iconPause.style.display = ""; mainAudio.play(); isMusicPaused = false; } function pauseMusic() { wrapper.classList.remove("paused"); iconPlay.style.display = ""; iconPause.style.display = "none"; mainAudio.pause(); isMusicPaused = true; } function prevMusic() { musicIndex--; if (musicIndex < 0) musicIndex = allMusic.length - 1; loadMusic(musicIndex); playMusic(); playingSong(); } function nextMusic() { musicIndex++; if (musicIndex >= allMusic.length) musicIndex = 0; loadMusic(musicIndex); playMusic(); playingSong(); } playPauseBtn.addEventListener("click", () => { const isMusicPlay = iconPlay.style.display !== "none"; isMusicPlay ? playMusic() : pauseMusic(); playingSong(); }); prevBtn.addEventListener("click", () => { prevMusic(); }); nextBtn.addEventListener("click", () => { nextMusic(); }); mainAudio.addEventListener("timeupdate", (e) => { const currentTime = e.target.currentTime; const duration = e.target.duration || 0; let progressWidth = (duration ? (currentTime / duration) * 100 : 0); progressBar.style.width = `${progressWidth}%`; let musicCurrentTime = wrapper.querySelector(".current-time"), musicDuartion = wrapper.querySelector(".max-duration"); mainAudio.addEventListener("loadeddata", () => { let mainAdDuration = mainAudio.duration || 0; let totalMin = Math.floor(mainAdDuration / 60); let totalSec = Math.floor(mainAdDuration % 60); if (totalSec < 10) totalSec = `0${totalSec}`; musicDuartion.innerText = `${totalMin}:${totalSec}`; }); let currentMin = Math.floor(currentTime / 60); let currentSec = Math.floor(currentTime % 60); if (currentSec < 10) currentSec = `0${currentSec}`; musicCurrentTime.innerText = `${currentMin}:${currentSec}`; updateLyrics(currentTime); }); progressArea.addEventListener("click", (e) => { let progressWidth = progressArea.clientWidth; let clickedOffsetX = e.offsetX; let songDuration = mainAudio.duration || 0; mainAudio.currentTime = (clickedOffsetX / progressWidth) * songDuration; playMusic(); playingSong(); }); let repeatState = "repeat"; wrapper.querySelector("#repeat-plist").addEventListener("click", () => { if (repeatState === "repeat") { repeatState = "repeat_one"; iconRepeat.style.display = "none"; iconRepeatOne.style.display = ""; iconShuffle.style.display = "none"; wrapper.querySelector("#repeat-plist").setAttribute("title", "Song looped"); } else if (repeatState === "repeat_one") { repeatState = "shuffle"; iconRepeat.style.display = "none"; iconRepeatOne.style.display = "none"; iconShuffle.style.display = ""; wrapper.querySelector("#repeat-plist").setAttribute("title", "Playback shuffled"); } else if (repeatState === "shuffle") { repeatState = "repeat"; iconRepeat.style.display = ""; iconRepeatOne.style.display = "none"; iconShuffle.style.display = "none"; wrapper.querySelector("#repeat-plist").setAttribute("title", "Playlist looped"); } }); mainAudio.addEventListener("ended", () => { if (repeatState === "repeat") { nextMusic(); } else if (repeatState === "repeat_one") { mainAudio.currentTime = 0; loadMusic(musicIndex); playMusic(); } else if (repeatState === "shuffle") { let randIndex; do { randIndex = Math.floor(Math.random() * allMusic.length); } while (musicIndex === randIndex && allMusic.length > 1); musicIndex = randIndex; loadMusic(musicIndex); playMusic(); playingSong(); } }); moreMusicBtn.addEventListener("click", () => { musicList.classList.toggle("show"); }); closemoreMusic.addEventListener("click", () => { moreMusicBtn.click(); }); const ulTag = wrapper.querySelector("ul"); for (let i = 0; i < allMusic.length; i++) { let liTag = `<li li-index="${i}"> <div class="row"> <span>${allMusic[i].name}</span> <p>${allMusic[i].artist}</p> </div> <span id="${allMusic[i].id}" class="audio-duration">3:40</span> <audio class="${allMusic[i].id}" src="${allMusic[i].src}"></audio> </li>`; ulTag.insertAdjacentHTML("beforeend", liTag); let liAudioDuartionTag = ulTag.querySelector(`#${allMusic[i].id}`); let liAudioTag = ulTag.querySelector(`.${allMusic[i].id}`); liAudioTag.addEventListener("loadeddata", () => { let duration = liAudioTag.duration || 0; let totalMin = Math.floor(duration / 60); let totalSec = Math.floor(duration % 60); if (totalSec < 10) { totalSec = `0${totalSec}`; }; liAudioDuartionTag.innerText = `${totalMin}:${totalSec}`; liAudioDuartionTag.setAttribute("t-duration", `${totalMin}:${totalSec}`); }); } function playingSong() { const allLiTag = ulTag.querySelectorAll("li"); for (let j = 0; j < allLiTag.length; j++) { let audioTag = allLiTag[j].querySelector(".audio-duration"); if (allLiTag[j].classList.contains("playing")) { allLiTag[j].classList.remove("playing"); let adDuration = audioTag.getAttribute("t-duration"); if (adDuration) audioTag.innerText = adDuration; } if (parseInt(allLiTag[j].getAttribute("li-index")) === musicIndex) { allLiTag[j].classList.add("playing"); audioTag.innerText = "Playing"; } allLiTag[j].onclick = function() { clicked(this); }; } } function clicked(element) { let getLiIndex = parseInt(element.getAttribute("li-index")); musicIndex = getLiIndex; loadMusic(musicIndex); playMusic(); playingSong(); } function setVolume() { curr_track.volume = volumeslider.value / 100; } volumeslider.addEventListener('input', setVolume); maxvolum.addEventListener("click", () => { curr_track.volume = 1; volumeslider.value = 100; }); minvolum.addEventListener("click", () => { curr_track.volume = 0.01; volumeslider.value = 1; }); // Sửa lỗi lời nhảy: updateLyrics mượt, không lặp lại active, đồng bộ thời gian, và không nhấp nháy. let lastActiveLyricIdx = -1; function updateLyrics(currentTime, force = false) { const lyricsArr = allMusic[musicIndex].lyrics; if (!lyricsArr || lyricsArr.length === 0) { document.getElementById('lyricsList').innerHTML = `<span class="lyric-line active">...</span>`; lastActiveLyricIdx = -1; return; } // Tìm lyric đang phát let activeIdx = 0; for (let i = 0; i < lyricsArr.length; i++) { if (currentTime >= lyricsArr[i].time) { activeIdx = i; } else { break; } } // Nếu lyric không đổi, không cần làm mới giao diện if (!force && lastActiveLyricIdx === activeIdx) return; lastActiveLyricIdx = activeIdx; // Chuẩn bị hiển thị lyric (1 câu trước, câu hiện tại, 1 câu sau) const prevLyric = lyricsArr[activeIdx - 1]?.text || ''; const currentLyric = lyricsArr[activeIdx]?.text || ''; const nextLyric = lyricsArr[activeIdx + 1]?.text || ''; const lyricsHtml = ` ${prevLyric ? `<span class="lyric-line">${prevLyric}</span>` : ''} <span class="lyric-line active">${currentLyric}</span> ${nextLyric ? `<span class="lyric-line">${nextLyric}</span>` : ''} `; document.getElementById('lyricsList').innerHTML = lyricsHtml; } </script>
Mỗi phần tử trong mảng này là một đối tượng biểu diễn một bài hát với các thuộc tính:
  1. name: Tên bài hát (chuỗi).
  2. artist: Tên nghệ sĩ (chuỗi).
  3. img: URL của ảnh bìa bài hát (chuỗi). Bạn cần có URL trực tiếp đến file ảnh.
  4. src: URL của file nhạc (chuỗi). Bạn cần có URL trực tiếp đến file .mp3 hoặc định dạng âm thanh được trình duyệt hỗ trợ.
  5. id: Một ID duy nhất cho bài hát (chuỗi), dùng để tham chiếu trong danh sách nhạc.
  6. lyrics: (Tùy chọn) Một mảng các đối tượng lời bài hát. Mỗi đối tượng có hai thuộc tính:
  7. time: Thời gian hiển thị lời bài hát (có thể là chuỗi "phút:giây" hoặc số giây).
  8. text: Dòng lời bài hát tương ứng (chuỗi).

Bạn có thể thêm, sửa, hoặc xóa các đối tượng bài hát trong mảng allMusic này để tạo danh sách nhạc của riêng mình.

Ví dụ:
{js} let allMusic = [ { name: "Tên bài hát của bạn", artist: "Tên nghệ sĩ", img: "URL_den_anh_bia.jpg", src: "URL_den_file_nhac.mp3", id: "baihatmoi", lyrics: [ { time: "00:05", text: "Dòng lời bài hát thứ nhất" }, { time: "00:10", text: "Dòng lời bài hát thứ hai" }, // Thêm các dòng khác... ] }, // Thêm các bài hát khác vào đây ];
Lưu ý quan trọng:
Đảm bảo các URL ảnh bìa (img) và file nhạc (src) là các liên kết trực tiếp và công khai. Bạn có thể tải file nhạc và ảnh lên các dịch vụ lưu trữ đám mây công cộng hoặc sử dụng các dịch vụ lưu trữ file tĩnh.
Khi thêm lời bài hát, hãy cố gắng căn chỉnh thời gian (time) chính xác với thời điểm dòng lời đó được hát trong bài nhạc để tính năng hiển thị lời bài hát hoạt động tốt nhất.

Lưu và kiểm tra

Sau khi đã dán cả ba phần HTML, CSS, và JavaScript vào template blog của mình, hãy lưu lại các thay đổi và tải lại trang blog để xem trình phát nhạc có hoạt động như mong đợi không.
Nếu có bất kỳ vấn đề gì, hãy kiểm tra lại các bước đã thực hiện, đảm bảo mã được dán đúng vị trí và không có lỗi cú pháp nào. 

Chúc bạn thành công và có một trình phát nhạc độc đáo trên blog của mình!
0/5
0 ratings
5
4
3
2
1
=