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
播放功能时,会显示标签里的其他内容,提示用户采用其他方法观看。
另外,video
是inline元素
。
当然,还是更简单是使用方法:
<video src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4">
<p>您的浏览器不支持HTML5播放器</p>
</video>
我们得到下图,不知道的以为我们只加载了一张图片,因为它不会动:
常用属性
autoplay
我们在video
标签上添加autoplay
属性让其加载后自动播放。
<video autoplay>...</video>
好了,现在视频动起来了,它开始播放了!
controls
但是问题又来了,把鼠标悬上去之后,发现啥也没发生,我们控制不了视频的播放!
我们为video
标签再加上controls
属性,再来看看。
现在当你鼠标移上视频的时候,底部会出现一些控制功能,每个浏览器都不一样,但是一般会提供:播放/暂停、导航、全屏、音量功能。
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:
- 属性:
duration
、currentTime
在确保视频元数据加载完毕后,我们来初始化进度条的一些基础信息:
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:
- 属性:
muted
、volume
我们先初始化音量控件的最大最小值:
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
- 事件:
waiting
,error
,canplay
,playing
…
缓冲提示
首先我们来测试一些播放事件:
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
区间的起始位置,start
、end
:
// 第一个区间的起始位置
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。