react 从0搭配react项目2上拉加载,下拉刷新,手撕虚拟列表

Posted lin-fighting

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react 从0搭配react项目2上拉加载,下拉刷新,手撕虚拟列表相关的知识,希望对你有一定的参考价值。

  • 声明非代码模块
  • 动画效果

声明非代码模块


在ts中,无法识别非代码资源。所以需要.d.ts文件来声明类型
项目编译过程中会自动读取.d.ts这种类型的文件,所以不需要我们手动去加载他们。
.d.ts文件也必须被ts编译,所以应该放置在tsconfig.json的includes里面。

动画效果


使用Transition去包裹。他会给你一个状态,表示进入中,进入后,退出中,退出后四个状态,让你可以根据四个状态定义不同的style。


一般正常只有显示和隐藏两个状态,这个组件可以给你提供四个状态。

正常步骤:
进来页面,点击,准备显示,先设置两种状态
exited退出后,{opacity: 0}, 立变为1, exiteing {opaity: 1},进入中
然后等一秒后:在变为extered {opacity: 1} 进入后
当准备隐藏的时候也是同样的道理,先变为1,在立马变为0,然后一秒后变为0。从extered变为exiting,在变为exited

实现上拉加载更多功能

思路:

给盒子固定高,设置overflow:auto;就会出现滚动条效果,然后通过判断盒子的卷去高度+可见高度是否》真实高度,来判断是否到达底部。

scollTop = 卷去的高度, offsetHeigth = 可视高度,scrollHeigth = 全部高度。
监听滚动事件。记录上一次的卷去的高度。


当上一次卷去的高度<此时卷去的高度,证明是向下滑动。当到底部的时候,再去请求更多的数据并且更新lastTop。
通过debounce延迟,防止多次触发。

实现下拉刷新功能。

思路:
实现一个hooks。通过监听某个div的touchStart事件,获取当前的pageY1。然后监听div的touchmove事件,获取当前的pageY2,将distance = pageY2-pageY1就是下拉的高度,当distance小于0的时候,证明是向上滑动,则不处理。然后设置div的position为relative,top值就是distance。再监听touchend事件,当手指离开的时候,使用requestAnimationFrame复原。
hooks代码:


touchStart用于记载手指开始的坐标,已经监听touchmove touchend事件。

touchMove主要记载滑动的距离以及让div滑动。

touchEnd主要是复原。当滑动距离大于30的时候,就触发回调函数。
整体代码:

const useDownRefresh = function <G = htmlElement>(
  cb: Function,
  takeElement = null
) {
  const boxRef = useRef<G>(null);
  useEffect(() => {
    takeElement = takeElement ? takeElement : boxRef.current;
    const element = boxRef.current as any;
    let startY: number; //开始时的纵坐标
    let distance: number; //本次下拉的距离
    let timer: NodeJS.Timer;
    const touchStart = (event: TouchEvent) => {
      startY = event.targetTouches[0].pageY;
      element && element.addEventListener("touchmove", touchMove);
      element && element.addEventListener("touchend", touchEnd);
    };
    const touchEnd = () => {
      if (distance > 30) {
        cb && cb();
      }
      const _back = () => {
        if (distance > 0) {
          requestAnimationFrame(_back);
          takeElement.style.top = `${--distance}px`;
        }
      };
      requestAnimationFrame(_back);
      element && element.removeEventListener("touchmove", touchMove);
      element && element.removeEventListener("touchend", touchEnd);
    };
    const touchMove = (event1: TouchEvent) => {
      distance = event1.targetTouches[0].pageY - startY;
      if (distance < 0) {
        return;
      }
      takeElement.style.top = `${distance}px`;
    };
    if (boxRef.current) {
      element && element.addEventListener("touchstart", touchStart);
    }
    return () => {
      element && element.removeEventListener("touchstart", touchStart);
    };
  }, [takeElement]);

  return { boxRef };
};


效果:

实现虚拟列表

思路:
比如可视区域10条,总的有20条,那么只渲染10条看得到的。为了避免白屏,会多处理两条,比如要展示5-15条,就渲染3-17条,避免白屏。最多只有14条会被渲染出来,其他的都不会渲染。
思路:
记录每个数据的高度,设置为absolute定位,给每个数据一个索引,他们的top就是对应的index*高度。先排好序,然后展示不同位置的数据就行了。
然后监听滚动事件。获取被卷去的个数,比如3,将他设置start。再获取现在内容展示的个数,比如4,设为end。那么应该截取数组的3-7个用来展示才对,但是还必须考虑白屏,所以就截取1-9个来展示。

监听滚动事件,或许需要截取的首尾,必须先知道每个数据的高度。然后截取展示就可以了。
可以看到,元素始终最多只有5个,通过预先设置了每个元素的top值,来撑开盒子,看起来就像是在滚动,但实际上没有现实的地方并没有渲染元素。
这就是虚拟滚动的原理。
后续如果数组过大,可以做成start-end的for循环,通过索引取出原数据,数据通过下标值获取值效率是很高的。

以上是关于react 从0搭配react项目2上拉加载,下拉刷新,手撕虚拟列表的主要内容,如果未能解决你的问题,请参考以下文章

React-native ScrollView 上拉加载和下拉刷新

react-native使用flatlist上拉加载下拉刷新

RN-第三方之react-native-pull 下拉刷新上拉加载

react-native ListView 封装 实现 下拉刷新/上拉加载更多

手机端效果实现下拉刷新上拉加载更多数据---自定义数据篇

手机端效果实现下拉刷新上拉加载更多数据---请求数据篇