保姆级range进度条音频播放器开发教程(五)
本帖最后由 马黑黑 于 2024-1-30 08:30 编辑 <br /><br /><style>.mum { font-size: 18px; }
.mum pre { padding: 12px; background: #eee; color: blue; font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; tab-size: 4; white-space: pre-wrap; word-wrap: break-word; }
.mum a { color: darkred; }
.mum a:hover { color: red; }
.mum mark { padding: 0 4px; background: lightblue; }
.tGreen { color: green; }
.tRed { color: red; }
</style>
<div class="mum">
<p>通过前面的四个讲座,range进度条播放器已经实现音乐可控、能显示播放进度、可调节播放位置。现在,还有三个细节需要处理:其一,进度条显示进度的运行状态是走一步停一步的样纸,这个要完善一下;其二,音频时长与当前播放位置时间信息要加入;其三,按钮的鼠标指针悬停提示语还不够智能,都是“暂停/播放”,我们要让它聪明起来。</p>
<p>先完善进度条的运行。我们可以去观察主流web音频播放器,滑块的进度指示基本都是走一步停一步,只是走停之间的间隔没有我们的大。走一步停一步避免不了,这是 audio 控件在播放时返回 currentTime 的机制所导致,我们要做的是让range走走停停的间隔尽可能短一些,换句话说让它用三寸金莲迈碎步且频率要加快。当然,我们加快range的迈步频率要尊重 audio 控件播放音频的工作机制,与它同步就好。range 有一个 step 属性,就是步幅,默认和缺省值为 1,我们修改这个值就好。修改为浮点数可以不?可以,第四讲里我就已经悄悄地改为了0.1,不过有一个更合适的值,any,any是任意的意思,这里指任意值。任意值就是依赖 audio 控件的播放机制而定,它以秒级时间单位步进,它怎么步进range的三寸金莲就会怎么跟进,这就接近平滑移动滑杆滑块了,虽然不可能是真正的平滑。这个容易,只需改装一下 input type="range" 标签的写法:</p>
<pre>
<input id="mprog" type="range" min="0" max="100" step="<span class="tRed">any</span>" value="0" title="调节进度" />
</pre>
<p>再解决第二个问题:在range播放器界面加入audio相关的时间信息。我们可以借助播放器的容器元素即 id="mplayer" 的标签的伪元素来显示时间信息,这样的话,mplayer 容器也需要简单改装一下:</p>
<pre>
<div id="mplayer" <span class="tRed">data-tt="0:00 0:00"</span>>
<span class="tGreen"><!-- 这里是播放器子元素代码 --></span>
</div>
</pre>
<p>红色部分的代码,<mark>data-tt</mark> 的值将赋予伪元素,伪元素一旦成功创建且接口指向 <mark>data-tt</mark>,它将能显示 0:00 0:00 的 data-tt 值。<mark>data-名称</mark> 是HTML制定好的内置名称可变的属性,data 是关键词前缀,短连接线之后跟一个自命名字串,如此,伪元素通过 content 属性接收 <mark>data-名称</mark> 的值,JS通过 <mark>元素.dataset.名称</mark> 来动态读写 data-名称 的值。先在 CSS 代码中创建 #mplayer 选择器的 ::before 伪元素:</p>
<pre>
#mplayer::before {
position: absolute;
content: attr(data-tt); <span class="tGreen">/* attr(data-tt) 表示伪元素将显示由html赋予的 data-tt 值 */</span>
left: 0;
bottom: 25px; <span class="tGreen">/* 文本位置 */</span>
width: 100%; <span class="tGreen">/* 宽度与容器一致 */</span>
text-align-last: justify; <span class="tGreen">/* 文本最后一行两端对齐 :我们的文本就一行、用空格分成两个单位,空格是两端对齐的分界 */</span>
}
</pre>
<p>还可以加入字体及文本颜色等设置,尺寸、定位等都可以调整,一切根据需要来。至此,mplayer 容器元素已经具备显示播放器时间信息的能力,下一步交给JS处理:以 <mark>分:秒</mark> 的形式显示时间信息。audio 的 currentTime 和 duration 返回的时间都是以秒为时间单位的浮点数,小数点后面的数字很长,我们需要换算为 <mark>0:00</mark> 这样的分秒形式。为此,需要编写一个秒数转为分秒的函数:</p>
<pre>
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;
};
</pre>
<p>toMin 是函数名称,需要一个待传参数 val,花括号 {} 内的四行行代码是函数要做的事情,即处理 <mark>秒数 → 分:秒</mark> 事宜。第一行,如果传来的数值不合法,则直接返回字符串 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的秒数需要前面补零,第三行就是处理面补零操作,这一部分是不足一分钟的秒数。最后一行,返回 分:秒 结构的字符串。函数一时半会不能透彻理解关系不大,有空再慢慢消化,我们现在要掌握的是这个单一功能函数的调用方法:<mark>toMin(秒数);</mark> ,就这么简单。当然,我们还需要把函数应用在 audio 控件 的 timeupdate 监听事件中,把 toMin(秒数) 的结果实时告知 mplayer —— mplayer 有一个 data--tt 用来显示播放器音频相关的时间信息:</p>
<pre>
aud.addEventListener('timeupdate', () => {
if(!mseek) mprog.value = aud.currentTime / aud.duration * mprog.max; <span class="tGreen">/* 显示进度 */</span>
mplayer.dataset.tt = <span class="tRed">toMin(aud.currentTime)</span> + ' ' + <span class="tRed">toMin(aud.duration)</span>; <span class="tGreen">/* 显示时间信息 */</span>
});
</pre>
<p>toMin() 函数使用了两次,一次转换当前播放位置的时间信息,另一次是音频总时长的转换。我们将转换结果交给 mplayer 的 dataset.tt 属性,然后 mplayer 容器根据伪元素的设定将获得的 data 值进行两端对齐显示出来。</p>
<p>第三个待解决问题:鼠标指针在播放器上悬停时的提示语。这个相对简单,修改 mState 函数即可:</p>
<pre>
<span class="tGreen">/* 原来的语句 */</span>
var mState = () => btnplay.style.setProperty('--state', aud.paused ? 'paused' : 'running');
<span class="tGreen">/* 改为如下语句 */</span>
var mState = () => aud.paused ?
( btnplay.style.setProperty('--state', 'paused'), btnplay.title = '点击播放' ) :
( btnplay.style.setProperty('--state', 'running'), btnplay.title = '点击暂停' );
</pre>
<p>最后重新整理完整播放器代码如下(红色部分是本节新增或修改过的,同时还对CSS和html做了相应改动):</p>
<pre>
<style>
#mplayer { position: absolute; text-align: center; }
<span class="tRed">#mplayer::before {
position: absolute;
content: attr(data-tt);
left: 0;
bottom: 25px;
width: 100%;
text-align-last: justify;
}</span>
#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" <span class="tRed">data-tt="0:00 0:00"</span>>
<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;
<span class="tRed">var mState = () => aud.paused ?
( btnplay.style.setProperty('--state', 'paused'), btnplay.title = '点击播放' ) :
( btnplay.style.setProperty('--state', 'running'), btnplay.title = '点击暂停' );</span>
<span class="tRed">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;
};</span>
aud.addEventListener('timeupdate', () => {
if (!mseek) mprog.value = aud.currentTime / aud.duration * mprog.max;
<span class="tRed">mplayer.dataset.tt = toMin(aud.currentTime) + ' ' + toMin(aud.duration);</span>
});
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>
</pre>
<p>到 <a href="http://mhh.52qingyin.cn/api/pcode/" target="_blank">pencil code</a> 运行上述代码或将代码存为本地html文档后运行,我们会发现现在播放器看起来相当令人满意,如果还有下一步就是扩展性的功能了——lrc歌词同步。敬请期待!</p>
</div> 讲解详细!我得慢慢学习理解套用。谢谢老师的教程!{:4_180:} 边吃瓜边等着三寸金莲走近来{:4_174:} 看到三寸金莲迈碎步的说法笑喷,原来还有个any,这个从来不知道呢。可以带来漂亮的三寸金莲{:4_173:} sec = Math.floor(val) % 60 这个没怎么懂。取完60的倍数余下的是秒,没看出来怎么取整的。
要是有超过一小时的,这个还得改吧{:4_204:} 非常详尽的讲述,黑黑辛苦了{:4_199:} 亦是金 发表于 2024-1-30 09:25
讲解详细!我得慢慢学习理解套用。谢谢老师的教程!
{:4_190:} 樵歌 发表于 2024-1-30 11:32
边吃瓜边等着三寸金莲走近来
{:4_172:} 红影 发表于 2024-1-30 12:56
看到三寸金莲迈碎步的说法笑喷,原来还有个any,这个从来不知道呢。可以带来漂亮的三寸金莲
HTML的东西说多不多,就这么些标签和属性,说少不少,每一样东东又可以貌似可以没完没了地扩展 红影 发表于 2024-1-30 13:10
sec = Math.floor(val) % 60 这个没怎么懂。取完60的倍数余下的是秒,没看出来怎么取整的。
要是有超过一 ...
不改也行,它就以 分:秒 显示,例如:120:09
一般的音乐,不会超过一个小时,特意整合的少而又少,因为网络资源不允许。所以不考虑小时的显示问题,如若非得考虑不可,也是简单的事情。 真的详细,且简明,大师级作品就是厉害! 马黑黑 发表于 2024-1-30 13:27
HTML的东西说多不多,就这么些标签和属性,说少不少,每一样东东又可以貌似可以没完没了地扩展
很多不知道,在黑黑这里知道了呢{:4_187:} 马黑黑 发表于 2024-1-30 13:30
不改也行,它就以 分:秒 显示,例如:120:09
一般的音乐,不会超过一个小时,特意整合的少而又少,因 ...
嗯嗯,我只是举个例而已,知道了{:4_187:} 红影 发表于 2024-1-30 17:11
嗯嗯,我只是举个例而已,知道了
{:4_190:} 红影 发表于 2024-1-30 17:11
很多不知道,在黑黑这里知道了呢
有空去翻翻那个官网,慢慢就知道很多 亚伦影音工作室 发表于 2024-1-30 13:53
真的详细,且简明,大师级作品就是厉害!
感谢支持 马黑黑 发表于 2024-1-30 17:45
谢谢黑黑解答{:4_187:} 马黑黑 发表于 2024-1-30 17:46
有空去翻翻那个官网,慢慢就知道很多
我先把基础的学好,现在连基础都不行呢{:4_173:} 红影 发表于 2024-1-30 20:34
我先把基础的学好,现在连基础都不行呢
那个官网的东东就是基础的 马黑黑 发表于 2024-1-31 13:04
那个官网的东东就是基础的
看那些不如看这里的更容易懂呢{:4_204:}
页:
[1]
2