这个想法是在观看一个关于职场霸凌的强制性培训视频时产生的。我仿佛能听到高中时期的 Geoff 嘲笑我这个胆小鬼不得不观看这个视频。
但我们现在就在这里。
视频 UI 实际上很漂亮,但真正引起我注意的是进度条——或者更确切地说是[progress].value
。它是一个简单的从绿色到蓝色的渐变,随着视频播放而逐渐增长。

我早就知道可以在<progress>
元素上创建同样的渐变。Pankaj Parashar 在 2016 年的一篇 CSS-Tricks 文章中演示了这一点。
我真的很想模拟类似的东西,但并没有玩过太多视频。我对 JavaScript 也不算专家。但这实际上能有多难呢?我的意思是,我只需要知道我们在视频中播放到了什么位置,并用它来更新进度值。对吧?
我内心的那个“恶霸”嘲笑我嘲笑得厉害,所以我决定尝试一下。这并不是世界上最复杂的事情,但我玩得很开心,并且想在这段演示中分享我是如何设置它的。
标记
HTML5 全程,宝贝!
<figure>
<video id="video" src="http://html5videoformatconverter.com/data/images/happyfit2.mp4"></video>
<figcaption>
<button id="play" aria-label="Play" role="button">►</button>
<progress id="progress" max="100" value="0">Progress</progress>
</figcaption>
</figure>
关键行是这个
<progress id="progress" max="100" value="0">Progress</progress>
max
属性告诉我们我们使用 100 作为最高值,而 value
属性将我们从零开始。这是有道理的,因为它允许我们以百分比的形式考虑视频的进度,其中 0% 是开始,100% 是结束,我们的初始起点是 0%。
样式
我当然不会深入探讨在 CSS 中设置 <progress>
元素样式的过程。我之前链接的 Pankaj 的文章已经对此进行了非常棒的介绍。我们需要在进度值上绘制渐变的 CSS 如下所示
/* Fallback stuff */
progress[value] {
appearance: none; /* Needed for Safari */
border: none; /* Needed for Firefox */
color: #e52e71; /* Fallback to a solid color */
}
/* WebKit styles */
progress[value]::-webkit-progress-value {
background-image: linear-gradient(
to right,
#ff8a00, #e52e71
);
transition: width 1s linear;
}
/* Firefox styles */
progress[value]::-moz-progress-bar {
background-image: -moz-linear-gradient(
right,
#ff8a00, #e52e71
);
}
诀窍是要注意各种细微差别,使其能够跨浏览器兼容。WebKit 和 Mozilla 浏览器都有自己处理进度元素的特殊方式。这使得样式变得有点冗长,但是,嘿,你能怎么办呢?
从视频中获取进度值
我知道如果我想获取视频的当前时间并将其显示为百分比表示的值,则会涉及一些数学运算。如果你认为在高中做书呆子给了我数学超能力,那么,很抱歉让你失望了。
我不得不写下我认为需要发生的事情的大纲
- 获取视频的当前时间。如果我们想将其显示为进度值,则必须知道视频播放到了什么位置。
- 获取视频时长。了解视频的长度将有助于将当前时间表示为百分比。
- 计算进度值。同样,我们使用百分比。我那本落满灰尘的代数课本告诉我公式是
部分 / 全部 = % / 100
。在视频的上下文中,我们可以将其重写为currentTime / duration = progress value
。
这给了我们开始所需的所有指示。事实上,我们可以开始为需要选择的元素创建变量,并找出我们需要使用哪些属性来填充方程式。
// Variables
const progress = document.getElementById( "progress" );
// Properties
// progress.value = The calculated progress value as a percent of 100
// video.currentTime = The current time of the video in seconds
// video.duration = The length of the video in seconds
不错,不错。现在我们需要通过将这些内容代入我们的方程式来计算进度值。
function progressLoop() {
setInterval(function () {
document.getElementById("progress").value = Math.round(
(video.currentTime / video.duration) * 100
);
});
}
我承认:我忘了方程式会产生小数。这就是Math.round()
发挥作用的地方,将它们更新到最接近的整数。
这实际上可以让渐变进度条在视频播放时进行动画!
我以为我可以将其视为胜利,然后开心地走开。但是,有几件事困扰着我。此外,我在控制台中收到了一些错误。不好。
显示当前时间
没什么大不了的,但肯定是一个不错的功能。我们可以在进度条旁边添加一个计时器,并在播放时计算秒数。我们已经拥有执行此操作所需的数据,所以我们只需要标记并将其连接起来。
让我们在<label>
中包装时间,因为<progress>
元素可以有一个。
<figure>
<video controls id="video" src="http://html5videoformatconverter.com/data/images/happyfit2.mp4"></video>
<figcaption>
<label id="timer" for="progress" role="timer"></label>
<progress id="progress" max="100" value="0">Progress</progress>
</figcaption>
</figure>
现在我们可以将其连接起来。我们将为其分配一个变量,并使用innerHTML
在标签内打印当前值。
const progress = document.getElementById("progress");
const timer = document.getElementById( "timer" );
function progressLoop() {
setInterval(function () {
progress.value = Math.round((video.currentTime / video.duration) * 100);
timer.innerHTML = Math.round(video.currentTime) + " seconds";
});
}
progressLoop();
嘿,这有效!
额外加分是将计时器转换为HH:MM:SS
格式显示。
添加播放按钮
两个 UI 同时运行的事实确实困扰着我。<video>
元素有一个controls
属性,当使用时,它会显示视频控件,例如播放、进度、跳过、音量等。让我们将其省略。
但这意味着我们至少需要提供一种启动和停止视频的方法。让我们按钮一下。
首先,将其添加到 HTML 中
<figure>
<video id="video" src="http://html5videoformatconverter.com/data/images/happyfit2.mp4"></video>
<figcaption>
<label id="timer" for="progress" role="timer"></label>
<button id="play" aria-label="Play" role="button">►</button>
<progress id="progress" max="100" value="0">Progress</progress>
</figcaption>
</figure>
然后,使用一个函数将其连接起来,该函数在点击时在播放和暂停之间切换视频。
button = document.getElementById( "play" );
function playPause() {
if ( video.paused ) {
video.play();
button.innerHTML = "❙❙";
}
else {
video.pause();
button.innerHTML = "►";
}
}
button.addEventListener( "click", playPause );
video.addEventListener("play", progressLoop);
嘿,它仍然有效!
我知道从一开始就移除 HTML5 提供的丰富的控件集似乎很奇怪。我可能不会在真实的项目中这样做,但我们只是在这里玩玩。
清理我丑陋的意大利面条代码
我真的很感谢我的朋友 Neal Fennimore。他花时间和我一起查看了这段代码,并提供了建议,这些建议不仅使代码更易读,而且在定义状态方面做得更好得多……
// States
const PAUSED = 'paused';
const PLAYING = 'playing';
// Initial state
let state = PAUSED;
……在侦听播放、暂停和点击事件时,对状态进行适当的检查,然后再触发进度函数……
// Animation loop
function progressLoop() {
if(state === PLAYING) {
progress.value = Math.round( ( video.currentTime / video.duration ) * 100 );
timer.innerHTML = Math.round( video.currentTime ) + ' seconds';
requestAnimationFrame(progressLoop);
}
}
video.addEventListener('play', onPlay);
video.addEventListener('pause', onPause);
button.addEventListener('click', onClick);
……甚至通过用requestAnimationFrame
替换setInterval
来提高动画性能,正如您在同一代码段中突出显示的那样。
这就是它的全部荣耀!
哦,是的:我在“观看”培训视频时一直在处理这个问题。而且,我顺利通过了最后的测验,非常感谢。🤓
快乐的大脚2?
这似乎并不有效。为什么不将进度最大值设置为时长,然后使用 currentTime 设置进度值?
当然,它不会慢到显著的程度。
我似乎错过了 JavaScript 中定义
video
的地方?在生产环境中你肯定想这样做,但这样只是以这种方式定位 HTML 元素本身。
不错。你可能会在生产环境中这样做,但不要忘记在视频完成后终止 setInterval 和事件侦听器。