要实现小程序类似抖音上下滑动预览视频,就我了解的,方案可以分为两种
1、利用原生组件swiper(据说很卡,我也试过,好像是有点不流畅)
2、自己实现一个上下滑动效果(只需要监听一组事件,判断上拉还是下拉,接着就是移动dom)
这里就采用第二种方案自己实现 ps: (本案例基于字节小程序,由于字节已经实现原生组件同层渲染,所以这里不考虑组件层级问题,如果是其他平台,可能需要结合实际解决同层渲染问题,思路应该是一致的)
布局
笔者准备在视频列表外嵌套一个大盒子,这个大盒子就用于监听触摸事件(由于是自己实现上下滑动画,所以这个盒子高度应该是内容区域高度,且设置 overflow: hidden 避免出现盒子自己的滚动条);内层就是需要添加动画的盒子+视频列表。
抖音开发平台:tt.createVideoContext_小程序_抖音开放平台 (open-douyin.com)
index.wxml
<!-- 大盒子 --> <view class="video-box" bindtouchstart="onTouchStart" bindtouchmove="onTouchMove" bindtouchend="onTouchEnd"> <!-- 上滑滑动 动画盒子 --> <view class="ani-box" animation="{{animationData}}"> <!-- 视频列表 --> <view tt:for="{{videoList}}" :key="{{item.id}}" class="item-{{item.id}} item"> <video id="video-{{index}}" src="{{item.src}}" autoplay="{{false}}" loop="{{true}}" object-fit="fill" show-fullscreen-btn="{{false}}" vslide-gesture-in-fullscreen="{{false}}" /> </view> </view> </view>
index.js
let animation = null let viewHeight = 0 Page({ data: { videoList: [{ id: 1, src: 'xxx', }, { id: 2, src: 'xxx', }, { id: 3, src: 'xxxx', } ], oldId: -1, startPage: 0, animationData: {}, viewIndex: 0 }, onLoad: function () { this.getViewHeight() this.getVideoCtx(0) }, getVideoCtx(id) { // 有上一个 if (this.data.oldId > -1) { tt.createVideoContext("video-${this.data.oldId}").pause() } const ctx = tt.createVideoContext("video-${id}") // console.log(ctx) ctx.play() this.setData({ oldId: id }) }, // 触摸开始 onTouchStart({ touches }) { const { pageY } = touches[0] this.setData({ startPage: pageY }) // console.log('按下',pageY) }, // 触摸移动 onTouchMove({ touches }) { // const { pageY } = touches[0] // console.log('移动',pageY) }, // 触摸结束 onTouchEnd({ changedTouches }) { const { pageY } = changedTouches[0] const diff = pageY - this.data.startPage if (Math.abs(diff) <= 30) { console.log('不触发') return } if (diff > 0) { this.setAni(1) } else if (diff == 0) { this.setAni(0) } else { this.setAni(-1) } }, // 滑动动画 0 不移动 -1 上拉 1 下拉 async setAni(status) { if (status == 0) return false if (!animation) { animation = tt.createAnimation({ duration: 500, timingFunction: 'ease' }); } if (!viewHeight) { await this.getViewHeight() } // 计算位移 let moveY = 0 let nowIndex = this.data.viewIndex status > 0 ? nowIndex-- : nowIndex++ if (nowIndex < 0) { tt.showToast({ title: '到顶部了' }) return } if (nowIndex == this.data.videoList.length) { tt.showToast({ title: '到底了哦' }) return } moveY = -1 * nowIndex * viewHeight animation.translateY(moveY).step() this.getVideoCtx(nowIndex) this.setData({ animationData: animation.export(), viewIndex: nowIndex }) }, // 获取dom高度 getViewHeight() { return new Promise((resolve) => { const query = tt.createSelectorQuery() query.select(".item-1").boundingClientRect() query.exec(function (res) { if (res.length && res[0]) { viewHeight = res[0].height resolve(viewHeight) } }) }) }, })
index.wxss
.video-box { height: 100vh; overflow: hidden; position: relative; } .item { width: 100%; height: 100vh; } video { width: 100%; height: 100%; } .ani-box { position: absolute; top: 0; bottom: 0; left: 0; right: 0; transform: translateY(0px); z-index: -1; }
原文链接:https://blog.csdn.net/qq_45219069/article/details/124164517