基于React实现无限滚动的日历详细教程,附源码【手写日历教程第二篇】
前言
最常见的日历大部分都是滚动去加载更多的月份,而不是让用户手动点击按钮切换日历月份。滚动加载的交互方式对于用户而言是更加丝滑和舒适的,没有明显的操作割裂感。
那么现在需要做一个这样的无限滚动的日历,前端开发者应该如何去思考和代码实现呢?下面我会详细的介绍实现思路和步骤。
实现步骤
渲染单个月日历
如何对于如何实现单个月份的日历没有思路的同学需要先阅读一下这篇简单日历的实现教程https://blog.csdn.net/m0_37890289/article/details/132457676;通过阅读这篇教程可以实现单月的渲染。
尝试同时渲染多个月份
对于无限滚动的日历,其实就是把所有的月份都放在一个列表中,然后用户滚动这个列表就行了,也就是我们想要的丝滑的切换日历。但是实现的话肯定不能真的把所有的月份都提前准备好,而且谁能知道未来有多少月。
所以这里可以利用滚动加载的方式进行月份数据的加载和列表数据的组装,关于滚动加载,我之前也写过一篇教程,没有思路的同学可以参考一下。https://blog.csdn.net/m0_37890289/article/details/130774836
好,那么好,接下来我们就开始动手实现这个无限滚动的日历。首先我们先尝试渲染3个月,当前月,当前前一个月,当前后一个月。
import "./index.css";
import { getDaysOfMonth } from "../../utils";
import { useCallback, useMemo, useState } from "react";
import dayjs, { Dayjs } from "dayjs";
import "dayjs/locale/zh-cn";
dayjs.locale("zh-cn");function ScrollCalendar() {// 获取月的所有日期const getDays = useCallback((month: Dayjs) => {return getDaysOfMonth(month.year(), month.month() + 1);}, []);// 定义好整个列表const [schedules, setSchedules] = useState(() => {return [dayjs().subtract(1, "month"), dayjs(), dayjs().add(1, "month")].map(month => ({month,days: getDays(month),}),);});const weekTitles = useMemo(() => {return [...Array(7)].map((_, weekInx) => {return dayjs().day(weekInx);});}, []);return (<div className="App"><div className="calendar"><div className="calendar-title">{weekTitles.map(title => {return <div className="calendar-week">{title.format("dd")}</div>;})}</div>{schedules.map(schedule => {return (<div><div className="calendar-month"><div>{schedule.month.format("MMM YYYY")}</div></div><div className="calendar-content">{schedule.days.map(day => {return <div className="calendar-day">{day.format("DD")}</div>;})}</div></div>);})}</div></div>);
}export default ScrollCalendar;

虽然多个月份的日历列表是渲染出来,但是看起来很奇怪,由于我们之前为了让单个月份的数据看起来完成,我们将上个月和下个月的数据填充到当前月,让日历看起来比较完整。
但是如果同时渲染多个月份,还将相邻月份的日期拿来填充,这肯定是不合理的。那么我们应该数据源头进行调整。接下来我们调整一下获取某一个月内所有日期的工具方法getDaysOfMonth。
-
关键步骤
非当前月就填充null,要把位置占住,不然又要出现每个月的1号都是在第一列的情况。
import dayjs from "dayjs";export const getDaysOfMonth = (year: number, month: number) => {let firstDayOfMonth = dayjs(`${year}-${month}-1`);let lastDayOfMonth = dayjs(`${year}-${month + 1}-1`).subtract(1, "day");// 开始补全第一天前的日期while (firstDayOfMonth.day() !== 0) {firstDayOfMonth = firstDayOfMonth.subtract(1, "day");}// 开始补全最后一天后的日期while (lastDayOfMonth.day() !== 6) {lastDayOfMonth = lastDayOfMonth.add(1, "day");}const days = [];const currentMonth = dayjs(`${year}-${month}-1`);let tempDate = firstDayOfMonth;while (tempDate.isBefore(lastDayOfMonth) || tempDate.isSame(lastDayOfMonth)) { // 关键步骤,非当前月就填充null,要把位置占住,不然又要出现每个月的1号都是在第一列的情况**if (tempDate.isSame(currentMonth, "month")) {days.push(tempDate);} else {days.push(null);}**tempDate = tempDate.add(1, "day");}return days;
};

