适用于微信小程序的视频滑动切换组件
以前用过小程序提供的video-swiper组件, 其中的问题和bug也不做过多描述了。
为了项目进度,当时也没过多思考,没有使用video-swiper,而是用swiper和swiper-item简单实现了功能,后来自己测出来了个问题,但是客户没有反馈,测试也没有提bug,所以这个问题就耽搁,现在抽空重新写个demo,等以后遇到类似的项目,再拿出来修改。
新写的这个demo就是参考了以前用过的轮播图组件和Element Plus中的Virtualized Table 虚拟化表格,并且基于小程序的movable-area和movable-view做的一个视频滑动切换组件,如果有其他问题,请大家多多指点,谢谢!
还是直接上代码!!!
//index.ts
const randomColor: string[] = ['a', 'b', 'c', 'd', 'e', 'f', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
let customState: {
videoExample: Record<string, any> | null
loading: boolean
currentIndex: number
startLocation: number
endLocation: number
isTouchmove: boolean
inertiaRollState: boolean
originalList: Array<listItem>
movingLength: number,
dragSource: null | string,
boundsReboundTimer: number,
} = {
videoExample: null, //video实例
loading: false, //数据加载
currentIndex: 0, //当前swiper
startLocation: 0, //手指摁下位置
endLocation: 0, //手指抬起位置
isTouchmove: false, //是否有滑动操作
inertiaRollState: true, //惯性滚动效果是否结束 true结束 false未结束
originalList: [], //列表总集合
movingLength: 0, //移动的距离
dragSource: null, //产生移动的原因 https://developers.weixin.qq.com/miniprogram/dev/component/movable-view.html
boundsReboundTimer: 0, //回弹结束定时器
};
interface listItem {
originalIndex: number
color: string
}
Component({
/**
* 组件的属性列表
*/
properties: {},
/**
* 组件的初始数据
*/
data: {
total: 0, //目前数据总数
distance: 0, //卷上去的距离
list: <Array<listItem>>[], //当前展示数据列表
windowInfo: <WechatMiniprogram.WindowInfo>{},
currentIndex: <number>0,
},
/**
* 组件的方法列表
*/
methods: {
//当前屏幕上渲染的数据的集合
showView(): Array<listItem> {
let showList: Array<listItem> = [];
let end = customState.currentIndex + 5 >= customState.originalList.length ? customState.originalList.length - 1 : customState.currentIndex + 5;
end = end <= 9 ? (9 > customState.originalList.length - 1 ? customState.originalList.length - 1 : 9) : end;
let start = customState.currentIndex - (9 - (end - customState.currentIndex));
start = start >= 0 ? start : 0;
for (let i = start; i <= end; i++) {
showList.push(customState.originalList[i])
}
return showList
},
//生成数据(拉取数据)操作
loadMoreColor() {
if (customState.loading) return
customState.loading = true
let list: Array<listItem> = []
for (let i = 0; i < 10; i++) {
let color = '#'
for (let n = 0; n < 6; n++) {
color += randomColor[Math.floor(Math.random() * randomColor.length)]
}
// console.log(color);
list.push({
originalIndex: customState.originalList.length + i, //记录当前项的index
color,
})
}
setTimeout(() => {
customState.originalList = [...customState.originalList, ...list]
console.log(customState.originalList)
this.setData({
total: customState.originalList.length,
list: this.showView(),
}, () => {
console.log(this.data.list)
customState.currentIndex === 0 && this.createVideoContext()
customState.loading = false
})
}, 2000);
},
createVideoContext() {
// customState.videoExample && customState.videoExample.stop && customState.videoExample.stop()
// customState.videoExample = wx.createVideoContext(`video_${customState.currentIndex}`)
// customState.videoExample.play()
},
//拖动过程中触发的事件
dragChange(e: WechatMiniprogram.MovableViewChange) {
// source 产生移动的原因
const { y, source } = e.detail;
customState.movingLength = y;
customState.dragSource = source;
},
//手指触摸抬起
dragEnd() {
clearTimeout(customState.boundsReboundTimer);
if (customState.dragSource !== "touch") {
if (this.data.distance !== customState.movingLength) {
customState.boundsReboundTimer = setTimeout(() => {
this.setData({
distance: -(customState.currentIndex * this.data.windowInfo.windowHeight),
});
}, 500);
}
customState.dragSource === "touch-out-of-bounds" && customState.currentIndex === this.data.total - 1 && this.loadMoreColor();
return;
}
//swiper是否切换视频 为true时, 切换视频; 为false时,滚回到原视频
//100 切换临界值 随意定的
const swiper = Math.abs(this.data.distance - customState.movingLength) >= 100;
//moveState滑动状态 为true时是下拉(上一个), 为false时是上拉(下一个)
const moveState = customState.movingLength - this.data.distance >= 0;
if (swiper) {
customState.currentIndex = moveState ? customState.currentIndex - 1 : customState.currentIndex + 1;
this.setData({
list: this.showView(),
});
}
customState.currentIndex === this.data.total - 1 && this.loadMoreColor();
this.setData({
distance: -(customState.currentIndex * this.data.windowInfo.windowHeight),
currentIndex: customState.currentIndex,
});
},
dragCancel() {
this.setData({
distance: -(customState.currentIndex * this.data.windowInfo.windowHeight),
});
},
},
lifetimes: {
created() {
customState = {
videoExample: null,
loading: false,
currentIndex: 0,
startLocation: 0,
endLocation: 0,
isTouchmove: false,
inertiaRollState: true,
originalList: [],
movingLength: 0,
dragSource: null,
boundsReboundTimer: 0,
}
},
attached() {
this.setData({
windowInfo: wx.getWindowInfo()
})
this.loadMoreColor();
}
}
})
<!--index.wxml-->
<movable-area class="container" style="width: {{windowInfo.windowWidth}}px; height: {{windowInfo.windowHeight}}px;">
<!-- movable-view容器在展示第一个元素时,"transform"的translateY不为0 -->
<!-- {{currentIndex === 0 && 'margin-top: 7px;'}} 所以这里增加样式判断(根据render树展示的样式再具体设置) -->
<movable-view direction="vertical"
out-of-bounds
y="{{distance}}px"
damping="{{50}}"
friction="{{100}}"
bindchange="dragChange"
bindtouchend="dragEnd"
bindtouchcancel="dragCancel"
class="list-view"
style="width: 100%; height: {{total * windowInfo.windowHeight + 50}}px; {{currentIndex === 0 && 'margin-top: 7px;'}}">
<view wx:for="{{list}}"
wx:key="originalIndex"
class="list-item"
style="width: 100%; height: {{windowInfo.windowHeight}}px; top: {{item.originalIndex * windowInfo.windowHeight}}px; background-color: {{item.color}};">
<!-- <video id="{{'video_' + item.originalIndex}}" src="{{item.url}}" loop style="width: 100%; height: 100%;"></video>-->
<view style="width: 100%; height: 100%; line-height: {{windowInfo.windowHeight}}px;">{{item.originalIndex}}</view>
</view>
<view wx:if="{{total > 0}}"
style="position: absolute; top: {{total * windowInfo.windowHeight}}px; width: 100%; height: 50px; line-height: 50px; text-align: center;">
<text wx:if="{{loading === 0}}">已经到底啦</text>
<text wx:else>加载中...</text>
</view>
</movable-view>
</movable-area>
/**index.wxss**/
.container {
width: 100%;
overflow: hidden;
.list-view {
position: relative;
.list-item {
position: absolute;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
}
}
}
欢迎大家留言讨论!!!