基于React实现日历组件详细教程

news2024/12/28 6:36:16

前言

日历组件是常见的日期时间相关的组件,围绕日历组件设计师做出过各种尝试,展示的形式也是五花八门。但是对于前端开发者来讲,主要我们能够掌握核心思路,不管多么奇葩的设计我们都能够把它做出来。

本文将详细分析如何渲染一个简单的日历组件。

步骤

计算每个月中具体包含的日期

因为日历需要把当前月的每一天都展示出来,展示的前提是我们能够知道当前月具体都有哪些日子。那么如何优雅的获取每个月所有的天呢?

为了能够更方便的操作时间,我们需要引入dayjs 工具库,这也是我们手写日历组件唯一需要的工具库。

npm install dayjs

接下来我们实现一个工具方法,方法的目的是当我们传入年、月,就会返回当前月份的所有天。

import dayjs from "dayjs";

export const getDaysOfMonth = (year: number, month: number) => {
  const firstDayOfMonth = dayjs(`${year}-${month}-1`);
  const lastDayOfMonth = dayjs(`${year}-${month + 1}-1`).subtract(1, "day");
  const days = [];

  let tempDate = firstDayOfMonth;
  while (tempDate.isBefore(lastDayOfMonth) || tempDate.isSame(lastDayOfMonth)) {
    days.push(tempDate);
    tempDate = tempDate.add(1, "day");
  }

  return days;
};

我们输出一下2023-08有哪些日子,并且简单渲染出来看看效果

function App() {
  const days = getDaysOfMonth(2023, 8);
	
	// 控制台打印
  days.forEach((day) => console.log(day.format("YYYY-MM-DD")));

  return (
    <div className="App">
      {days.map((day) => {
        return <div>
          {
            day.format('DD')
          }
        </div>;
      })}
    </div>
  );
}

在这里插入图片描述
在这里插入图片描述

以周为单位分组日期

  1. 首先我们先计算出日历分组标题,也就是周一,周二 … 周日
import dayjs from "dayjs";
import "dayjs/locale/zh-cn";
dayjs.locale("zh-cn");

const weekTitles = useMemo(() => {
    return [...Array(7)].map((_, weekInx) => {
      return dayjs().day(weekInx);
    });
  }, []);

// 日历标题渲染
<div className="calendar-title">
    {weekTitles.map((title) => {
         return <div>{title.format("ddd")}</div>;
    })}
</div>
  1. 对于当月所有的日期,每7天一组进行分组,也就是共分成7列
<div className="calendar-content">
    {days.map((day) => {
        return <div>{day.format("DD")}</div>;
    })}
</div>
  1. 加上对应的样式
.calendar {
  display: flex;
  flex-direction: column;
  width: 400px;
}

.calendar-title {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  padding-bottom: 8px;
}

.calendar-content {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}
  1. 看看效果
    在这里插入图片描述

猛的一看好像完成了,但是仔细检查会发现,2023年8月1号是周二,我们渲染出来的却是周日。这肯定是不对的,那么问题出在哪儿呢?

由于我们是通过grid布局来渲染日期数组,数组的第一位数据是8月1号,所以就成了上面图中的效果。所以我们得想办法将每个月1号前的日期也补全直到每周周日。

让我们改造一下获取日期的工具方法getDaysOfMonth ,下面代码是最终改造完成后的。

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 = [];
  let tempDate = firstDayOfMonth;
  while (tempDate.isBefore(lastDayOfMonth) || tempDate.isSame(lastDayOfMonth)) {
    days.push(tempDate);
    tempDate = tempDate.add(1, "day");
  }

  return days;
};

在这里插入图片描述

可以看出我们已经正确的渲染出了日历,只是样式看起来比较简陋。

日历支持切换月份

上面的结果是我固定渲染了2023年8月的日历,大多数的日历是需要支持月份切换的,甚至有的日历设计是需要支持用户上下滚动就能够显示对应的月份。我们先简单实现通过按钮点击支持日历月份的切换。

  1. 显示当前月份(日历顶部显示档期月份)
// tsx
<div className="calendar-month">
  <div className="calendar-month-switch">{"<"}</div>
  <div>{month.format("MMM YYYY")}</div>
  <div className="calendar-month-switch">{">"}</div>
</div>

// css
.calendar-month {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px;
}

.calendar-month-switch {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 24px;
  width: 24px;
  cursor: pointer;
}

简单看看效果

在这里插入图片描述

  1. 支持点击切换月份

