评论

小程序的视频内容流自动播放

记录一下开发视频流自动播放时走过的坑

小程序的视频内容流自动播放

啊啊啊,又解决一个问题

0、起因

这个需求产生的起因,是在做内容流(包含文本,图片,视频)的时候,需要如果流里面有视频,则滚动到一定位置时自动播放视频,类似朋友圈、微博等等的自动播放效果。

1、第一版尝试

第一版的思路是:

  • 收集当前所有内容流相对于页面头部的高度,做成一个Array
  • 滚动过程中,监听页面滚动事件,当达到某个高度要求,则播放对应的索引视频

这个操作缺点太多了,捡几个主要的说

缺点:

  • 内容流是一个个的组件,获取距离顶部高度不方便,也不太准。并且组件内需要通过事件传播到列表页,在列表页进行高度Array整理、事件监听、切换索引等等(如果有几种列表页,就要写几遍,很麻烦)
  • 监听滚动事件本身就消耗性能,做了节流也不是那么优秀

2、第二版尝试

突然,就发现了wx.createIntersectionObserver这个属性,它的作用是:返回intersectionObserver对象,用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见(创建一个目标元素,根据目标元素和视窗的相交距离来判断当前页面滚动的情况。通常这个方案也用于页面图片的懒加载)。参考https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.html

怎么解释呢,就是可以理解为,做一个监听,如果当前被监听的元素,进入了你规定的视界或者离开你规定的视界,就触发。

那么,怎么做到监听呢,参考如下代码:

/** 监控视频是否需要播放 */
let {screenWidth, screenHeight} = this.extData.systemInfo //获取屏幕高度
let topBottomPadding = (screenHeight - 80)/2 //取屏幕中间80的高度作为播放触发区域,然后计算上下视窗的高度 topBottomPadding
// 80这个高度可以根据UI样式调整,我这边基本两个视频间隔高度在100左右,超过了两个视频之间的间隔,就会冲突,两个视频会同时播放,不建议过大

const videoObserve = wx.createIntersectionObserver()
videoObserve.relativeToViewport({bottom: -topBottomPadding, top: -topBottomPadding})
    .observe(`#emotion${this.data.randomId}`, (res) => {
        let {intersectionRatio} = res
        if(intersectionRatio === 0) {
            //离开视界,因为视窗占比为0,停止播放
            this.setData({
                playstart: false
            })
        }else{
            //进入视界,开始播放
            this.setData({
                playstart: true
            })
        }

    })

其中,observe 是对应你需要监听的视频(也就是滚动进入视窗的元素)

那么,为什么选择relativeToViewport呢,是因为我们需要对它进入某一个视窗进行监听,而不是对进入整个屏幕视窗监听(因为可能整个视窗里会有多个视频)。

以上,就是整个逻辑思路。

最开始用的relativeTo监听视频进入某个元素(如.view-port),但是后来发现每个页面都要写这个元素,太麻烦,并且容易遮盖操作区域

// 太麻烦,后来舍弃了这个方案
<view class="view-port" style="height: 100rpx; position: fixed; z-index: 1;width: 100%;letf:0;top:50%;transform: translateY(-50%);"></view>
最后一次编辑于  2019-12-01  
点赞 8
收藏
评论

