The applet imitates the vibrato and slides the video up and down

prospect knowledge

To achieve a small program similar to Douyin sliding up and down to preview videos, as far as I know, there are two solutions:
1. Use the native component swiper (it is said to be very stuck, I have tried it, it seems a bit not smooth)
2. Implement one by yourself Up and down sliding effect (only need to monitor a set of events, judge whether to pull up or pull down, and then move the dom)

Here is the second solution to implement ps by yourself: (This case is based on the Byte applet . Since Byte has already achieved the same layer rendering of native components , the issue of component hierarchy is not considered here . If it is another platform, it may need to be combined with actual solutions. Layer rendering problem, the idea should be the same)

First look at the effect:
insert image description here

layout

The author plans to nest a large box outside the video list , which is used to monitor touch events (since the up and down animation is realized by itself, the height of the box should be the height of the content area, and overflow: hidden is set to avoid the appearance of the box itself scroll bar ); the inner layer is the box + video list that needs to be animated

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

So the idea can be determined from the above layout: monitor touch events on the video-box, and use ani-box to control sliding up and down

touch event

The principle is to record the touch start position + end position, and the y-coordinate difference between the two, and then you can know whether it is a pull-up or pull-down (usually there is a buffer distance, here is set to 30, not triggering within 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)
    }
  },

animation

The above knows whether it is a pull-up or a pull-down, and the next step is to scroll the entire list. In fact, the scrolling height is always an integer multiple of the height of the content area (the effect of moving while touching is not done like swiper for the time being, but after sliding directly, it scrolls directly to the next video, so it is an integer multiple)

  // 获取内容高度高度
  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()
   })

If you are not familiar with the applet animation api, you can go to createAnimation . The main idea here is: total scroll height = content height * which number to scroll to; suppose it is the first video, and you want to scroll to the second video, then scroll height = 1 x content height, and you want to scroll to the third video Then scroll height = 2 x content height

full 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)
        }
      })
    })
  },
})

add some css

ps: This is added later (for reference only)

.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;
}

Replenish

Animation : I stepped on a pit when I was doing it. The box to add animation needs to set the initial css state translateY = 0 , resulting in no response. This is actually the same as the css transition. The entire animation should be a 0--1 process, and Not None – Yes (for example, display none — adding a transition to a block is invalid).
I personally feel that the video tag can always be considered to use one, and sliding only switches the video address. Maybe this will have better performance (but this will cause every video after sliding) will be reloaded, it will not be the last loaded position)

Guess you like

Origin blog.csdn.net/qq_45219069/article/details/124164517