从上图可以看到,月份两边有两个「箭头」按钮,接下来我们在这两个按钮上绑定事件,用来切换不同的月份

// 切换月份事件,-1 代表前一个月,1代表后一个月
const onMonthSwitch = (action: number) => {
    setMonth((month) => {
      return month.add(action, "month");
    });
  };

<div className="calendar-month">
    <div className="calendar-month-switch" onClick={()=> onMonthSwitch(-1)}
        >
        {"<"} </div> <div>{month.format("MMM YYYY")}</div>
    <div className="calendar-month-switch" onClick={()=> onMonthSwitch(1)}
        >
        {">"}
    </div>
</div>

在这里插入图片描述

小节总结

本文详细的记录了一个最简单的日历组件的实现过程,感兴趣但是之前还没有实现过日历的同学可以直接下载代码试试,希望对大家有所启发。也可以直接访问https://react-calendar-training.vercel.app/看成品效果。

由于设计师脑洞的千变万化,日历的展现形式也各不相同,后续我还将继续记录更多形式的日历实现过程,感兴趣的同学敬请期待。有任何问题请留言,如果对你有帮助,请帮忙点个赞🦉

相关链接

  1. 源码

    https://github.com/levenx/react-calendar-training

  2. 在线DEMO效果

    https://react-calendar-training.vercel.app

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/918569.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

windows系统activemq一键安装启动

前言 官网下载的mq提供了启动和卸载服务器的脚本&#xff0c;但是安装的时候不会自动启动服务&#xff0c;需要去手动比较麻烦&#xff0c;执行脚本的时候也需要去右键选择管理员执行做起来比较啰嗦。 优化方案 修改脚本权限为自动判断并获取 在脚本开头加入下面的代码 …

店铺收款系统开源_商城收款分账系统_OctShop

近来&#xff0c;很多客户对OctShop店铺收款分账系统有很大的需求&#xff0c;下面详细介绍一下&#xff0c;门店收款分账系统是什么&#xff0c;以及其作用与意义是什么&#xff1f; 店铺收款分账系统实质上是一个解决门店各种收款场景&#xff0c;如&#xff1a;扫码付款&…

零基础学习,初学者也能轻松制作高质量母婴行业小程序

现如今&#xff0c;随着移动互联网的发展&#xff0c;小程序成为了各行各业的新宠。对于母婴行业来说&#xff0c;拥有一个功能完善、用户友好的小程序&#xff0c;无疑是提升企业形象和服务质量的重要一环。然而&#xff0c;对于初学者来说&#xff0c;如何轻松掌握母婴行业小…

微服务中间件--http客户端Feign

http客户端Feign http客户端Feigna.Feign替代RestTemplateb.自定义Feign的配置c.Feign的性能优化d.Feign的最佳实践分析e.Feign实现最佳实践(方式二) http客户端Feign a.Feign替代RestTemplate 以前利用RestTemplate发起远程调用的代码&#xff1a; String url "http:…

【面试题】你理解中JS难理解的基本概念是什么?

前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 作用域与闭包 作用域 作用域是当前的执行上下文&#xff0c;值和表达式在其中“可见”或可被访问。如果一个变量或表达式不在当前的作用域中&#xff0…

SpringBoot利用ConstraintValidator实现自定义注解校验

一、前言 ConstraintValidator是Java Bean Validation&#xff08;JSR-303&#xff09;规范中的一个接口&#xff0c;用于实现自定义校验注解的校验逻辑。ConstraintValidator定义了两个泛型参数&#xff0c;分别是注解类型和被校验的值类型。在实现ConstraintValidator接口时&…

【业务功能篇77】微服务-OSS对象存储-上传下载图片

3. 图片管理 文件存储的几种方式 单体架构可以直接把图片存储在服务器中 但是在分布式环境下面直接存储在WEB服务器中的方式就不可取了&#xff0c;这时我们需要搭建独立的文件存储服务器。 3.1 开通阿里云服务 针对本系统中的相关的文件&#xff0c;图片&#xff0c;文本等…

JavaSE 数组

定义&#xff1a; int []arr; int arr[]; 初始化 // 完整格式 int arr[] new int[]{1, 2, 3}; // 简单格式 int arr[] {1, 2, 3}; 数组的元素访问、遍历 按照下标访问即可。数组的长度函数为 arr.length()。idea快速生成遍历的方法&#xff1a;数组名.fori 静态初始化 &a…

9.Sentinel哨兵

