做为HTML5中的一大亮点,video
标签的出现取代了以往Flash
的地位,成为了标准的视频播放方案。css
接下来咱们将一步一步挖掘video
的全部功能。html
<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
播放功能时,会显示标签里的其余内容,提示用户采用其余方法观看。web
固然,仍是更简单是使用方法:浏览器
<video src="http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"> <p>您的浏览器不支持HTML5播放器</p> </video> 复制代码
咱们获得下图,不知道的觉得咱们只加载了一张图片,由于它不会动:缓存
咱们在video
标签上添加autoplay
属性让其加载后自动播放。bash
<video autoplay>...</video> 复制代码
好了,如今视频动起来了,它开始播放了!服务器
可是问题又来了,把鼠标悬上去以后,发现啥也没发生,咱们控制不了视频的播放!markdown
咱们为video
标签再加上controls
属性,再来看看。数据结构
如今当你鼠标移上视频的时候,底部会出现一些控制功能,每一个浏览器都不同,可是通常会提供:播放/暂停、导航、全屏、音量功能。dom
布尔属性,循环播放
布尔属性,控制视频是否静音
视频预加载设置。
设置合适的值能够减小服务器的访问量。
视频海报,须要是图片地址。 当视频在第一帧可用时,啥都不会展现,可是若是设置了poster
,那么在用户播放或者跳帧前展现该图片做为封面。
<video poster="/example/image.jpg">...</video> 复制代码
可是咱们发现,poster
的尺寸和视频并不匹配,会出现空白的地方,那咱们怎么去调整呢?使用css
属性object-fit
能够帮助咱们,经过设置合适的值能让咱们的poster
撑满元素。
元素的宽高,通常不在这里设置。
了解了video
标签支持的属性以后,咱们能够将一个视频展示在用户面前,它能够自动播放,循环播放,静音播放,有首帧海报。可是,这些功能确定是不能知足咱们的不少需求的,而且浏览器提供的controls
各有所异,咱们又怎么去实现一个跨浏览器的播放器呢?
还好咱们可使用JS去控制DOM来实现更多的媒体操做功能。
点击查看HTML Audio/Video
具体支持的属性方法列表
接下来,咱们就要实现一个本身的视频播放器!
咱们来实现的第一个功能,就是最基础的播放暂停。
须要使用到的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:
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。