浅谈video标签在移动端的运用

最近在移动端项目用到了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)。

注意:
  1. 返回的值是Number类型,这使得在使用mint-ui这类的ui框架调试时可能会Toast一个空值,需要先转为字符串才能显示。
  2. 关于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

本文参考文章
视频播放--踩坑小计
h5 video 移动端填坑记
移动端实践 - video

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342