1.Sentinel Sentinel&#xff08;哨兵&#xff09;是由阿里开源的一款流量控制和熔断降级框架&#xff0c;用于保护分布式系统中的应用免受流量涌入、超载和故障的影响。它可以作为微服务架构中的一部分&#xff0c;用于保护服务不被异常流量冲垮&#xff0c;从而提高系统的稳定…

【神州数码】BGP路由器案例

SwitchB、SwitchC和SwitchD位于AS200中&#xff0c;SwitchA位于AS100中。SwitchA和SwitchB共享一个相同的网络段11.0.0.0。而SwitchB和SwitchD彼此物理上不相邻。 则SwitchA的配置如下&#xff1a; SwitchA(config)#router bgp 100SwitchA(config-router-bgp)#neighbor 11.1.1…

指针(初阶)

1. 指针是什么&#xff1f; 指针是什么&#xff1f; 指针理解的2个要点&#xff1a; 1. 指针是内存中一个最小单元的编号&#xff0c;也就是地址 2. 平时口语中说的指针&#xff0c;通常指的是指针变量&#xff0c;是用来存放内存地址的变量 总结&#xff1a;指针就是地址&…

Wlan——锐捷零漫游网络解决方案以及相关配置

目录 零漫游介绍 一代零漫游 二代单频率零漫游 二代双频率零漫游 锐捷零漫游方案总结 锐捷零漫游方案的配置 配置无线信号的信道 开启关闭5G零漫游 查看配置 零漫游介绍 普通的漫游和零漫游的区别 普通漫游 漫游是由一个AP到另一个AP或者一个射频卡到另一个射频卡的漫…

jquery实现单独使用laydate时间控件设置开始时间,结束时间最大最小值以及设置默认时分秒

因项目内 会话时间所用框架为layui 里面的laydate时间控件 具体的设置文档里面都有些 我所用的这个不是日期时间范围 而是单独的日期时间的控件 意思就是两个是单独的 但是需要设置的是 开始最大 时间为结束时间的最小值 结束最小时间为开始结束的最大值 其余不能点击 当我选择…

leetcode54. 螺旋矩阵(java)

螺旋矩阵 题目描述解题 收缩法 上期经典算法 题目描述 难度 - 中等 原题链接 - leecode 54 螺旋矩阵 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7…

潮湿对电子元器件有哪些影响?如何选择电子防潮柜?

随着科技的飞速发展&#xff0c;电子设备的应用越来越广泛&#xff0c;无论是生活、工业、农业领域随处可见各种各样的电子设备。电子设备在稳定的环境中是可以短时间存放的&#xff0c;但如果放置环境的湿度和温度发生较大改变&#xff0c;其性能会受到影响。电子设备受潮会有…

开学后运营校园跑腿小程序行不行?

校园跑腿小程序的运营是完全可行的&#xff0c;它为学生提供了便捷的校园代办服务。随着社会的发展和生活节奏的加快&#xff0c;越来越多的学生需要在学业之余处理个人事务&#xff0c;如购买日常用品、快递代取、打印复印文件等。传统的校园跑腿服务通常由个别学生或者组织提…

LeetCode3.无重复字符的最长子串

虽然是一道中等题&#xff0c;但我5分钟就写完了&#xff0c;而且是看完题就知道怎么写&#xff0c;这一看就知道双指针&#xff0c;一个左一个右&#xff0c;右指针往后移如果没有重复的长度1&#xff1b;如果有重复的&#xff0c;左指针往右移&#xff0c;那如何判断重复呢&a…

MyBatis相关知识

什么是MyBatis&#xff1f; MyBatis 是一个开源、轻量级的数据持久化框架&#xff0c;是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC&#xff0c;简化了加载驱动、创建连接、创建 statement 等繁杂的过程&#xff0c;开发者只需要关注 SQL 语句本身。 什么是持久…

SpringBoot案例-修改员工-查询回显

根据页面原型&#xff0c;明确需求 页面原型 需求 在员工信息栏的右侧存在一个编辑按钮&#xff0c;点击该按钮可以对员工信息进行修改&#xff0c;但是修改之前&#xff0c;会出现上述页面&#xff0c;将员工原有的信息进行展示回显。 阅读接口文档 接口文档的链接如下&am…

API接口文档利器:Swagger 和 接口调试利器:Postman

2.接口相关工具 2.1API接口文档利器&#xff1a;Swagger 2.1.1Swagger介绍 Swagger 是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务 (https://swagger.io/)。 它的主要作用是&#xff1a; 使得前后端分离开发更加方便&#xff0…