小程序模仿抖音上下滑动视频

微信   2024-12-28 10:16   73   0  

要实现小程序类似抖音上下滑动预览视频,就我了解的,方案可以分为两种

1、利用原生组件swiper(据说很卡,我也试过,好像是有点不流畅)

2、自己实现一个上下滑动效果(只需要监听一组事件,判断上拉还是下拉,接着就是移动dom)

这里就采用第二种方案自己实现 ps: (本案例基于字节小程序,由于字节已经实现原生组件同层渲染,所以这里不考虑组件层级问题,如果是其他平台,可能需要结合实际解决同层渲染问题,思路应该是一致的)

8254_jdnz_9495.gif

布局

笔者准备在视频列表外嵌套一个大盒子,这个大盒子就用于监听触摸事件(由于是自己实现上下滑动画,所以这个盒子高度应该是内容区域高度,且设置 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






博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。