·悄然 - 2024-12-17 15:19
·悄然 - 2024-12-15 15:12
·悄然 - 2024-12-9 12:32
·飞飞 - 2024-12-9 12:31
·小希 - 2024-12-7 11:50
·飞飞 - 2024-12-5 15:53
·飞飞 - 2024-12-3 16:42
·悄然 - 2024-12-3 16:41
·飞飞 - 2024-12-1 18:27
保姆级range进度条音频播放器开发教程(五)
通过前面的四个讲座,range进度条播放器已经实现音乐可控、能显示播放进度、可调节播放位置。现在,还有三个细节需要处理:其一,进度条显示进度的运行状态是走一步停一步的样纸,这个要完善一下;其二,音频时长与当前播放位置时间信息要加入;其三,按钮的鼠标指针悬停提示语还不够智能,都是“暂停/播放”,我们要让它聪明起来。
先完善进度条的运行。我们可以去观察主流web音频播放器,滑块的进度指示基本都是走一步停一步,只是走停之间的间隔没有我们的大。走一步停一步避免不了,这是 audio 控件在播放时返回 currentTime 的机制所导致,我们要做的是让range走走停停的间隔尽可能短一些,换句话说让它用三寸金莲迈碎步且频率要加快。当然,我们加快range的迈步频率要尊重 audio 控件播放音频的工作机制,与它同步就好。range 有一个 step 属性,就是步幅,默认和缺省值为 1,我们修改这个值就好。修改为浮点数可以不?可以,第四讲里我就已经悄悄地改为了0.1,不过有一个更合适的值,any,any是任意的意思,这里指任意值。任意值就是依赖 audio 控件的播放机制而定,它以秒级时间单位步进,它怎么步进range的三寸金莲就会怎么跟进,这就接近平滑移动滑杆滑块了,虽然不可能是真正的平滑。这个容易,只需改装一下 input type="range" 标签的写法:
<input id="mprog" type="range" min="0" max="100" step="any" value="0" title="调节进度" />
再解决第二个问题:在range播放器界面加入audio相关的时间信息。我们可以借助播放器的容器元素即 id="mplayer" 的标签的伪元素来显示时间信息,这样的话,mplayer 容器也需要简单改装一下:
<div id="mplayer" data-tt="0:00 0:00"> <!-- 这里是播放器子元素代码 --> </div>
红色部分的代码,data-tt 的值将赋予伪元素,伪元素一旦成功创建且接口指向 data-tt,它将能显示 0:00 0:00 的 data-tt 值。data-名称 是HTML制定好的内置名称可变的属性,data 是关键词前缀,短连接线之后跟一个自命名字串,如此,伪元素通过 content 属性接收 data-名称 的值,JS通过 元素.dataset.名称 来动态读写 data-名称 的值。先在 CSS 代码中创建 #mplayer 选择器的 ::before 伪元素:
#mplayer::before { position: absolute; content: attr(data-tt); /* attr(data-tt) 表示伪元素将显示由html赋予的 data-tt 值 */ left: 0; bottom: 25px; /* 文本位置 */ width: 100%; /* 宽度与容器一致 */ text-align-last: justify; /* 文本最后一行两端对齐 :我们的文本就一行、用空格分成两个单位,空格是两端对齐的分界 */ }
还可以加入字体及文本颜色等设置,尺寸、定位等都可以调整,一切根据需要来。至此,mplayer 容器元素已经具备显示播放器时间信息的能力,下一步交给JS处理:以 分:秒 的形式显示时间信息。audio 的 currentTime 和 duration 返回的时间都是以秒为时间单位的浮点数,小数点后面的数字很长,我们需要换算为 0:00 这样的分秒形式。为此,需要编写一个秒数转为分秒的函数:
var toMin = (val) => { if(!val) return '0:00'; var min = parseInt(val / 60), sec = Math.floor(val) % 60; if(sec < 10) sec = '0' + sec; return min + ':' + sec; };
toMin 是函数名称,需要一个待传参数 val,花括号 {} 内的四行行代码是函数要做的事情,即处理 秒数 → 分:秒 事宜。第一行,如果传来的数值不合法,则直接返回字符串 0:00(不再往下执行余下的语句),return 是JS内置的返回方法。第二行,声明两个变量,min(分)和 sec(秒),JS支持一次声明多个变量,各变量间用逗号隔开;min 和 sec 变量在声明的时候给它们分别赋值,val 传来的是秒数,秒数除以60得到分钟,我们用JS的 parseInt(数值) 强制将运算结果变为整数,这会得到准确的分钟数(小于 1 的浮点数得 0,大于 1 且小于 2 的浮点数得 1,依此类推),秒数则先对 val 值进行向下取整【Math.floor(val)】,接着将得到的结果取 60 的余数【Math.floor(val) % 60】即可拿到所需秒数,小于10的秒数需要前面补零,第三行就是处理面补零操作,这一部分是不足一分钟的秒数。最后一行,返回 分:秒 结构的字符串。函数一时半会不能透彻理解关系不大,有空再慢慢消化,我们现在要掌握的是这个单一功能函数的调用方法:toMin(秒数); ,就这么简单。当然,我们还需要把函数应用在 audio 控件 的 timeupdate 监听事件中,把 toMin(秒数) 的结果实时告知 mplayer —— mplayer 有一个 data--tt 用来显示播放器音频相关的时间信息:
aud.addEventListener('timeupdate', () => { if(!mseek) mprog.value = aud.currentTime / aud.duration * mprog.max; /* 显示进度 */ mplayer.dataset.tt = toMin(aud.currentTime) + ' ' + toMin(aud.duration); /* 显示时间信息 */ });
toMin() 函数使用了两次,一次转换当前播放位置的时间信息,另一次是音频总时长的转换。我们将转换结果交给 mplayer 的 dataset.tt 属性,然后 mplayer 容器根据伪元素的设定将获得的 data 值进行两端对齐显示出来。
第三个待解决问题:鼠标指针在播放器上悬停时的提示语。这个相对简单,修改 mState 函数即可:
/* 原来的语句 */ var mState = () => btnplay.style.setProperty('--state', aud.paused ? 'paused' : 'running'); /* 改为如下语句 */ var mState = () => aud.paused ? ( btnplay.style.setProperty('--state', 'paused'), btnplay.title = '点击播放' ) : ( btnplay.style.setProperty('--state', 'running'), btnplay.title = '点击暂停' );
最后重新整理完整播放器代码如下(红色部分是本节新增或修改过的,同时还对CSS和html做了相应改动):
<style> #mplayer { position: absolute; text-align: center; } #mplayer::before { position: absolute; content: attr(data-tt); left: 0; bottom: 25px; width: 100%; text-align-last: justify; } #mprog { width: 240px; accent-color: darkgreen; outline: none; cursor: pointer; } #btnplay { width: 80px; height: 80px; cursor: pointer; animation: rotating 6s infinite linear var(--state); } @keyframes rotating { to { transform: rotate(360deg); } } </style> <audio id="aud" src="https://music.163.com/song/media/outer/url?id=1201604" autoplay loop></audio> <div id="mplayer" data-tt="0:00 0:00"> <img id="btnplay" src="https://638183.freep.cn/638183/small/002_133507167677724892.png" title="播放/暂停" alt="" /><br> <input id="mprog" type="range" min="0" max="100" step="any" value="0" title="调节进度" /> </div> <script> var mseek = false; var mState = () => aud.paused ? ( btnplay.style.setProperty('--state', 'paused'), btnplay.title = '点击播放' ) : ( btnplay.style.setProperty('--state', 'running'), btnplay.title = '点击暂停' ); var toMin = (val) => { if(!val) return '0:00'; var min = parseInt(val / 60), sec = Math.floor(val) % 60; if(sec < 10) sec = '0' + sec; return min + ':' + sec; }; aud.addEventListener('timeupdate', () => { if (!mseek) mprog.value = aud.currentTime / aud.duration * mprog.max; mplayer.dataset.tt = toMin(aud.currentTime) + ' ' + toMin(aud.duration); }); aud.addEventListener('pause', () => mState()); aud.addEventListener('playing', () => mState()); mprog.onmousedown = () => mseek = true; mprog.onmouseup = () => mseek = false; mprog.onchange = () => aud.currentTime = aud.currentTime = mprog.value / mprog.max * aud.duration; btnplay.onclick = () => aud.paused ? aud.play() : aud.pause(); </script>
到 pencil code 运行上述代码或将代码存为本地html文档后运行,我们会发现现在播放器看起来相当令人满意,如果还有下一步就是扩展性的功能了——lrc歌词同步。敬请期待!
评论列表 [3条]
#3 | 知名不具 于 2024-1-31 20:05 发布: 非常详尽。辛苦了。
#2 | 悄然 于 2024-1-30 20:39 发布: 老师真是超级大能者~~保姆级教程教这么复杂的东东,无基础小白居然也能看个七七八八。。乐一个。。
#1 | 飞飞 于 2024-1-30 08:58 发布: 豪华版教程……继续期待中……