18_微信小程序之微信视频号滚动自动播放视频效果实现2.0
一.先上效果图
关于这个效果呢,我之前已经实现过一个12_微信小程序之微信视频号滚动自动播放视频效果实现,只不过之前的实现是监听scroll-view的onscroll,根据scroll-view的滑动位置动态计算出播放的index来控制自动播放,本文将采用微信小程序为我们提供的IntersectionObserver来实现。
二.IntersectionObserver
-
IntersectionObserver wx.createIntersectionObserver(Object component, Object options)
- 在页面中使用: IntersectionObserver wx.createIntersectionObserver(this, {observeAll: true}),observeAll为true可同时监听多个节点
- 在自定义组件中使用:IntersectionObserver this.createIntersectionObserver({observeAll: true})
-
IntersectionObserver IntersectionObserver.relativeTo(string selector, Object margins)
-
IntersectionObserver.observe(string targetSelector, IntersectionObserver.observeCallback callback)
- 指定目标节点的选择器并监听由relativeTo指定的参照区域与目标节点是否相交,由于我们需要监听多个video节点,所以这里的目标节点选择器我们使用class选择器即可。
- 当目标节点与参照区域相交时,callback(res)返回的res中的intersectionRatio大于0
- 当目标节点与参照区域不相交时,callback(res)返回的res中的intersectionRatio等于0
-
IntersectionObserver.disconnect()
三.视频列表渲染
细节的地方可参考12_微信小程序之微信视频号滚动自动播放视频效果实现,这里就直接上代码了。
<scroll-view class="video-list" scroll-y>
<view class="list">
<view class="video-item-wrapper video" style="width: {{item.videoWidth}}px;" wx:for="{{_videoList}}">
<view class="video-item" style="height: {{item.videoHeight}}px;">
<video wx:if="{{playIndex == index}}" id="player" class="player" src="{{item.src}}" object-fit="contain" show-center-play-btn="{{false}}" custom-cache="{{true}}" autoplay="{{true}}"></video>
<block wx:else>
<image class="thumbnail" src="{{'data:image/jpg;base64,' + item.thumbnail}}"/>
<view class="action">
<view class="play-wrapper" bindtap="play" data-index="{{index}}">
<image class="play" src="./images/ic_play.png"/>
<view style="margin-top: 10rpx;">{{item.formatDur}}</view>
</view>
</view>
</block>
</view>
</view>
<view style="width: 100%; height: {{_videoList[0] ? _videoList[0].videoHeight + 'px' : '600rpx'}};"></view>
</view>
</scroll-view>
.video-list {
width: 100%;
height: 100vh;
}
.list {
width: 100%;
}
.video-item-wrapper {
background: #000;
margin-top: 20rpx;
}
.video-item-wrapper:last-child {
margin-bottom: 20rpx;
}
.video-item {
position: relative;
width: 100%;
}
.thumbnail, .player {
position: absolute;
left: 50%;
top: 50%;
width: 100%;
height: 100%;
transform: translate(-50%, -50%);
}
.action {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, .6);
}
.play-wrapper {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #fff;
}
.play {
width: 48rpx;
height: 48rpx;
}
Component({
properties: {
videoList: {
type: Array,
value: [],
observer: function(newVal, oldVal) {
var that = this
const query = that.createSelectorQuery()
query.select(".video-list").boundingClientRect()
query.exec((res) => {
var itemWidth = res[0].width
for(var i=0; i<newVal.length; i++) {
newVal[i].videoWidth = Math.floor(itemWidth)
newVal[i].videoHeight = Math.floor(itemWidth/(newVal[i].width/newVal[i].height))
}
that.setData({
_videoList: newVal
})
})
}
},
playIndex: {
type: Number,
value: -1,
observer: function(newVal, oldVal) {
var that = this
this.setData({
playIndex: newVal
})
if(newVal >= 0) {
var videoContext = wx.createVideoContext('player', that)
if(videoContext) {
videoContext.stop()
}
var timer = setTimeout(function() {
clearTimeout(timer)
var videoContext = wx.createVideoContext('player', that)
if(videoContext) {
videoContext.play()
}
}, 500)
}
}
}
},
data: {
_videoList: []
},
methods: {
play: function(event) {
var that = this
var index = event.currentTarget.dataset.index
this.setData({
playIndex: index
})
}
}
})
四.参照节点选定
这里我们以第一个视频的高度一半的位置作为参照节点即可,在实际应用的时候也可以根据自己的需求去设置参照节点。
<scroll-view class="video-list" scroll-y>
<view class="list">
<view class="video-item-wrapper video" style="width: {{item.videoWidth}}px;" wx:for="{{_videoList}}">
<view class="video-item" style="height: {{item.videoHeight}}px;">
<video wx:if="{{playIndex == index}}" id="player" class="player" src="{{item.src}}" object-fit="contain" show-center-play-btn="{{false}}" custom-cache="{{true}}" autoplay="{{true}}"></video>
<block wx:else>
<image class="thumbnail" src="{{'data:image/jpg;base64,' + item.thumbnail}}"/>
<view class="action">
<view class="play-wrapper" bindtap="play" data-index="{{index}}">
<image class="play" src="./images/ic_play.png"/>
<view style="margin-top: 10rpx;">{{item.formatDur}}</view>
</view>
</view>
</block>
</view>
</view>
<view style="width: 100%; height: {{_videoList[0] ? _videoList[0].videoHeight + 'px' : '600rpx'}};"></view>
</view>
</scroll-view>
<view class="relativeView" style="top: {{_videoList[0] ? _videoList[0].videoHeight/2 + 'px':'30%'}};"></view>
.relativeView {
position: fixed;
left: 0;
top: 30%;
width: 100%;
height: 1px;
background: #f00;
}
我们根据上面的图片分析一波,默认情况下,第一个视频与参照物相交,控制第一个视频播放即可,当向上滑动到第二个视频与参照物相交时,控制第二个视频播放即可
五.滑动自动播放
上面已经分析清楚了,直接上代码。
<scroll-view class="video-list" scroll-y>
<view class="list">
<view class="video-item-wrapper video" style="width: {{item.videoWidth}}px;" wx:for="{{_videoList}}" data-index="{{index}}">
<view class="video-item" style="height: {{item.videoHeight}}px;">
<video wx:if="{{playIndex == index}}" id="player" class="player" src="{{item.src}}" object-fit="contain" show-center-play-btn="{{false}}" custom-cache="{{true}}" autoplay="{{true}}"></video>
<block wx:else>
<image class="thumbnail" src="{{'data:image/jpg;base64,' + item.thumbnail}}"/>
<view class="action">
<view class="play-wrapper" bindtap="play" data-index="{{index}}">
<image class="play" src="./images/ic_play.png"/>
<view style="margin-top: 10rpx;">{{item.formatDur}}</view>
</view>
</view>
</block>
</view>
</view>
<view style="width: 100%; height: {{_videoList[0] ? _videoList[0].videoHeight + 'px' : '600rpx'}};"></view>
</view>
</scroll-view>
<view class="relativeView" style="top: {{_videoList[0] ? _videoList[0].videoHeight/2 + 'px':'30%'}};"></view>
Component({
properties: {
videoList: {
type: Array,
value: [],
observer: function(newVal, oldVal) {
var that = this
const query = that.createSelectorQuery()
query.select(".video-list").boundingClientRect()
query.exec((res) => {
var itemWidth = res[0].width
for(var i=0; i<newVal.length; i++) {
newVal[i].videoWidth = Math.floor(itemWidth)
newVal[i].videoHeight = Math.floor(itemWidth/(newVal[i].width/newVal[i].height))
}
that.setData({
_videoList: newVal
})
this.intersectionObserver = this.createIntersectionObserver({observeAll: true})
this.intersectionObserver.relativeTo('.relativeView')
.observe(".video", (res) => {
let index = res.dataset.index
let intersectionRatio = res.intersectionRatio
if(intersectionRatio > 0) {
that.setData({
playIndex: index
})
}
})
})
}
},
...
},
...
})
大功告成…
六.隐藏参照节点
注意:这里不能给参照节点设置 hidden=“{{true}}” 或者使用wx:if,否则,将监听不到目标节点与参照节点相交的情况,而应该使用z-index。
<view class="relativeView" style="top: {{_videoList[0] ? _videoList[0].videoHeight/2 + 'px':'30%'}}; z-index: -9999"></view>
最后在组件销毁的时候,取消监听即可
lifetimes: {
detached: function() {
if (this.intersectionObserver) {
this.intersectionObserver.disconnect()
}
}
},
|