HTML基础 - 视频播放

HTML基础 - 视频播放

作为HTML5中的一大亮点,video标签的出现取代了以往Flash的地位,成为了标准的视频播放方案。

接下来我们将一步一步挖掘video的所有功能。

基础使用

<video> <source src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4" type="video/mp4"> <source src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.webm" type="video/webm"> <p>您的浏览器不支持HTML5播放器</p> </video>

video标签下面使用多个source标签链接视频源,浏览器选取第一个支持的视频格式进行播放,当浏览器不支持video播放功能时,会显示标签里的其他内容,提示用户采用其他方法观看。

另外,videoinline元素

当然,还是更简单是使用方法:

<video src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"> <p>您的浏览器不支持HTML5播放器</p> </video>

我们得到下图,不知道的以为我们只加载了一张图片,因为它不会动:

video_demo.jpg

常用属性

autoplay

我们在video标签上添加autoplay属性让其加载后自动播放。

<video autoplay>...</video>

好了,现在视频动起来了,它开始播放了!

controls

但是问题又来了,把鼠标悬上去之后,发现啥也没发生,我们控制不了视频的播放!

我们为video标签再加上controls属性,再来看看。

截屏20200324上午11.19.15.jpg

现在当你鼠标移上视频的时候,底部会出现一些控制功能,每个浏览器都不一样,但是一般会提供:播放/暂停、导航、全屏、音量功能。

loop

布尔属性,循环播放

muted

布尔属性,控制视频是否静音

preload

视频预加载设置。

  • none 不会请求视频任何信息
  • metadata 只请求视频的一些元信息
  • auto 视频需要下载

设置合适的值可以减少服务器的访问量。

poster

视频海报,需要是图片地址。
当视频在第一帧可用时,啥都不会展示,但是如果设置了poster,那么在用户播放或者跳帧前展示该图片作为封面。

<video poster="/example/image.jpg">...</video>

但是我们发现,poster的尺寸和视频并不匹配,会出现空白的地方,那我们怎么去调整呢?使用css属性object-fit可以帮助我们,通过设置合适的值能让我们的poster撑满元素。

width,height

元素的宽高,一般不在这里设置。

DOM API

了解了video标签支持的属性之后,我们可以将一个视频展现在用户面前,它可以自动播放,循环播放,静音播放,有首帧海报。但是,这些功能肯定是不能满足我们的很多需求的,并且浏览器提供的controls各有所异,我们又怎么去实现一个跨浏览器的播放器呢?

还好我们可以使用JS去控制DOM来实现更多的媒体操作功能。

点击查看HTML Audio/Video具体支持的属性方法列表

接下来,我们就要实现一个自己的视频播放器!

DIY

播放/暂停

我们来实现的第一个功能,就是最基础的播放暂停。

需要使用到的API:

  • 方法:play()parse()
  • 属性:paused

我们按照最开始的基础使用小节,先写一个video标签。一开始这个视频是没有控制器,并且不会自动播放的。

然后我们开始使用JS配合DOM来定义一些视频播放控制器,先来定义一个Video

class Video { $video; constructor(options) { this.$video = document.querySelector(options.videoElement); } }

再定义播放/暂停方法:

play() { this.$video.play(); } pause() { this.$video.pause(); }

然后我们将事件绑定在对应的元素上:

$playCtr.addEventListener("click", e => { if (this.$video.paused) { this.play(); e.target.innerText = "暂停"; } else { this.pause(); e.target.innerText = "播放"; } });

这里注意的是我们判断视频的播放状态paused来切换播放状态。
这样我们就有了一个可以切换的播放/暂停的功能。

进度条

接下来我们实现一个实时反映视频播放进度的控制器,并且能够利用该控制器来跳转视频的播放时间,这里我们使用最基础的控件input[type=range]

需要使用到的API:

  • 属性:durationcurrentTime

在确保视频元数据加载完毕后,我们来初始化进度条的一些基础信息:

initProgress($progressCtr) { $progressCtr.value = 0; $progressCtr.min = 0; $progressCtr.max = this.$video.duration; }

然后监听播放时间变化来更新控件的值:

this.$video.addEventListener("timeupdate", e => { $progressCtr.value = e.target.currentTime; });

最后监听控件的值来切换视频播放点:

$progressCtr.addEventListener("change", e => { this.$video.currentTime = +e.target.value; });

这样我们就实现了一个简单的进度条:


可以看出还有很多细节需要完善,但目前我们不作讨论。

音量

其实音量控制和进度控制的思路是一样的,也是采用input[type=range]控件,这里我们还需要控制静音状态。

需要使用到的API:

  • 属性:mutedvolume

我们先初始化音量控件的最大最小值:

initVolume($volumeCtr) { $volumeCtr.value = this.$video.volume; $volumeCtr.step = 0.01; $volumeCtr.min = 0; $volumeCtr.max = 1; }

这里有些小细节,音量的最大值为1.0(100%),最小值为0。
由于默认step为1,所以我们需要调节该值,使我们能更精细地控制音量。

然后我们再监听控件的值变化,去改变视频的音量:

$volumeCtr.addEventListener("change", e => { const volume = e.target.value; if (volume > 0) { this.$video.muted = false; } this.$video.volume = volume; });

播放速率

调节播放速率也是一个很常见的功能,慢速看细节,快速跳剧情,我们来实现一个。也是使用input[type=range]控件。

需要使用到的API:

  • 属性:playbackRate
inityPlaybackRate($speedCtr) { $speedCtr.value = 1; $speedCtr.step = 0.25; $speedCtr.min = 1; $speedCtr.max = 2; $speedCtr.addEventListener("change", e => { this.$video.playbackRate = +e.target.value; }); }

全屏

我们来实现一个让视频全屏播放的功能。

需要使用到的API:

  • DOM:Element.requestFullscreen()

这个功能和video标签没有什么太大的联系,任何元素都可以全屏展示。该方法会返回一个Promise,并在完成全屏时resolve

initFullscreen($fullscreenCtr) { $fullscreenCtr.addEventListener("click", e => { this.$video.requestFullscreen(); }); }

这里我们发现,在Chrome和Safari中,如果直接将video标签全屏,浏览器会自动将元素撑满屏幕,并且会加上controls。所以如果我们要把自己的控制器也带上,就得在最外层再包装一层,然后全屏最外层元素。

另外全屏接口是有浏览器差异的,所以这里有个兼容方法:

function toFullVideo(videoDom) { if (videoDom.requestFullscreen) { return videoDom.requestFullscreen(); } else if (videoDom.webkitRequestFullScreen) { return videoDom.webkitRequestFullScreen(); } else if (videoDom.mozRequestFullScreen) { return videoDom.mozRequestFullScreen(); } else { return videoDom.msRequestFullscreen(); } }

用户体验优化

尽管我们能够实现基础的视频控制,不过现在我们的用户体验还是很差的,比如视频缓存量显示、视频加载提示、错误提示、字幕等等,这些我们都没有,不过好在我们有一些想法去实现这些功能。

所需要使用到的API:

  • 属性:buffered
  • 事件:waitingerrorcanplayplaying

缓冲提示

首先我们来测试一些播放事件:

function log(e) { console.log(`${e.type}`); } this.$video.addEventListener("canplay", log); this.$video.addEventListener("canplaythrough", log); this.$video.addEventListener("play", log); this.$video.addEventListener("playing", log); this.$video.addEventListener("waiting", log); this.$video.addEventListener("ended", log);

在我们播放一个正常视频时它是这样的:

play
playing
ended

当视频有一些卡顿时是这样的:

play
playing
waiting
canplay
playing
waiting
...

在这里我们可以针对视频缓冲增加加载提示,也就是在waiting的时候加载提示(转圈圈),在canplay时去掉提示。

缓冲量展示

另外还有一个事件叫做progress,用它搭配上buffered,咱们就可以清楚的知道视频的缓存加载了多少。

buffered返回的是一个TimeRanges对象,他有length属性标识有几个buffer区间,用户可能跳转视频,所以可能出现多个。还有俩方法获取buffer区间的起始位置,startend

// 第一个区间的起始位置 start(0) // 第一个区间的结束位置 end(0)

那么我们可以获取到所有的buffer记录:

logBuffered() { this.$video.addEventListener("progress", e => { const { length } = this.$video.buffered; const buffers = []; for (let i = 0; i < length; i++) { const s = this.$video.buffered.start(i); const e = this.$video.buffered.end(i); buffers.push(`${s} - ${e}`); } }); }

最后buffers的结果可能是这样的:

["0 - 10", "20 - 40"]

我们这里只是展示其数据结构

考虑到progress的触发频率也可以做节流,然后渲染UI。

上一篇 前端基础回顾 - HTML篇
下一篇 Medium每日精华