歌词同步+进度可调audio播放器帖子模板(2022.6.11)
<style>/*帖子外层盒子*/
.mama { position: relative; margin: auto; width: 1000px; height: 600px; background: transparent linear-gradient(to right bottom, darkgreen, snow); box-shadow: 2px 2px 2px #444; }
/*播放器外层盒子*/
.lrcWrap { position: absolute; top: 10px; left: 10px; padding: 20px; width: fit-content; height: fit-content; text-align: center; background: transparent linear-gradient(rgba(255,255,255,.25), rgba(255,255,255,.15)); box-shadow: 2px 2px 4px #eee; display: flex; flex-direction: column;align-items: center; }
/*播放控制外层盒子*/
.meterWrap { position: relative; display: flex; align-items: center; width: fit-content; height: 50px; }
/*播放按钮*/
.playbtn { width: 10px; height: 20px; background: #eee; clip-path: polygon(0 0, 0% 100%, 100% 50%); cursor: pointer; }
/*播放按钮鼠标滑过*/
.playbtn:hover { background: red; }
/*暂停按钮*/
.pausebtn { width: 2px; height: 20px; border-style: solid; border-width: 0px 4px; border-color: transparent #eee; display: none; cursor: pointer; }
/*暂停按钮鼠标滑过*/
.pausebtn:hover { border-color: transparent red; }
/*进度条*/
.meter { position: relative; width:300px; height: 11px; cursor: pointer; background: linear-gradient(transparent 5px, snow 6px,transparent 0); }
/*进度滑块*/
.slider { display: block; position: absolute; width: 4px; height: 100%; background: white; }
/*歌词面板外层盒子*/
.lrcWrap p { margin: 0 0 12px 0; padding: 0px; color: #ccc; font: normal 1.2em sans-serif; text-shadow: 1px 1px 1px #333; }
/*歌词区域限制层*/
.lrcBox { margin: 0; padding: 0; width: 400px; height: 72px; overflow: hidden; user-select: none; position: relative; }
/*歌词ul标签*/
.lrcUl { position: relative; top: 0; margin: 0; padding: 0; }
/*歌词li标签*/
.lrcUl li { margin: 0; padding: 0; height: 24px; font: normal 18px / 24px sans-serif; color: gray; text-shadow: 1px 1px 1px black; list-style-type: none; }
</style>
<div class="mama">
<!-- 播放器开始 -->
<div class="lrcWrap">
<p>帖子标题</p>
<div class="lrcBox"><ul class="lrcUl"></ul></div>
<div class="meterWrap">
<div class="playbtn"></div>
<div class="pausebtn"></div>
<div class="meter"><span class="slider"></span></div>
</div>
<!-- 播放器结束 -->
</div>
</div>
<audio class="aud" src="音频地址" autoplay="autoplay" loop="loop"></audio>
<script>
//N多盒子句柄
let aud = document.querySelector('.aud'),
playbtn = document.querySelector('.playbtn'),
pausebtn = document.querySelector('.pausebtn'),
meter = document.querySelector('.meter'),
slider = document.querySelector('.slider'),
lrcUl = document.querySelector('.lrcUl');
let slip = 0; //误差修正
//lrc歌词数组
let lrcAr = [
['0.00','第一句'],
['3.84','第二句'],
//...第N句
['331.05','最后一句']
];
//将歌词写入li标签
for(j=0; j<lrcAr.length; j++){
lrcUl.innerHTML += '<li id="li' + lrcAr + '" style="list-style-type: none">' + lrcAr + '</li>';
}
//监听进度
aud.addEventListener('timeupdate', () => {
let prog = (meter.clientWidth - slider.clientWidth) * aud.currentTime / aud.duration;
slider.style.transform = 'translate(' + prog + 'px)';
let tt = aud.currentTime;
for(j=0; j<lrcAr.length; j++){
if(tt >= lrcAr - slip){
if(j > 0){
let idxLast = lrcAr;
document.getElementById('li' + idxLast).style.color = 'gray';
lrcUl.style.top = '-' + (j * 24 - 24) + 'px';
}
let idx = lrcAr;
document.getElementById('li' + idx).style.color = 'ghostwhite';
}
}
})
//监听结束事件
aud.addEventListener('ended', () => {
document.getElementById("li" + lrcAr).style.color = 'gray';
lrcUl.style.top = 0;
})
//监听暂停与播放
aud.addEventListener('pause', () => btnstate(1));
aud.addEventListener('play',() => btnstate(0));
//进度条点击事件
meter.onclick = (e) => {
e = e || event;
aud.currentTime = (e.clientX - offset(meter,"left")) * aud.duration / meter.clientWidth;
}
//暂停与播放按钮点击事件
pausebtn.onclick = () => { aud.pause(); btnstate(1); }
playbtn.onclick = () => { aud.play(); btnstate(0); }
//获取进度条偏移总量
let offset = (obj,direction) => {
let offsetDir = "offset" + direction.toUpperCase() + direction.substring(1);
let realNum = obj;
let positionParent = obj.offsetParent;
while(positionParent != null){
realNum += positionParent;
positionParent = positionParent.offsetParent;
}
return realNum;
}
//按钮状态
let btnstate = (paused) => {
paused == 1 ? (playbtn.style.display = 'block', pausebtn.style.display = 'none') : (playbtn.style.display = 'none', pausebtn.style.display = 'block');
}
//初始化按钮状态
aud.paused ? btnstate(1) : btnstate(0);
</script>
<style>
.mama { position: relative; left: -202px; width: 1000px; height: 600px; background: transparent linear-gradient(to right bottom, darkgreen, snow); box-shadow: 2px 2px 2px #444; }
.lrcWrap { position: absolute; top: 10px; left: 10px; padding: 20px; width: fit-content; height: fit-content; text-align: center; background: transparent linear-gradient(rgba(255,255,255,.25), rgba(255,255,255,.15)); box-shadow: 2px 2px 4px #eee; display: flex; flex-direction: column;align-items: center; }
.meterWrap { position: relative; display: flex; align-items: center; width: fit-content; height: 50px; }
.playbtn { width: 10px; height: 20px; background: #eee; clip-path: polygon(0 0, 0% 100%, 100% 50%); cursor: pointer; }
.playbtn:hover { background: red; }
.pausebtn { width: 2px; height: 20px; border-style: solid; border-width: 0px 4px; border-color: transparent #eee; display: none; cursor: pointer; }
.pausebtn:hover { border-color: transparent red; }
.meter { position: relative; width:300px; height: 11px; cursor: pointer; background: linear-gradient(transparent 5px, snow 6px,transparent 0); }
.slider { display: block; position: absolute; width: 4px; height: 100%; background: white; }
.lrcWrap p { margin: 0 0 12px 0; padding: 0px; color: #ccc; font: normal 1.2em sans-serif; text-shadow: 1px 1px 1px #333; }
.lrcBox { margin: 0; padding: 0; width: 400px; height: 72px; overflow: hidden; user-select: none; position: relative; }
.lrcUl { position: relative; top: 0; margin: 0; padding: 0; }
.lrcUl li { margin: 0; padding: 0; height: 24px; font: normal 18px / 24px sans-serif; color: gray; text-shadow: 1px 1px 1px black; list-style-type: none; }
</style>
<div class="mama">
<div class="lrcWrap">
<p>帖子标题</p>
<div class="lrcBox"><ul class="lrcUl"></ul></div>
<div class="meterWrap">
<div class="playbtn"></div>
<div class="pausebtn"></div>
<div class="meter"><span class="slider"></span></div>
</div>
</div>
</div>
<audio class="aud" src="https://music.163.com/song/media/outer/url?id=1432843317.mp3" autoplay="autoplay" loop="loop"></audio>
<script>
let aud = document.querySelector('.aud'),
playbtn = document.querySelector('.playbtn'),
pausebtn = document.querySelector('.pausebtn'),
meter = document.querySelector('.meter'),
slider = document.querySelector('.slider'),
lrcUl = document.querySelector('.lrcUl');
let slip = 0;
let lrcAr = [
['0.00','月牙泉 - 原唱 田震'],
['3.84','月牙泉童声版 - 翻唱 史筱璇'],
['6.79','词:杨海潮'],
['10.28','曲:杨海潮'],
['13.82','就在天的那边 很远很远'],
['18.01','有美丽的月牙泉'],
['22.84','它是天的镜子 沙漠的眼'],
['27.43','星星沐浴的乐园'],
['32.04','从那年我月牙泉边走过'],
['37.06','从此以后魂儿绕梦牵'],
['41.83','也许你们不懂得这种爱恋'],
['46.39','除非也去那里看看'],
['50.55','看那 看那 月牙泉'],
['59.72','想那 念那 月牙泉'],
['78.90','看那 看那 月牙泉'],
['88.19','想那 念那 月牙泉'],
['98.10','看那 看那 月牙泉'],
['107.10','想那 念那 月牙泉'],
['117.15','就在天的那边 很远很远'],
['121.70','有美丽的月牙泉'],
['126.40','它是天的镜子 沙漠的眼'],
['131.05','星星沐浴的乐园']
];
for(j=0; j<lrcAr.length; j++){
lrcUl.innerHTML += '<li id="li' + lrcAr + '" style="list-style-type: none">' + lrcAr + '</li>';
}
aud.addEventListener('timeupdate', () => {
let prog = (meter.clientWidth - slider.clientWidth) * aud.currentTime / aud.duration;
slider.style.transform = 'translate(' + prog + 'px)';
let tt = aud.currentTime;
for(j=0; j<lrcAr.length; j++){
if(tt >= lrcAr - slip){
if(j > 0){
let idxLast = lrcAr;
document.getElementById('li' + idxLast).style.color = 'gray';
lrcUl.style.top = '-' + (j * 24 - 24) + 'px';
}
let idx = lrcAr;
document.getElementById('li' + idx).style.color = 'ghostwhite';
}
}
})
aud.addEventListener('ended', () => {
document.getElementById("li" + lrcAr).style.color = 'gray';
lrcUl.style.top = 0;
})
aud.addEventListener('pause', () => btnstate(1));
aud.addEventListener('play',() => btnstate(0));
meter.onclick = (e) => {
e = e || event;
aud.currentTime = (e.clientX - offset(meter,"left")) * aud.duration / meter.clientWidth;
}
pausebtn.onclick = () => { aud.pause(); btnstate(1); }
playbtn.onclick = () => { aud.play(); btnstate(0); }
let offset = (obj,direction) => {
let offsetDir = "offset" + direction.toUpperCase() + direction.substring(1);
let realNum = obj;
let positionParent = obj.offsetParent;
while(positionParent != null){
realNum += positionParent;
positionParent = positionParent.offsetParent;
}
return realNum;
}
let btnstate = (paused) => {
paused == 1 ? (playbtn.style.display = 'block', pausebtn.style.display = 'none') : (playbtn.style.display = 'none', pausebtn.style.display = 'block');
}
aud.paused ? btnstate(1) : btnstate(0);
</script>
主要更新:
一、重新绘制了播放与暂停按钮;
二、修复、调整JS部分功能实现机制;
三、加入简单注释 加了这么多注释,这个更清楚了。黑黑真棒{:4_187:} 红影 发表于 2022-6-12 08:06
加了这么多注释,这个更清楚了。黑黑真棒
还是简单的注释呢,细节的都没加 播放与暂停按钮是绘制的,比前面直接用符号漂亮多了{:4_199:} 红影 发表于 2022-6-12 08:11
播放与暂停按钮是绘制的,比前面直接用符号漂亮多了
最主要是,他不会受到客户端字体的影响 谢谢老黑。我来学习了。{:4_190:} 加林森 发表于 2022-6-12 10:56
谢谢老黑。我来学习了。
{:5_108:} 马黑黑 发表于 2022-6-12 08:10
还是简单的注释呢,细节的都没加
已经非常清楚了,黑黑很细心{:4_187:} 马黑黑 发表于 2022-6-12 08:23
最主要是,他不会受到客户端字体的影响
对,这样不会受字体限制,这个之前倒没想到。 红影 发表于 2022-6-12 11:35
已经非常清楚了,黑黑很细心
本来应该有些注释的习惯,个人觉得好简单的东东就不怎么写 红影 发表于 2022-6-12 11:36
对,这样不会受字体限制,这个之前倒没想到。
不同的客户端字体不一样,还有,有些人会强制字体很大(这样按钮样式就会被破坏) 马黑黑 发表于 2022-6-12 11:36
本来应该有些注释的习惯,个人觉得好简单的东东就不怎么写
这样注释了更清晰,非常好。用最新的这个做了个帖子,本来想把那个频谱变小放旁边,发现不行呢,也许JS都要调用监听,冲突了吧,反正我弄不进去。实在不行弄个动图吧{:4_173:} 马黑黑 发表于 2022-6-12 11:38
不同的客户端字体不一样,还有,有些人会强制字体很大(这样按钮样式就会被破坏)
哦 哦,还是现在的好,可以回避这些问题了。{:4_187:} 红影 发表于 2022-6-12 14:50
哦 哦,还是现在的好,可以回避这些问题了。
感觉好了一些吧 红影 发表于 2022-6-12 14:49
这样注释了更清晰,非常好。用最新的这个做了个帖子,本来想把那个频谱变小放旁边,发现不行呢,也许JS都 ...
频谱本身就是一个播放器,不能简单叠加,但可以将频谱的内容加以处理,然后将实现它的相关代码加入到 timeupdate 监听事件里 马黑黑 发表于 2022-6-12 11:34
{:5_108:} 谢谢黑黑,这个是新的模板吧,收藏一下了,{:4_187:} 小辣椒 发表于 2022-6-12 18:05
谢谢黑黑,这个是新的模板吧,收藏一下了,
这是最新更新的,也基本定型了吧