IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 小程序模仿抖音上下滑动视频 -> 正文阅读

[移动开发]小程序模仿抖音上下滑动视频

前景知识

要实现小程序类似抖音上下滑动预览视频,就我了解的,方案可以分为两种
1、利用原生组件swiper(据说很卡,我也试过,好像是有点不流畅)
2、自己实现一个上下滑动效果(只需要监听一组事件,判断上拉还是下拉,接着就是移动dom)

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

先看效果:
在这里插入图片描述

布局

笔者准备在视频列表外嵌套一个大盒子,这个大盒子就用于监听触摸事件(由于是自己实现上下滑动画,所以这个盒子高度应该是内容区域高度,且设置 overflow: hidden 避免出现盒子自己的滚动条);内层就是需要添加动画的盒子+视频列表

<!-- 大盒子 -->
<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>

所以由上布局可以确定思路:在 video-box 上监听触摸事件,用 ani-box 控制上下滑动

触摸事件

原理就是记录下触摸开始位置+结束位置,两者y坐标差值,即可得知是上拉还是下拉(通常会有一个缓冲距离,这里设置为30,30以内不触发)

// 触摸开始
  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)
    }
  },

动画

上面知道了是上拉还是下拉,接下来就是滚动整个列表。其实滚动高度始终是 内容区域高度的整数倍(这里暂时不做像swiper那样,边触摸边移动的效果,而直接滑动后,直接滚动到下一个视频,所以是整数倍)

  // 获取内容高度高度
  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)
        }
      })
    })
  },
  // 动画实现
   moveY = -1 * nowIndex * viewHeight
   animation.translateY(moveY).step()
   this.getVideoCtx(nowIndex)
   this.setData({
     animationData: animation.export()
   })

如果对小程序动画api不熟悉的,可以去看下createAnimation。这里主要思路是:滚动总高度 = 内容高度 * 滚动到第几个 ;假设当是第一个视频,要滚动到第二个视频,则滚动高度 = 1 x 内容高度,要滚动到第三个视频则滚动高度 = 2 x 内容高度

完整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)
        }
      })
    })
  },
})

补充

动画:在做时踩了一个坑,添加动画的盒子需要设置初始css状态translateY = 0,导致一直没反应,这一点其实跟css过渡是一样的,整个动画应该是 0 --1 的过程,而不是 无 – 有 (好比 display none — block 添加过渡是无效的)
个人感觉 video标签 可以考虑始终使用一个,滑动只切换视频地址,或许这样性能会更好点(但是这样会导致滑动后,每一个视频都会重新加载,不会是上一次加载位置)

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 17:54:07  更:2022-04-18 17:58:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 21:33:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码