【微信小程序】视频页的相关逻辑及API

wxml代码及相关api:

    <!-- 视频列表 -->
    <scroll-view 
    class="videoScroll" 
    scroll-y 
    refresher-enabled 
    bindrefresherrefresh="handleRefresher"
    refresher-triggered='{{isTriggered}}'
    bindscrolltolower="handleSecrollToLower"
    >
        <view class="videoItem" wx:for="{{videoList}}" wx:key="id">
            <video  
            wx:if="{{videoId === item.data.vid}}"
            class="common" 
            src="{{item.data.urlInfo.url}}" 
            object-fit="cover" bindplay="handlerPlay" 
            id="{{item.data.vid}}"
            poster="{{item.data.coverUrl}}"
            bindtimeupdate="handleTimeUpdate"
            bindended="handleEnder"
            ></video>
            <!-- 性能优化:使用image图片代替video标签作为封面 -->
            <image wx:else bindtap="handlerPlay" id="{{item.data.vid}}" class="common" src="{{item.data.coverUrl}}"></image>
            <view class="content">{{item.data.title}}</view>

            <!-- 视频底部描述和头像、喜欢、评论 -->
            <view class="footer">
                <image class="avatar" src="{{item.data.creator.avatarUrl}}"></image>
                <text class="nickName">{{item.data.creator.nickname}}</text>
                <view class="comments_praised">
                    <text class="item">
                        <text class="iconfont icon-buoumaotubiao15"></text>
                        <text class="count">{{item.data.praisedCount}}</text>
                    </text>
                    <text class="item">
                        <text class="iconfont icon-pinglun1"></text>
                        <text class="count">{{item.data.commentCount}}</text>
                    </text>
                    <button open-type="share" class="item btn">
                        <text class="iconfont icon-gengduo"></text>
                    </button>
                </view>
            </view>
        </view>
    </scroll-view>

scroll-view 滚动区域标签
scroll-y 允许纵向滚动
refresher-enabled 开启下拉刷新
bindrefresherrefresh="handleRefresher" 自定义下拉刷新的触发回调
refresher-triggered='{{isTriggered}}' 下拉状态标识符
bindscrolltolower="handleSecrollToLower" 触底回调

video 视频标签
wx:if="{{videoId === item.data.vid}}" 视频和遮盖图的条件判断
src="{{item.data.urlInfo.url}}" 视频动态路径
object-fit="cover" 当视频大小与 video 容器大小不一致时,视频的表现形式
bindplay="handlerPlay" 点击视频播放/继续回调
id="xxx" 给event事件绑定event.currentTarget.id=xxx
data-id="{{xxx}} 则是给event事件绑定event.currentTarget.dataset.id=xxx
poster="{{item.data.coverUrl}}" 为视频设置封面(遮罩层)
bindtimeupdate="handleTimeUpdate" 进度条变化触发
bindended="handleEnder" 视频播放结束时的回调

JS代码

初始化数据

  data: {
    videoGroupList: [],    //初始化导航标签数据
    navId: '',             //导航标签的id标识
    videoList: [],         //视频列表数据
    videoId: '',           //video的标识
    videoUpdateTime: [],   //记录视频播放的时间,而且应该是对象数组(这里要思考数据类型)
    isTriggered: false     //下拉刷新的标识符
  },

视频导航标签页和获取视频列表的逻辑

  onLoad: function (options) {
    // 页面加载即请求导航标签
    this.getVideoGroupList()
  },

  // 获取导航标签数据的功能函数
  async getVideoGroupList() {
    let result = await request('/video/group/list')
    // 更新videoGroupList
    this.setData({
      videoGroupList: result.data.slice(0, 14),
      navId: result.data[0].id
    })
    // 在获取导航标签id的时候获取
    this.getVideoList(this.data.navId)
  },

  // 获取视频列表数据的功能函数
  async getVideoList(navId) {
    let videoListData = await request('/video/group', { id: navId })
    // 如果没有请求视频列表的数据直接结束当前事件
    if (!videoListData.datas) {
      return
    }
    // 给需要遍历的视频列表手动添加id,方便遍历
    let index = 0
    let videoList = videoListData.datas.map(item => {
      item.id = index++
      return item
    })
    // 获取视频成功后,关闭加载的消息提示框
    wx.hideLoading()
    // 更新videoList的状态数据
    this.setData({
      videoList,
      isTriggered: false
    })
  },

  // 点击下标,切换视频列表分类
  selectTab(event) {
    // let navId = event.currentTarget.id    // 会自动将number转换成string
    let navId = event.currentTarget.dataset.id

    // 位移运算符,>>>0,目标数据线转换成二进制,然后去移动指定的位数,移出去的不要,不够的用零补齐
    // 右移零位会强制转换数据类型为number,强制转布尔值: !!!

    // 修改navId的状态
    this.setData({
      navId,
      // 在切换视频页下标时,将之前的视频页清空,屏幕留白
      videoList: []
    })
    // 显示正在加载
    wx.showLoading({
      title: '正在努力加载中',
    })
    // 点击下标,直接调用getVideoList()获取最新的视频列表数据
    this.getVideoList(this.data.navId)
  },

