小程序视频组件踩坑历险记

原始需求:

实现一个长列表页,列表中有视频和图文两种元素,未播放的视频上显示标题,在列表页点击视频后直接全屏播放。

感觉问题不大,稳得一匹,毕竟小程序本身就有video组件,而视频播放源又是腾讯视频,优秀的腾讯视频甚至还封装了腾讯视频小程序播放插件。这个插件本身就是基于video组件的封装,用法差不多,不过腾讯视频上的内容只能通过这个插件进行播放。

简单看一下用法:

// 支持slot,用于在video上显示UI
<txv-video 
  v   // 可使用v的方式应用data变量
  player 
  width="{{100%}}"    //自定义宽度
  height="{{\'auto\'}}" // 自定义高度
  autoplay="{{true}}"> // 是否自动播放
  <cover-view class=\'txv-video-slot\'>video slot</cover-view>
</txv-video>

真香预警

尝试一

直接用腾讯视频视频插件,在上面覆盖一层cover-view渲染封面、标题,在video的bindplay,bindpause等事件控制cover-view的隐藏。

好吧这个方案听起来就很有问题,列表长了渲染这么多视频组件肯定会crash的。然而寄希望于小程序说不定对长列表做了优化(并不会)还是做了一下尝试。然后真机测试的时候十分争气地在刷了几屏后崩溃并死机了。

尝试二

  • 列表中的视频区域用图片代替,整个页面只有一个视频组件并隐藏,点击图片时修改视频源并全屏播放。

隐藏视频组件

第一个问题就是:如何把真实的视频组件藏起来:

尝试二.1.1

把width和height设为0不就可以了吗+v+!!

需要注意的是,我们需要在点击图片后通过这段代码来获取视频上下文并使其全屏播放:

TxvContext.getTxvContext(playerid).requestFullScreen();

在调试工具上十分顺利!

然而:真机测试时,这种情况下视频组件没有在视图中渲染,TxvContext.getTxvContext(playerid)无法获取到视频上下文(设width和height为1倒是可以的)。

这大概是某种优化机制?

尝试二.1.2

设置一个父容器,令宽高为0,并设置

overflow: hidden;

熟悉小程序原生组件的同学大概也知道了,像video这样的原生组件,是不能在父级节点使用 overflow: hidden 来裁剪原生组件的显示区域的。视频上下文虽然可以正常获得,但并没有达到隐藏的目的。更多其他限制可以阅读原生组件说明文档

尝试二.1.3

既然把父容器高度设为0不会影响视频的播放,那我们换一种方式,给父容器设置一个负的margin来隐藏这个组件。

(紧张尝试)

很感动地成功了,终于把视频组件隐藏,且不影响播放了T T

  • 最终实现其实也很简单:

wxml:

<view class="fake-video-wrapper">
    <txv-vedio></txv-vedio>
</view>

wxss:

.fake-video-wraper {
  width: 0;
  height: 0;
  margin-left: -750rpx;
}

控制视频播放

那么接下里第二个问题就是如何去控制视频的播放、暂停和全屏状态切换

尝试三.2.1

这个问题看起来根本不是问题啊,在点击图片的事件触发后改变视频组件绑定的vid(腾讯视频中的视频标识符),并调用视频上下文的全屏和播放方法就可以了。

尝试之后发现视频组件在全屏后并没有自行开始播放。

排查排查排查

最后认为原因是改变了组件绑定的vid后需要加载重新视频数据,而视频还没有被加载出来就调用了play()方法,所以这里其实是无效的。

  • 经过了各种尝试后,在改变vid后设置了一个800ms的延迟去调用play方法(视频大概都能在这个延时加载出来.......然而这样的写法好迷)
尝试三.2.2

看起来上面好像已经解决了问题(然而并不)

因为隐藏了视频组件,我们要保证组件只在全屏状态下播放,否则用户会在列表能听到视频的声音却看不到视频播放(???)

  • 这只要监听fullscreenchange 事件,在退出视频的时候pause()掉视频就好了~

然而用户大概总是不太听话(小程序也是)

这里发现如果视频正在播放时,小程序被切到后台(触发onHide()生命周期)再重新切换到前台时,视频已经退出全屏了,但是仍然在播放(听得到声音),没有触发fullscreenchange事件!

fine,那我们在onHide()生命周期里让他pause()掉就好了。然而他还在播放。

fine,那我们在onShow()生命周期里让他pause()掉可以不。然而他还在播放。

fine,,,,

  • 最终的解决方法是,用一个flag标记视频在播放,onHide()时将flag设为false。监听视频的play事件,如果flag为false则把视频pause()掉。

结果

在上面一波挣扎之后,虽然代码变得有点奇怪,这个列表基本上是实现了,加上图片的lazyload,长列表划了好多好多屏都没有crash噢!T^T

【真 · 结果】

产品&设计体验了之后,认为点击视频图片后突然横屏播放的交互太差劲了(小程序似乎还不支持自动旋转屏幕)。

于是把直接在列表页播放视频的功能删掉了。

改为点击视频后进入新页面再自动播放。

【踩过的坑啊打水漂了啊T T】

后记

然后列表页就没有视频了。

那我们就不用再考虑性能和全屏不全屏和蜜汁背景播放的问题啦!

我以为不会再有麻烦了。

然后在视频播放页有一个自己定制的modal。

然后。【这个modal根本盖不住视频组件(原生组件层级最高噢)】。

只好在显示modal的时候把视频组件隐藏掉,显示一张视频图片占位。

为什么不用wx:if呢,因为性能不好,视频组件会被完全重新渲染,而且也无法保留之前的播放状态。

  • 嗯,这段可以隐藏视频的wxss还是十分有用噢!
.fake-video-wraper {
  width: 0;
  height: 0;
  margin-left: -750rpx;
}