微信小程序Video组件实践总结

16,040 阅读6分钟

我司微店+小程序最近有需要在商品详情里面添加视频的功能,故使用了微信官方Video组件,在使用过程中遇到了一些问题,有些问题特别坑,我觉得有必要总结一下,分享出来供有这方面需求的同行参考,本篇文章我主要讲解最终我使用的兼容性比较好的方案,希望能帮助有需要的童鞋。

在我使用video组件的过程中,主要碰到以下问题:

  • 布局表现不一致
  • wifi下自动播放,视频播放黑屏(安卓常见)
  • 视频列表不能随页面滚动
  • 视频全局播放,点击返回按钮,布局位置异常
  • 视频每次都需要重新加载

示例源码

UI布局

我们要实现的布局

  • video上面有个覆盖video的图片,在图片上有个控制播放的按钮
  • video上面有视频和图片切换按钮组合,视频播放的时候该组合按钮也必须存在

按照常规的写法,我们的代码结构可能如下

<view>
    <!-- 视频 -->
    <video></video>
    <!-- 覆盖视频的图片 -->
    <view>
        <image/>
        <!-- 播放按钮 -->
        <view></view>
    </view>
    <!-- 底部视频和图片切换按钮 -->
    <view></view>
</view>

然后通过图片position:absolute绝对定位,z-index层叠上下文来完成布局,这种方式在ios系统下表现还好,在一些安卓机上图片和按钮就会被video组件覆盖,这是因为video组件是原生组件,使用上有一些限制,具体限制可以看这里微信官方文档,在这里主要影响我们的原因是,原生组件的层级是最高的,所以页面中的其他组件无论设置z-index为多少,都无法盖在原生组件上。

幸好,微信官方提供了cover-viewcover-image组件,可以覆盖原生组件上面。通过使用,我们的方案如下:

<view>
    <video>
        <cover-view>
            <!-- 覆盖图片 -->
            <cover-image></cover-image>
            <!-- 播放按钮 -->
            <cover-image></cover-image>
        </cover-view>
    </video>
</view>

播放功能

单个视频,点击播放

有关video组件播放的几个配置,主要有

  • src 要播放视频的资源地址
  • autoplay 是否自动播放

另外,微信还提功能了const video = wx.createVideoContext()方法可以获得一个video实例,通过video.play()方法来控制播放。

故有几种方案:

第一种方案,拿到视频地址后,通过setData方法更新video组件地址和自动播放为true,该方法优点是由组件自身控制开始播放时间,基本不会存在黑屏或者视频加载出来不播放的情况,但缺点是切换到其它页面再回来,每次都需要重新加载视频,性能和体验上不是很好。

<!--方案一-->

<!--js-->
this.setData({
    src:url,
    autoplay:true
})
<!--html-->
<video src="{{src}}" autoplay="{{autoplay}}"></video>

第二种方案,通过api控制,存在的问题就是视频播放的时机,因为视频地址加载是需要时间的,如果我们在页面一渲染后就点击播放按钮,此时如果视频还未加载完,直接调用videoContext.play()方法可能会失效,故我们需应该等待视频加载完之后,才调用play()方法,这里采用的是setData的回调方法里面去调用play()方法开启播放,另外还可以通过pause()暂停,stop()停止等方法来控制视频播放状态。这种方案可以只需加载一次视频,之后播放无需重新加载,用户体验较好,另外对于无法控制播放时机问题做了处理,基本不会出现黑屏的情况

<!--方案一-->

<!--js-->
data:{
    autoplay:false
}
const { videoContext } = this.data;

<!--视频播放,关键代码-->
this.setData({
    autoplay:true,
    url:videoUrl
},()=>{
    videoContext.play()
})
<!--html-->
<video src="{{url}}" autoplay="{{autoplay}}"></video>

单个视频,wifi下自动播放

微信提供判断网络环境的api,通过api判断,如果是wifi下,就主动调用videoContext.play()方法。

wx.getNetworkType({
        success(res) {
            const networkType = res.networkType;
            if (networkType === 'wifi') {
                that.videoPlay();
            }
        },
    });
    
videoPlay(){
    this.setData({
        autoplay:true,
        url:videoUrl
    },()=>{
    videoContext.play()
    })
}

视频不在可视范围,停止播放

微信提供IntersectionObserver对象,用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见,以前判断元素是否在视图内,我们常用的是getBoundingClientRect,该方法在微信环境也能使用,但该方法因为需要计算元素位置,引起页面重绘,比较影响性能。而IntersectionObserver是原生自己判断位置,不会有相关问题,可以通过这个api来监听元素位置,从而控制是否播放。

wx.createIntersectionObserver(that)
    .relativeToViewport()
        .observe('.video--wrap', res => {
            if (res && res.intersectionRatio > 0) {
                that.videoPlay();
            } else {
                that.videoPause();
            }
        });

视频静音播放

可以通过变更muted这个属性来控制视频是否静音,比如一开始默认静音,点击音量按钮开启声音这个功能。

data:{
    muted:true //初始为true,静音
}
methods:{
    handleMuted(){
        this.setData({
            muted:false
        })
    }
}


视频列表

一开始我是用过wx:for列举视频播放组件,子组件播放,触发一个事件,父组件监听子组件事件,更新视频列表值,然后子组件监听属性变化来更新播放状态的,该方案在ios上表现不错,可是放在安卓上,页面竟然不能滚动了,后面通过查阅资料,video列表组件不能直接像普通图片列表一样,真正的方案是需要用视频首帧图片做列表,然后点击首帧图片,跳转到一个新页面,或者一个蒙层播放视频。

小程序左滑右滑实现

我们需求上还有左右滑切换图片和视频显示的功能,这需要通过监听touchstarttouchmove来判断手机滑动方向,从而切换视频和图片的显示位置。

touchstart: function(e) {
        this.setData({
            startX: e.changedTouches[0] && e.changedTouches[0].clientX,
            startY: e.changedTouches[0] && e.changedTouches[0].clientY,
        });
    },
touchmove: function(e) {
        var that = this,
        startX = that.data.startX, //开始X坐标
        startY = that.data.startY, //开始Y坐标
        touchMoveX = e.changedTouches[0].clientX, //滑动变化坐标
        touchMoveY = e.changedTouches[0].clientY, //滑动变化坐标
    },
//根据起点和终点返回方向 1:向上,2:向下,3:向左,4:向右,0:未滑动
GetSlideDrection: function(startX, startY, endX, endY) {
    var dy = endY - startY;

    var dx = endX - startX;

    var result = 0;

    //如果滑动距离太短

    if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
        return result;
    }

    var angle = this.GetSlideAngle(dx, dy);

    if (angle >= -45 && angle < 45) {
        result = 4;
    } else if (angle >= 45 && angle < 135) {
        result = 1;
    } else if (angle >= -135 && angle < -45) {
        result = 2;
    } else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
        result = 3;
    }

    return result;
    },
GetSlideAngle: function(dx, dy) {
    return (Math.atan2(dy, dx) * 180) / Math.PI;
},

以上方案都可以通过小程序代码片段进行查看

点击查看方案

调试

开发者工具和真机表现差异很大,故在开发者工具上开发完之后,需要多在不同真机上进行调试。

总结

实际我在开发中碰到的问题还挺多,小程序video组件坑还是挺多的,我没有一一描述出来,如果你有碰到其它问题,欢迎留言评论,如果我曾经碰到过,或许能够提供一些意见,我写这篇文章的目的就是让更多的小程序开发者尽量少踩坑,如果刚好能够帮助到你,感谢点赞支持!