如何优雅的解决Video元素的`load事件被浏览器拦截?
画面回顾
- RD:我要加载视频。
- WeChatBrowser:不,你不行!
- HTMLVideoElement:啊这...
- RD:???浏览器不让我加载视频!
- PM:能解决吗?
- 。。。
开始烹饪
记录Video在微信浏览器加载策略踩坑经历
本期仅分享解决方案,原理见文末其他作者解析。
👇🏿👇🏿👇🏿👇🏿👇🏿👇🏿 暂且跟x5内核无关 ( 已修改测试过Phaser源码的video加载属性
video.setAttribute('x5-playsinline', 'true')
video.setAttribute('playsinline', 'true')
video.setAttribute('x5-video-player-type', 'h5')
现在进入正题
「享用时间:5min」
需求产生情形复现
某一天产品拍了我的肩膀,微笑着说:“小程序中的H5-Phaser3游戏场景中可以加视频吗?”;
我相顾一笑、一个坚定的眼神:“我做个技术调研,其他的走正常排期。” ( 因为是PhaserAPI熟练的搬运工,此番言论回答的.非..常..自...信....... 此处省略1w字.......
问题出现及排查
当距离提测还有一天的节点,于是乎我分别在安卓和iOS真机测试了一下,大事不妙!
“好家伙,我直呼好家伙!”
安卓可以正常使用,但这!!iOS的加载进度条却一直卡在100%
- 1.查看
Network:code:200以Blob正常加载了视频; - 2.查看Phaser的
Game实例:cache.video这个Map对象没有被set任何元素( 奇怪点1; - 3.
load事件没有触发'complete'方法 ( 奇怪点2; - 4.
load事件没有触发'onLoad'方法 ( 奇怪点3;
通过这四个点初步定位到了卡顿是发生了Load事件但是其回调函数被某不知名的事件拦截,接下来就是如何解的问题了。
问题搜索
“iOS是点击播放后才会去加载视频流。android下会执行canplay事件回调,但视频流也是边下边播。所以无法准确获得视频可加载时间点。”
上面引文描述和我的情形一样,没有用户行为的加载都没有loadComplete回调,固无法对Map()进行set,所以它的size为0。
// Phaser生命周期 - 预加载事件放在preload中
preload(){
this.load.video('key' ,'url' ,'canplay' ,true ,false ) // 参数具体看附件文档
}
// Phaser生命周期 - create渲染场景,load回调可以在此监听
create(){
this.load.once('complete', (e) => {
// PC、Android 都能够正常打印该对象size:1
// iOS 无法执行complete回调,没有任何打印
console.error(this.cache.video.entries)
)}
}
解决思路
最佳方案:
触发wx浏览器中video对象的load事件时,在wxAPI的回调中强行触发Video原型 load方法,于是乎有了当通过代码(非用户行为)调用load时,借用weixin-js-sdk包的异步api进行暴力load事件:
/**
* 微信环境下的加载
* @param {...any} args - 参数
* @returns {void}
*/
const wx = require('weixin-js-sdk');
let _load = window.HTMLVideoElement.prototype.load
window.HTMLVideoElement.prototype.load = function (...args): void {
wx.getNetworkType({
success: (res) => {
_load.call(this, ...args)
}
})
}
野路子:
上述是优化后的代码,也可以通过wx自带浏览器的window对象下的WeixinJSBridge.invoke(()=>{})处理该事件:
但是值得注意的是,微信可能未来取消在内置浏览器挂在window(ts:globalThis)下的WeixinJSBridge对象,建议通过引入'weixin-js-sdk'包或者引入script标签jweixin.js 来支持该事件。
let _load = window.HTMLVideoElement.prototype.load
function wxLoad(...args) {
const self = this
if(globalThis.WeixinJSBridge){
globalThis.WeixinJSBridge.invoke('getNetworkType', {}, function (e) {
_load.call(self, ...args)
})
}
}
window.HTMLVideoElement.prototype.load = function (...args):void {
_load.call(this, ...args)
}
文档贡献
Phaser3视频文件文档:Phaser3 Class: VideoFile
站内文章:H5 Video踩坑记录
站外文章:H5音频踩坑与填坑
GitHub:JavaScript判断是否微信内置浏览器
视频播放--踩坑小计