集成快捷键的多曲音频播放器
<h3>效果</h3><div id="showRes" style="margin: 30px; text-align: center;"></div>
<h3>代码</h3>
<div id="hEdiv"><pre id="hEpre">
<style>
#mboard { margin-bottom: 170px; width: 500px; height: 40px; background: beige; border-radius: 8px; box-shadow: 2px 2px 4px rgba(0,0,0,.5); display: flex; justify-content: center; align-items: center; gap: 8px; position: relative; }
#mboard:hover #mlist { opacity: 1; }
#mboard img { width: 26px; cursor: pointer; }
#mboard img:hover { filter: drop-shadow(1px 1px 1px rgba(0,0,0,.5)); }
#tMsg1, #tMsg2 { width: 45px; font-size: 13px; text-align: center; }
#volwrap { position: absolute; width: 60px; height: 40px; right: 75px; display: grid; place-items: center; background: none; }
#volwrap:hover #volume { display: inline; }
#btnMute:hover ~ #volwrap > #volume { display: inline; }
#volume { position: absolute; width: 50px; height: 4px; opacity: .75; display: none; }
#prog { --track: gray; --prog: red; --prg: 0%; width: 200px; height: 20px; cursor: pointer; background: linear-gradient(to right, var(--prog) var(--prg), var(--track) 0) no-repeat 0% 50%/100% 2px; }
#mlist { position: absolute; top: 45px; width: 100%; height: 160px; padding: 12px; border-radius: 0px; overflow: auto; scrollbar-width: thin; background: inherit; column-count: 2; column-rule: 2px solid #eee; column-fill: auto; box-sizing: border-box; border: 1px solid gray; opacity: 0; transition: .7s; }
.list { margin: 0 8px; padding: 0 0 0 30px; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; line-height: 20px; text-align: left; position: relative; }
.list::before { position: absolute; content: attr(data-idx); width: 30px; left: 0; }
.list1 { color: black; cursor: pointer; }
.list2 { color: red; cursor: default; }
.list1:hover { color: red; }
</style>
<div id="mboard">
<div id="mlist"></div>
<img id="btnPrev" src="https://638183.freep.cn/638183/web/icon/prev.svg" title="前一曲 (Alt+↑)" alt="" />
<img id="btnPlay" src="https://638183.freep.cn/638183/web/icon/play.svg" title="播放/暂停 (Alt+X)" alt="" />
<img id="btnNext" src="https://638183.freep.cn/638183/web/icon/next.svg" title="下一曲 (Alt+↓)" alt="" />
<span id="tMsg1">00:00</span>
<span id="prog"></span>
<span id="tMsg2">00:00</span>
<img id="btnMute" src="https://638183.freep.cn/638183/web/icon/unmuted.svg" title="静音 (Alt+J)" alt="" />
<img id="btnCirc" src="https://638183.freep.cn/638183/web/icon/circs.svg" title="随机循环 (Alt+S)" alt="" />
<div id="volwrap"><input id="volume" type="range" min="0" max="1" step="0.1" value="1" /></div>
<audio id="aud"></audio>
</div>
<script>
let lastVolume = 1, currentId = null, circ = true, muted = false, playAr = [], lists = [];
const musics = [
['https://music.163.com/song/media/outer/url?id=1321297488','跟太阳系说再见'],
['https://music.163.com/song/media/outer/url?id=1380590400','优雅的邂逅-致丁仪'],
['https://music.163.com/song/media/outer/url?id=1406003054','逝去的时间(time is over)'],
['https://music.163.com/song/media/outer/url?id=1421093308','人列计算机'],
['https://music.163.com/song/media/outer/url?id=1482053654','地球反抗军'],
['https://music.163.com/song/media/outer/url?id=1488362358','黑暗战役A'],
['https://music.163.com/song/media/outer/url?id=1488360994','黑暗战役B'],
['https://music.163.com/song/media/outer/url?id=1322066478','歌行者'],
['https://music.163.com/song/media/outer/url?id=524406882','地下世界-Without Spiccato'],
['https://music.163.com/song/media/outer/url?id=534254637','水滴-摧毁舰队+metal drums'],
['https://music.163.com/song/media/outer/url?id=1384983167','为了生存-末日方舟开场曲'],
['https://music.163.com/song/media/outer/url?id=1331220129','四维空间深不见底'],
['https://music.163.com/song/media/outer/url?id=1363443031','章北海登上自然选择号'],
['https://music.163.com/song/media/outer/url?id=2015042301','人生的旅途'],
['https://music.163.com/song/media/outer/url?id=520502976','面壁者的沉思+CL'],
];
const btnImg = {
play: 'https://638183.freep.cn/638183/web/icon/play.svg',
pause: 'https://638183.freep.cn/638183/web/icon/pause.svg',
unmute: 'https://638183.freep.cn/638183/web/icon/unmuted.svg',
mute: 'https://638183.freep.cn/638183/web/icon/muted.svg',
prev: 'https://638183.freep.cn/638183/web/icon/prev.svg',
next: 'https://638183.freep.cn/638183/web/icon/next.svg',
circs: 'https://638183.freep.cn/638183/web/icon/circs.svg',
circ1: 'https://638183.freep.cn/638183/web/icon/circ1.svg',
};
const setVolume = (val) => Math.min(1, Math.max(0, val));
const scrollX = (elm, x) => {
if (elm.scrollWidth < elm.offsetWidth) return;
elm.scroll({left: x, top: 0, hehavior: 'smooth'});
};
const ranAr = (total) => {
let ar = Array(total).fill().map((_,key) => key);
ar.sort(() => 0.5 - Math.random());
return ar;
};
playAr = ranAr(musics.length);
musics.forEach((item,key) => {
let p = document.createElement('p');
p.className = 'list list1';
p.dataset.idx = key + 1;
p.innerText = p.title = item;
p.onclick = () => mplay(key);
mlist.appendChild(p);
lists.push(p);
});
const mplay = (idx = null) => {
if(musics.length === 0 || (idx !== null && idx === currentId)) return;
if(idx === null) {
if(playAr.length === 0) playAr = ranAr(musics.length);
let tmpIdx = Math.floor(Math.random() * playAr.length);
idx = playAr;
playAr.splice(tmpIdx, 1);
}
aud.src = musics;
if(idx !== null && idx !== currentId) aud.play();
lists.className = 'list list2';
if(currentId !== null) lists.className = 'list list1';
currentId = idx;
scrollX(mlist, lists.offsetLeft - 22);
};
const setMute = () => {
if(lastVolume === 0) return;
muted = !muted;
muted ? aud.volume = 0 : aud.volume = lastVolume;
};
const s2m = (seconds) => {
const secs = Math.floor(seconds || 0);
return `${String(secs/60|0).padStart(2,'0')}:${String(secs%60).padStart(2,'0')}`;
};
const mState = () => {
btnPlay.src = aud.paused ? btnImg.play : btnImg.pause;
btnPlay.title = (aud.paused ? '播放' : '暂停') + ' (Alt+X)';
};
document.addEventListener('keydown', e => {
if(!e.altKey) return;
switch (e.keyCode) {
case 38:
btnPrev.click();
break;
case 40:
btnNext.click();
break;
case 74:
setMute();
break;
case 83:
btnCirc.click();
break;
case 88:
btnPlay.click();
break;
case 187: case 107:
aud.volume = setVolume(aud.volume + 0.1);
lastVolume = aud.volume;
break;
case 189: case 109:
aud.volume = setVolume(aud.volume - 0.1);
lastVolume = aud.volume;
break;
default:
return;
}
});
aud.onplaying = aud.onpause = () => mState();
aud.onended = () => { circ ? mplay() : aud.play(); };
aud.ontimeupdate = () => {
prog.style.setProperty('--prg', aud.currentTime/aud.duration*100 +'%');
tMsg1.innerText = s2m(aud.currentTime);
tMsg2.innerText = s2m(aud.duration);
};
aud.onvolumechange = () => {
btnMute.src = aud.volume === 0 ? btnImg.mute : btnImg.unmute;
volume.value = aud.volume;
}
btnPlay.onclick = () => aud.paused ? aud.play() : aud.pause();
btnPrev.onclick = () => { if(currentId > 0) mplay(currentId - 1); };
btnNext.onclick = () => { if(currentId < musics.length - 1) mplay(currentId + 1); };
btnMute.onclick = () => setMute();
btnCirc.onclick = () => {
circ = !circ;
btnCirc.src = circ ? btnImg.circs : btnImg.circ1;
btnCirc.title = (circ ? '随机循环' : '单曲循环') + ' (Alt+S)';
};
volume.onchange = () => aud.volume = lastVolume = volume.value;
prog.onclick = (e) => aud.currentTime = e.offsetX * aud.duration / prog.offsetWidth;
prog.onmousemove = (e) => prog.title = s2m(e.offsetX * aud.duration / prog.offsetWidth);
volwrap.onmousemove = () => volwrap.title = '音量 : ' + volume.value + ' (Alt++/-)';
mboard.onmouseenter = () => scrollX(mlist, lists.offsetLeft - 22);
mlist.onwheel = (e) => {
const delta = Math.abs(e.deltaX) > 0 ? e.deltaX : e.deltaY;
mlist.scrollLeft += delta * 1.5;
e.preventDefault();
};
mplay();
</script>
</pre></div>
<script type="module">
import hlight from 'https://638183.freep.cn/638183/web/mod/helight.js';
hlight.hl(hEdiv, hEpre);
var runCodes = (str,target) => {
let reg = /(<script(.*?)>)(.|\n)*?(<\/script>)/g;
let js_str, html_str;
if(str.match(reg) !== null) {
js_str = str.match(reg);
html_str = str.replace(js_str, '').trim();
js_str = js_str.replace(/<[\/]{0,1}script[^>]*>/g,'').trim();
} else {
js_str = '';
html_str = str.trim();
}
target.innerHTML = html_str;
let myfunc = new Function(js_str);
myfunc();
};
runCodes(hEpre.innerText, showRes);
</script>
歌曲曲目列表支持鼠标滑轮翻页 谢谢老师分享{:5_116:}{:5_116:}{:5_116:} 谢谢老师辛苦!{:4_190:} 梦江南 发表于 2025-2-23 10:21
谢谢老师辛苦!
{:4_191:} 近朱者赤 发表于 2025-2-23 10:19
谢谢老师分享
{:4_191:} 昨天完成的大工程,超强制作~~
多曲比想象中的更复杂,不单是多加几首歌那么简单。。
还增加了前一曲,后一曲,循环播放~~ 马黑黑 发表于 2025-2-23 10:16
歌曲曲目列表支持鼠标滑轮翻页
试了滚轮翻页,相当流畅,应响极快。。 这个更接近于平时用的播放器的效果。。
想到之前的出的多曲播放本地音乐,天天用的那个。。
键盘控制与否差别,还有曲目本地和网络的差别。。。哈哈,好象差别很大。。
花飞飞 发表于 2025-2-23 12:51
试了滚轮翻页,相当流畅,应响极快。。
这组滚轮代码参考了DeepSeek给的建议 花飞飞 发表于 2025-2-23 12:50
昨天完成的大工程,超强制作~~
多曲比想象中的更复杂,不单是多加几首歌那么简单。。
还增加了前一曲,后 ...
以及歌曲列表的设计和实现 这个可以做专辑贴。。占地更小一点。。功能更强大控制更方便一些。{:4_173:} 花飞飞 发表于 2025-2-23 12:55
这个更接近于平时用的播放器的效果。。
想到之前的出的多曲播放本地音乐,天天用的那个。。
键盘控制与否 ...
彼此间的设计和实现没有互相参考 马黑黑 发表于 2025-2-23 12:55
这组滚轮代码参考了DeepSeek给的建议
DS马上用到实例中了,真是造福代码人 花飞飞 发表于 2025-2-23 12:57
DS马上用到实例中了,真是造福代码人
它给的代码基本也不能照搬,参考是可以的 花飞飞 发表于 2025-2-23 12:57
这个可以做专辑贴。。占地更小一点。。功能更强大控制更方便一些。
歌曲列表的位置需要考量 马黑黑 发表于 2025-2-23 12:56
以及歌曲列表的设计和实现
{:4_173:}嗯哪,多曲需要曲目列表。。感觉工程量翻倍了 花飞飞 发表于 2025-2-23 12:59
嗯哪,多曲需要曲目列表。。感觉工程量翻倍了
代码量也翻番了 马黑黑 发表于 2025-2-23 12:57
彼此间的设计和实现没有互相参考
我瞅了好多眼,完全没有重叠 的地方。。
就是一个管本地多曲,一个管网络多曲。。全乎了 马黑黑 发表于 2025-2-23 12:58
它给的代码基本也不能照搬,参考是可以的
嗯嗯,它给的都是个大轮廓,要修修剪剪的。。。除了诗歌,基本都要改改{:4_173:}