视频滑动切换组件

适用于微信小程序的视频滑动切换组件

以前用过小程序提供的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;
    }
  }
}

欢迎大家留言讨论!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值