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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
分页上拉加载下拉刷新是移动端开发中常见的功能,通常需要使用到第三方库来实现,比如React Native中的FlatList、ListView、SectionList等组件,这些组件都提供了相关的API来实现分页、上拉加载下拉刷新功能。 具体实现步骤如下: 1.在界面中添加FlatList、ListView、SectionList等组件,并设置相关属性如数据源、渲染函数等。 2.为组件添加onEndReachedThreshold和onEndReached事件,onEndReachedThreshold表示距离底部多少距离触发onEndReached事件,onEndReached事件表示到达底部时触发的回调函数,通常在回调函数中发起下一页数据的请求。 3.为组件添加refreshControl属性,并设置onRefresh事件,refreshControl表示下拉刷新的控件,onRefresh事件表示下拉刷新时触发的回调函数,通常在回调函数中重新请求第一页数据。 4.为组件添加ListFooterComponent属性,并设置上拉加载的控件,通常在请求下一页数据时显示上拉加载的控件,请求完毕后隐藏上拉加载的控件。 5.在请求数据时需要记录当前页码,并将请求的数据添加到数据源中,然后重新渲染组件即可。 示例代码如下(以React Native中的FlatList组件为例): ```javascript import React, { useState, useEffect } from 'react'; import { FlatList, View, Text, ActivityIndicator } from 'react-native'; const pageSize = 10; // 每页大小 const data = []; // 数据源 const renderItem = ({ item }) => { return ( <View> <Text>{item.title}</Text> </View> ); }; const renderFooter = () => { return ( <View style={{ alignItems: 'center', justifyContent: 'center', paddingVertical: 10 }}> <ActivityIndicator size="small" /> </View> ); }; const App = () => { const [page, setPage] = useState(1); // 当前页码 const [isLoading, setIsLoading] = useState(false); // 是否正在加载 useEffect(() => { fetchData(); }, [page]); const fetchData = async () => { setIsLoading(true); try { const response = await fetch(`https://api.example.com/list?page=${page}&pageSize=${pageSize}`); const result = await response.json(); data.push(...result.data); setIsLoading(false); } catch (error) { console.error(error); setIsLoading(false); } }; const handleRefresh = () => { setPage(1); }; const handleEndReached = () => { setPage(page + 1); }; return ( <FlatList data={data} renderItem={renderItem} ListFooterComponent={isLoading && renderFooter} onEndReachedThreshold={0.1} onEndReached={handleEndReached} refreshControl={<RefreshControl refreshing={isLoading} onRefresh={handleRefresh} />} /> ); }; export default App; ``` 以上代码中,App组件中的FlatList组件实现了分页上拉加载下拉刷新功能,pageSize表示每页大小,data表示数据源,renderItem表示渲染函数,renderFooter表示上拉加载的控件,page表示当前页码,isLoading表示是否正在加载,fetchData表示请求数据的函数,handleRefresh表示下拉刷新时触发的函数,handleEndReached表示到达底部时触发的函数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderlin_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值