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循环,通过索引取出原数据,数据通过下标值获取值效率是很高的。

猜你喜欢

转载自blog.csdn.net/lin_fightin/article/details/121258346