用 HTML 视频和进度条玩点无伤大雅的把戏

Avatar of Geoff Graham
Geoff Graham

DigitalOcean 为您旅程的每个阶段提供云产品。从 200 美元的免费额度 开始!

这个想法是在观看一个关于职场霸凌的强制性培训视频时产生的。我仿佛能听到高中时期的 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来提高动画性能,正如您在同一代码段中突出显示的那样。

这就是它的全部荣耀!

哦,是的:我在“观看”培训视频时一直在处理这个问题。而且,我顺利通过了最后的测验,非常感谢。🤓