点击播放视频的回调

  // 点击视频、图片播放选项,bindplay="handlerPlay"    点击视频播放/继续回调
  handlerPlay(event) {
    /* 
      播放新的视频,关掉之前的视频
      思路
        1.先找到关闭视频的方法  wx.createVideoContext(string id, Object this)
        2.必须找到上一个视频的实例对象,然后关掉
      设计模式:  单例模式
        1.需要创建多个对象的情况下,使用一个变量来保存
        2.当创建新的对象的时候就会把之前的对象覆盖掉
        3,节省内存空间
    */

    let vid = event.currentTarget.id

    // 使用性能优化后解决了同时播放的问题,上面的代码反而多余了
    /* this.videoContext  //首次点击为undefined,之后均为某一个视频的上下文对象
    // 若this.videoContext为真,则不是第一次点击,并且关闭当前的视频。第一次点击,这条代码直接忽略
    this.videoContext && this.vid !== vid && this.videoContext.stop()
    // 这里就使用了设计模式中的单例模式,来通过视频的vid属性 判断当前视频和上一个视频是否为同一个
    this.vid = vid */

    // 将当前点击的vid更新至data中videoId
    this.setData({
      videoId: vid
    })

    // 判断当前视频是否为已经播放记录
    let { videoUpdateTime } = this.data
    // 找当前播放记录中是否有相同vid判断是否为播放过的视频
    let videoItem = videoUpdateTime.find(item => item.vid === vid)
    // 一旦有播放记录跳转至指定的位置,跳转为api为this.videoContext.seek()
    if (videoItem) {
      this.videoContext.seek(videoItem.currentTime)
    }
    // 创建新视频上下文,可以调用api来对视频进行操作
    this.videoContext = wx.createVideoContext(vid)
    // 用image代替video和遮罩层图片进行性能优化后,需要进行视频的自动播放不然
    this.videoContext.play()
  }

监听视频播放结束后播放

  // 监听视频播放结束移除播放记录的事件,bindended="handleEnder"       视频播放结束时的回调
  handleEnder(event) {
    // 将当前的播放记录从 videoUpdateTime 中移除,先解构出videoUpdateTime
    let { videoUpdateTime } = this.data
    // findIndex找到播放完视频的下标
    let index = videoUpdateTime.findIndex(item => item.vid === event.currentTarget.id)
    // 截取数组,破坏原数组
    videoUpdateTime.splice(index, 1)

    // 移除完后,要更新数组
    this.setData({
      videoUpdateTime
    })
  },

视频播放进度条的回调

  // 视频播放进度实时变化的回调,bindtimeupdate="handleTimeUpdate"   进度条变化触发
  handleTimeUpdate(event) {
    // currentTime: 2.988868   当前播放时长
    // duration: 71.64    视频总时长

    // 整理数据
    let videoTimeObj = { vid: event.currentTarget.id, currentTime: event.detail.currentTime }
    /* 
     添加播放记录到数组中
      思路:判断videoUpdateTime中是否已经有当前视频的播放记录了
      1.如果有:  删除
      2.如果没有:直接push
    */

    let { videoUpdateTime } = this.data
    // 判断播放记录是否包含当前点击的视频
    // 以下这种写法性能太差
    /* //如果没有直接push进播放数据的数组
     videoUpdateTime.every(item => item.vid !== videoTimeObj.vid) && videoUpdateTime.push(videoTimeObj)
     // 如果有,更新currentTime 
     videoUpdateTime.some(item => {
       return item.vid === videoTimeObj.vid
     }) && videoUpdateTime.map(item=>{
       if (item.vid === videoTimeObj.vid) {
           item.currentTime = videoTimeObj.currentTime
       }
     }) */

    // 换种方式,从是否能到这个相同的vid开始,find方法很契合这个思路,若没有找到返回undefined
    let videoItem = videoUpdateTime.find(item => item.vid === videoTimeObj.vid)
    if (videoItem) {  //播放记录中有相同的视频,直接更新事件
      videoItem.currentTime = videoTimeObj.currentTime
    } else {  //若没有直接push进数组
      videoUpdateTime.push(videoTimeObj)
    }

    // 将处理完的videoUpdateTime数据更新页面中的数据
    this.setData({
      videoUpdateTime
    })
  }

下拉刷新和上划触底的回调

  // 下拉刷新的回调   bindrefresherrefresh="handleRefresher"    自定义下拉刷新的触发回调
  handleRefresher() {
    // 发送请求获取最的数据
    this.getVideoList(this.data.navId)
  },

  //  scroll-view 上拉触底加载的回调   bindscrolltolower="handleSecrollToLower"  触底回调
  handleSecrollToLower() {
    // mock 假数据
    let newVideoList = []
    let { videoList } = this.data
    // 扩展运算符
    videoList.push(...newVideoList)
    this.setData({
      videoList
    })
  },

CSS代码

滑块区的高度需动态设置

/* 视频区 */
.videoScroll {
    margin-top: 20rpx;
    /* 
        滑块的高度必须是动态的
        vh vw 是视口高度,1vh = 1%视口高度
    */
    height: calc(100vh - 162rpx);
}

总结

  • scroll-view 和 video 的标签各个api
  • 性能优化:使用image图片作为视频的遮罩层代替video标签作为封面
  • id="xx" 给event事件绑定event.currentTarget.id=xx
    data-name="xx" 则是给event事件绑定自定义属性name,event.currentTarget.dataset.name=xx,注意这里的name命名不能使用驼峰写法,因为打印event发现即使你写驼峰也会帮你转化为全部小写
  • 视频页各个需求的逻辑
posted @ 2022-04-10 21:07  wanglei1900  阅读(219)  评论(0编辑  收藏  举报