最近在移动端项目用到了video标签展示视频,原本以为作为html5标准一员的video到了今天兼容性应该没什么问题了,可一用才知道还是有些坑的......那么在这里就由浅入深的论述一下使用video的心得吧(一些众所周知的常用属性和我没怎么用过的在此均省略不表)。
属性
-
src
要播放的视频的 URL。只支持3种格式:MP4、WebM、Ogg。另外src除了可以使用放在服务器的绝对路径和项目中的相对路径(对于本地路径在PC端有一些限制,移动端貌似无法取到本地视频),还支持 base64码(需添加前缀data:video/mp4;base64,
注意有逗号) -
poster
规定视频正在下载时显示的图像,直到用户点击播放按钮。(也就是视频播放之前显示的一个预览图)其值和img标签的src属性相同,填上一个图片路径或者base64。 -
playsinline
webkit-playsinline
视频在移动端播放时会自动全屏,而这个属性就是为了阻止全屏动作的,添加-webkit-前缀增加在ios safari上的兼容性。(只用写上属性名就行,和autoplay,loop之类的类似) -
muted
如果出现该属性,视频的音频输出为静音。 -
autoplay
自动播放,但是在ios上无法执行自动播放......(添加muted
可以自动播放)需要用户执行play方法。(具体可参考视频播放--踩坑小计)
以上几种属性是写video标签时经常设置的几种属性,以下的几种则一般是需要在js中获取的属性,以便进行一些操作。
-
networkState
返回音频/视频的当前网络状态(activity)。
返回值如下- 0 = NETWORK_EMPTY - 音频/视频尚未初始化
- 1 = NETWORK_IDLE - 音频/视频是活动的且已选取资源,但并未使用网络
- 2 = NETWORK_LOADING - 浏览器正在下载数据
- 3 = NETWORK_NO_SOURCE - 未找到音频/视频来源
一般用到的就是1和2,当值为1的时候表示视频已经可以播放了(至少是当前帧已经加载好了,不过ios此时播放可能会白屏),值为2的时候播放下一帧会卡住(安卓)或者白屏(ios)。
注意:
- 返回的值是
Number
类型,这使得在使用mint-ui这类的ui框架调试时可能会Toast
一个空值,需要先转为字符串才能显示。 - 关于ios上返回2继续播放会白屏的问题(仅仅出现在视频第一次点击播放的时候),这里我暂时没发现是什么原因导致的(有可能是视频太大??)
-
readyState
readyState 属性返回音频/视频的当前就绪状态。
返回值如下- 0 = HAVE_NOTHING - 没有关于音频/视频是否就绪的信息
- 1 = HAVE_METADATA - 关于音频/视频就绪的元数据
- 2 = HAVE_CURRENT_DATA - 关于当前播放位置的数据是可用的,但没有足够的数据来播放下一帧/毫秒
- 3 = HAVE_FUTURE_DATA - 当前及至少下一帧的数据是可用的
- 4 = HAVE_ENOUGH_DATA - 可用数据足以开始播放
这里的返回值也是Number
类型的
我在项目中先使用的就是这个属性,在返回1或2的时候代表刚进入这个页面浏览器还在加载视频,返回4则代表整个视频已经加载完毕了。可是我发现ios在返回3的时候立即播放还是有时会出现白屏,而安卓在返回3的时候是可以播放的,因此最后我选择了使用networkState
来对视频加载状态进行判断。
- 不可思议的
currentTime
定义是这么说的:设置或返回音频/视频播放的当前位置(以秒计)。当设置该属性时,播放会跳跃到指定的位置。
而我使用这个属性来判断视频是否能连续播放,当视频播放的时候如果这个属性的值‘走动了’,可以认为视频已经可以播放下一帧了。感谢h5 video 移动端填坑记这篇文章提供的方法。
方法
我一般用到的方法也就是play()
,pause()
这里不过多赘述。
事件
-
canplay
当浏览器能够开始播放指定的音频/视频时,会发生 canplay 事件。
乍一看有了这个事件似乎就不需要上面各种state去判断视频能否播放了,可惜万恶的ios不支持这个事件,实测第一次进入页面ios在canplay事件触发的时候视频的networkState
仍然处于2这个状态,也就是还未加载完成...... -
progress
当浏览器正在下载指定的音频/视频时,会发生 progress 事件。
我使用这个事件的方向可能有点‘歪门邪道’,众所周知一个视频在页面加载的时候等待时间或许会有点长,一般网站使用的是一个图片或者gif去代替video标签,当视频加载好了的时候就让video显示出来。而上面的canplay
用不了,所以我抱着试试的想法在progress事件触发的时候让loading图隐藏起来(loading图默认显示),结果实际效果让我很满意,progress事件触发的时候视频的poster
已经显示出来了,我初步判断第一次触发这个事件应该是视频第一帧可能前几帧都加载好了。 -
timeupdate
timeupdate 事件在音频/视频(audio/video)的播放位置发生改变时触发。
这个事件一看就是结合上面的currentTime
属性用的。在后文我会详述。 -
waiting
waiting 事件在视频由于需要缓冲下一帧而停止时触发。
在我的项目用它主要是因为ios白屏的时候会触发这个事件......这样我可以在这个事件中让视频暂停。 -
ended
ended 事件在音频/视频(audio/video)播放完成后触发。
由于我的视频没有用loop
属性所以我使用这个事件来提示用户视频播放结束。
应用
项目基于vue,ui框架使用的是mint-ui,还是直接上代码吧...
<div class="video-con fl">
<div class="video" @click="$_videoFromApp_getVideoRecord">
<!-- 后台没有视频时显示的内容 -->
<img class="no-video" v-show="videoFromApp_noVideo" :src="require('@/images/icon/video.png')" alt="">
<div class="bg-gray" v-show="videoFromApp_noVideo"></div>
<!-- 视频处于暂停时显示的内容 -->
<div v-show="!videoFromApp_playing">
<img class="play-video" v-show="!videoFromApp_noVideo"
:src="require('@/images/icon/play-video.png')" alt="">
<span class="play-time" v-show="!videoFromApp_noVideo">{{videoFromApp_videoTime}}</span>
</div>
<!-- loading -->
<mt-spinner v-show="videoFromApp_loading" class="loading-css" type="fading-circle"></mt-spinner>
<!-- 视频 -->
<video webkit-playsinline playsinline
ref="indentVideo"
class="real-video"
:src="videoFromApp_videoSrc"
v-show="!videoFromApp_noVideo"
:poster="videoFromApp_videoImg"
@progress="$_videoFromApp_hasVideo"
@waiting="$_videoFromApp_waiting"
@ended="$_videoFromApp_endVideo"></video>
</div>
</div>
js部分
<script>
......
//播放视频
$_videoFromApp_getVideoRecord(v){
......
if (this.videoFromApp_playing) {
this.videoFromApp_playing = false;
this.$refs.indentVideo.pause();
} else {
this.videoFromApp_playing = true;
let video = this.$refs.indentVideo;
let networkState = this.$refs.indentVideo.networkState;
let readyState = this.$refs.indentVideo.readyState;
if(networkState==1){
this.$refs.indentVideo.play();
this.videoFromApp_loading = true;
video.ontimeupdate = ()=>{
if(video.currentTime > 0.1){
this.videoFromApp_loading = false;
}
}
}else{
Toast({
message: '拼命加载中,请稍后',
position: 'bottom',
duration: 1000
});
this.videoFromApp_playing = false;
}
}
},
//视频初步加载
$_videoFromApp_hasVideo(){
this.videoFromApp_loading = false;
},
//缓冲
$_videoFromApp_waiting(){
this.$refs.indentVideo.pause();
this.videoFromApp_playing = false;
Toast({
message: '拼命加载中,请稍后',
position: 'bottom',
duration: 1000
});
},
//视频播放完成
$_videoFromApp_endVideo(){
this.videoFromApp_playing = false;
},
</script>
在此就简述一下播放视频方法,当视频处于暂停状态时,点击播放,判断networkState
,当值为1的时候允许播放,此时监听timeupdate
事件,(使用addEventListener
监听会有毛病,如果看过我之前的一篇关于iframe的文章应该会有所了解)先加上loading,如果currentTime > 0.1
代表视频已经能流畅播放了,再隐藏loading。这个过程中如果无法播放的话就会走到waitting事件中,视频会暂停。
再次点击继续重复这个过程直到视频可以正常播放。
写在后面
其实吧本文写作的真正目的是为了抛砖引玉,文中所写的一些方法只为解决项目中的燃眉之急,文中部分内容仅仅为个人观点,如果各位读者发现了文中的缺陷与问题,欢迎在评论区留言探讨。
======================
2018-11-12更新
关于播放白屏的原因:最近发现安卓部分手机播放视频又白屏了,最终总结出了避免白屏的方法。
1.播放视频的格式最好是mp4 avc h.264格式的,不是这种格式的视频用video播放很可能白屏。
2.如果视频格式已经是avch264的了那么就需要看看是后台原因还是原生那边的原因了,一般来说应该是后台的问题,ios目前获取视频的时候请求头会带一个与断点续传有关的信息,后台需要对此进行相应的配置。而部分安卓手机也会用类似的方式请求视频,目前项目里的后台同事将返回的状态码从200改成了206就ok了。
最后,转载请注明出处https://www.jianshu.com/p/8f39050aa607