让日历列表滚动起来
上面我们已经成功将3个月份成功渲染出来了,接下来我们通过列表滚动加载的思路,分别监听列表滚动置顶和滚动触底这两个时机。
const calendarRef = useRef<HTMLDivElement>(null);useEffect(() => {let prevScrollHeight = 0;const scrollEvent = debounce(async e => {let scrollHeight = e.target.scrollHeight;let scrollTop = e.target.scrollTop;let offsetHeight = e.target.offsetHeight;if (scrollTop < 100) {console.log("列表置顶");setSchedules(schedules => {const lastSchedule = schedules[0];const prevMonth = lastSchedule.month.subtract(1, "month");const prevSchedule = {month: prevMonth,days: getDays(prevMonth),};return [prevSchedule, ...schedules];});const targetScrollTop =scrollTop + (scrollHeight - prevScrollHeight) + 50;calendarRef.current?.scrollTo({ top: targetScrollTop });// 记录前一个滚动高度prevScrollHeight = scrollHeight;}if (offsetHeight + scrollTop >= scrollHeight - 10) {console.log("列表触底,触发接口请求数据");setSchedules(schedules => {const lastSchedule = schedules[schedules.length - 1];const nextMonth = lastSchedule.month.add(1, "month");const nextSchedule = {month: nextMonth,days: getDays(nextMonth),};return [...schedules, nextSchedule];});}}, 100);calendarRef.current?.addEventListener("scroll", scrollEvent);return () => {if (calendarRef.current) {}};}, []);<divclassName="calendar"style={{height: "100vh",overflowY: "scroll",}}ref={calendarRef}>...
</div>

总结
本文实现了无限滚动的日历组件,满足了很大一部分需求的基础,有需要的同学可基于源码进行二次修改。有任何问题都可以进行留言,如何文章对你有帮忙,可以帮我点个赞🦉。源码链接https://github.com/levenx/react-calendar-training/tree/main/src/pages/scroll-calendar
感兴趣的可访问DEMO页面https://calendar.levenx.com/#/scroll-calendar
相关文章:
基于React实现无限滚动的日历详细教程,附源码【手写日历教程第二篇】
前言 最常见的日历大部分都是滚动去加载更多的月份,而不是让用户手动点击按钮切换日历月份。滚动加载的交互方式对于用户而言是更加丝滑和舒适的,没有明显的操作割裂感。 那么现在需要做一个这样的无限滚动的日历,前端开发者应该如何去思考…...
68、使用aws官方的demo和配置aws服务,进行视频流上传播放
基本思想:参考官方视频,进行了配置aws,测试了视频推流,rtsp和mp4格式的视频貌似有问题,待调研和解决 第一步:1) 进入aws的网站,然后进入ioT Core 2)先配置 Thing types & Thing,选择香港的节点,然后AWS ioT--->Manage---> Thing type 然后输入名字,创建Th…...
数据库
表 记录:行 字段(属性): 列 以行列的形式就组成了表(数据存储在表中) 关系数据库的表由记录组成,记录由字段组成,字段由字符或数字组成。它可以供各种用户共享, 具有最小冗余度和较高…...
深入了解fcntl函数:Linux系统编程中的文件控制
文章目录 概述介绍函数原型与参数 拓展:fcntl改文件属性总结 概述 摘要: fcntl函数是Linux系统编程中一个重要的函数,用于对文件描述符进行各种控制操作。本文将详细介绍fcntl函数的原型、各个参数的用法,以及阻塞和非阻塞模式切换的方法&am…...
汇川技术内推码
[庆祝]不一样的内推码[庆祝]:IVSM2R 投递了可以评论下名字,我会帮忙留意进度。 汇尔成川,共赴星海,欢迎加入,职等你来。 嵌入式软硬件,机器人算法,电机控制,通信软件,PLC…...
nacos服务器启动报错集合
报错1 Error creating bean with name ‘user‘: Unsatisfied dependency expressed through field ‘jwtTokenManage 开启鉴权之后,你可以自定义用于生成JWT令牌的密钥,application.properties中的配置信息为: ### Since 1.4.1, worked when…...
C语言_分支和循环语句(2)
文章目录 前言一、for 循环1.1语法1.2 for 语句的循环控制变量1.3 一些 for 循环的变种 二、do ... while()循环2.1 do 语句的语法2.2 do ... while 循环中的 break 和 continue2.3 练习1 **- 计算n的阶乘**2. - **在一个有序数组中查找具体的某个数字 n** 二分查找算法&#x…...
JMeter 接口自动化测试:从入门到精通的完全指南
JMeter 是一个开源的负载测试工具,它可以模拟多种协议和应用程序的负载,包括 HTTP、FTP、SMTP、JMS、SOAP 和 JDBC 等。在进行接口自动化测试时,使用 JMeter 可以帮助我们快速地构建测试用例,模拟多种场景,发现接口的性…...
【Java】集合List的toArray()方法及其重载
在Java中,集合(List 接口的实现类)提供了一个名为 toArray 的方法,用于将集合中的元素转换成数组。该方法有两个主要的重载形式,分别用于不同的情况。 toArray()重载方法1 <T> T[] toArray(T[] a)这个方法将集…...
Python学习笔记:Requests库安装、通过url下载文件
1.下载安装requests库 在pipy或者github下载,通常是个zip,解压缩后在路径输入cmd,并运行以下代码 Python setup.py install 安装完成后,输入python再输入import requests得到可以判断时候完成安装 2.通过url下载文件 使用的是u…...
git pull --rebase 用法
git pull --rebase git pull --rebase 是 Git 命令中的一个选项,它的作用是在从远程仓库拉取更新时使用 rebase 而不是默认的合并方式。使用这个命令会使您的提交历史更加整洁,因为它将您的本地提交在远程更新之前重新应用到新的提交之上。 这个命令的…...
react antd框架中的徽标获取数据对应状态的数量
实现思路:获取数量的思路是通过filter过滤符合数据来实现。 列表数组.filter(item > item.status 值).length; 例子:以下这个例子是判断data数组中的status中在职的数量。 data.filter((item) > item.status 在职).length 效果展示ÿ…...
【多线程】Thread类的用法
文章目录 1. Thread类的创建1.1 自己创建类继承Thread类1.2 实现Runnable接口1.3 使用匿名内部类创建Thread子类对象1.4 使用匿名内部类创建Runnable子类对象1.5 使用lambda创建 2. Thread常见的构造方法2.1 Thread()2.2 Thread(Runnable target)2.3 Thread(String name)2.4 Th…...
第八章 贪心算法 part03 1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果 (day34补)
本文章代码以c为例! 一、力扣第1005题:K 次取反后最大化的数组和 题目: 给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组: 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择…...
Android Activity启动过程一:从Intent到Activity创建
关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 目录 一、概览二、应用内启动源码流程 (startActivity)2.1 startActivit…...
第9章:聚类
聚类任务 性能度量 距离度量 非度量距离 原型聚类 有很好的统计学上的意义,但是只能找到椭球形的聚类。 密度聚类 层次聚类...
程序员为什么要写bug,不能一次性写好吗?
仅仅听到“Bug”这个词就会让你作为一个开发人员感到畏缩。我们相信,优秀的程序员是那些编写无错误代码的人。随着一些开发人员强调要成为一名零错误程序员,我们进行了更深刻的思考,并发现事实的准确性。 所有制作的软件都应该没有错误。对此…...
Nginx反向代理其他服务
Nginx反向代理 嘿,你的网络遇到了限制,不能直接通过服务的端口进行访问?别担心,我们可以借助Nginx这个超级英雄来解决这个问题!让我给你讲讲关于Nginx反向代理的故事吧。 首先,让我们明确一下反向代理的概…...
MQ 简介-RabbitMQ
一. MQ 简介 消息队列作为高并发系统的核心组件之一,能够帮助业务系统结构提升开发效率和系统 稳定性,消息队列主要具有以下特点: 削峰填谷:主要解决瞬时写压力大于应用服务能力导致消息丢失、系统奔溃等问题系统解耦:解决不同重要程度、不…...
强化学习(2)
强化学习(1) 1.多智能体深度强化学习重要性采样 多智能体深度强化学习(Multi-Agent Deep Reinforcement Learning,MADRL)是指在多智能体环境下使用深度强化学习算法进行协同学习。重要性采样(Importance Sampling)是…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