9 个评论

  • Graves
    Graves
    2021-07-12

    这里方法传参是有问题么,卡到这一步了,控制台一直报这个错

    2021-07-12
    赞同 1
    回复
  • Frank
    Frank
    2023-03-07


    非常有趣的技术实现方式!看来你对小程序的开发有很深的理解和经验。使用IntersectionObserver来实现播放视频的逻辑,不仅可以减少页面滚动事件的监听带来的性能消耗,而且还可以避免组件内部事件传播到列表页的麻烦。在处理视窗高度方面,你还考虑了屏幕的高度、UI样式等因素,这展现了你注重细节和用户体验的态度。感谢你分享这个技术实现的思路!

    2023-03-07
    赞同
    回复
  • 为之奈何
    为之奈何
    2021-02-20

    视频不能全屏,进入屏幕播放后,点击视频全屏,ios会闪退

    2021-02-20
    赞同
    回复
  • 终点亦是起点
    终点亦是起点
    2020-12-23

    可以看下博主的小程序吗? 视屏播放能力有用到插件吗?

    2020-12-23
    赞同
    回复 1
    • 宗仔GEG
      宗仔GEG
      2021-01-25
      可以搜一下we站,不过目前用的人少了
      2021-01-25
      回复
  • 默
    2020-12-22

    思路可以借鉴下,不错

    2020-12-22
    赞同
    回复
  • 苗苗
    苗苗
    2020-08-14

    this.data.randomId 这个id后缀的值是怎么动态给的呢?

    2020-08-14
    赞同
    回复 2
    • 宗仔GEG
      宗仔GEG
      2020-08-15
      可以通过random方法生成就行
      2020-08-15
      回复
    • 薛定谔的猫
      薛定谔的猫
      2021-04-26
      这个id怎么获取还是不太明白,有具体点的解决的方法吗
      2021-04-26
      回复
  • 熊猫大侠
    熊猫大侠
    2020-04-03

    你好,博主,我想问一下,怎么样能获取视频的第一帧做为封面?不想要自动播放。因为现在视频不播放是黑屏。很难看。

    2020-04-03
    赞同
    回复 1
    • 宗仔GEG
      宗仔GEG
      2020-04-17
      这个视频封面是后端在上传时截取并传给前端的~貌似没有别的办法,这个不像h5的canvas可以直接获取
      2020-04-17
      回复
  • 温暖的弦 🥭
    温暖的弦 🥭
    2020-03-17

    wxml:

    <video wx:if="{{showvideo == 1}}" class="vids{{index}}" autoplay="{{playstart}}" class='img' id="vids{{index}}" src='{{video}}' data-index="{{index}}"></video>

    ready:

    ready: function ready() {

    let that = this

    /** 监控视频是否需要播放 */

    let screenHeight = that.data.screenHeight //获取屏幕高度

    let screenWidth = that.data.screenWidth //获取屏幕宽度

    let topBottomPadding = (screenHeight - 80) / 2

    console.log(topBottomPadding)

    const videoObserve = wx.createIntersectionObserver()

    videoObserve.relativeToViewport({ top: 80 })

    .observe(`.vids${that.properties.index}`, (res) => {

    let { intersectionRatio } = res

    console.log(res)

    console.log(intersectionRatio)

    if (intersectionRatio === 0) {

    //离开视界,因为视窗占比为0,停止播放

    that.setData({

    playstart: false

    })

    } else {

    //进入视界,开始播放

    that.setData({

    playstart: true

    })

    }


    })


    },


    2020-03-17
    赞同
    回复 13
    • 宗仔GEG
      宗仔GEG
      2020-03-18
      哦哦,组件里面使用需要用 this.createIntersectionObserver() ,把wx -> this,我这因为想的是大家方便看明白,就用的wx,看这个文档第一段https://developers.weixin.qq.com/miniprogram/dev/api/wxml/wx.createIntersectionObserver.html
      2020-03-18
      回复
    • 温暖的弦 🥭
      温暖的弦 🥭
      2020-03-18回复宗仔GEG
      真的是非常感谢,现在还有一个小小的问题,就是多图的轮播图已经生效了,但是video autoplay 我看代码里面已经为true了,还是不播放~
      2020-03-18
      回复
    • 宗仔GEG
      宗仔GEG
      2020-03-18回复温暖的弦 🥭
      这里有两种处理方式,一种是通过拿到video实例,在曝光的时候通过实例的api去播放;另一种是先不渲染video,用封面图替代,曝光的时候再wx:if渲染video,这里auto-play默认设置为true就行(因为好像不支持通过auto-play来控制播放),移出曝光范围以后就不再渲染video,这里就看你需不需要记录播放时长了,要记录的话,第一种比较好
      2020-03-18
      回复
    • 温暖的弦 🥭
      温暖的弦 🥭
      2020-03-18回复宗仔GEG
      实在是太不好意思了,问了你这么久,现在还有一个问题,就是视频这里,当前视频已经离开了当前区域应该不播放了,但是还在播放,我在想是不是这里需要修改啊videoObserve.relativeToViewport({ bottom: -topBottomPadding, top: -topBottomPadding})
      2020-03-18
      回复
    • 宗仔GEG
      宗仔GEG
      2020-03-18回复温暖的弦 🥭
      对的,你应该用我给的那段,控制上下区域的,这样只要在这个区域外,就让它暂停
      2020-03-18
      回复
    查看更多(8)
  • 温暖的弦 🥭
    温暖的弦 🥭
    2020-03-17

    有demo 之类的吗~~

    2020-03-17
    赞同
    回复 9
    • 宗仔GEG
      宗仔GEG
      2020-03-17
      上面那段代码就够啦~改一改就能跑了
      2020-03-17
      回复
    • 温暖的弦 🥭
      温暖的弦 🥭
      2020-03-17回复宗仔GEG
      我这边跑不动啊,这段代码是写在onload里面的,对吧,但是并没有所有的视频都监听到啊,之监听到了第一个;可能是我哪里写的不对
      2020-03-17
      回复
    • 宗仔GEG
      宗仔GEG
      2020-03-17回复温暖的弦 🥭
      哦哦,这个是把视频抽出来,作为单独的组件,然后这段代码放到视频组件里面的,因为这个监听是针对当前这个组件的
      2020-03-17
      回复
    • 温暖的弦 🥭
      温暖的弦 🥭
      2020-03-17回复宗仔GEG
      额,视频组件?咋弄的嘞,我这个获取的数据还有分页啥的~~
      2020-03-17
      回复
    • 宗仔GEG
      宗仔GEG
      2020-03-17回复温暖的弦 🥭
      就是把video单独抽出来作为一个组件,然后在ready监听这个曝光事件,我这边封装的组件不单纯是个video,还有图片滚动播放的效果,所以就没法那么快给demo代码片段啦,原理就是在组件里面监听这个,跟父页面没啥关系,不用考虑父页面是啥
      2020-03-17
      回复
    查看更多(4)
登录 后发表内容