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 下拉刷新上拉加载