【微信小程序】视频页的相关逻辑及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发现即使你写驼峰也会帮你转化为全部小写 - 视频页各个需求的逻辑
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix