你好,我是小白Coding日志,一个热爱技术的程序员。在这里,我分享自己在编程和技术世界中的学习心得和体会。希望我的文章能够给你带来一些灵感和帮助。欢迎来到我的博客,一起在技术的世界里探索前行吧!
前言
现在市面上的组件库大多包含日历组件,那么它们具体是如何实现的呢?其实原理很简单,主要是通过使用Date对象的相关API。今天我们就来开发一个迷你版的日历组件。
功能分析
实现一个迷你版的日历组件页面布局:
- Header部分:最中间的位置用于显示当前月份,左右两边分别设置切换上月和下月的按钮。
- 星期显示:Header的下方依次显示星期日到星期六。
- Body部分:显示当前月的天数。可以根据当前月份获取这个月有多少天,以及根据当前月1号获取是星期几,这样我们就能知道这个月从哪一天开始。
为日历组件提供默认值,即当前日历组件默认渲染为当前年份和月份。此外,还需提供一个onChange
方法,用于返回当前选中的日期。这样,一个基本的日历组件就完成了。
项目搭建
使用vite来创建一个React
项目
npm create vite@latest calendar-mini
选择React
然后选择JavaScript
切换到项目目录下cd calendar-mini
,运行npm install
安装依赖,npm run dev
把项目跑起来
编码
- 页面基本布局
删除App.jsx中App组件中的HTML
内容只保留下h1
标签,
import { useState } from 'react'
import './App.css'function App() {const [count, setCount] = useState(0)return (<><h1>mini日历组件📅</h1></>)
}export default App
新增一个Calendar组并提供基本样式,在App.jsx中导入组件
import React from 'react';function Calendar() {return (<div className="calendar"><div className="header"><button><</button><div>2023 年 7 月</div><button>></button></div><div className="days"><div className="day">日</div><div className="day">一</div><div className="day">二</div><div className="day">三</div><div className="day">四</div><div className="day">五</div><div className="day">六</div><div className="empty"></div><div className="empty"></div><div className="day">1</div><div className="day">2</div><div className="day">3</div><div className="day">4</div><div className="day">5</div><div className="day">6</div><div className="day">7</div><div className="day">8</div><div className="day">9</div><div className="day">10</div><div className="day">11</div><div className="day">12</div><div className="day">13</div><div className="day">14</div><div className="day">15</div><div className="day">16</div><div className="day">17</div><div className="day">18</div><div className="day">19</div><div className="day">20</div><div className="day">21</div><div className="day">22</div><div className="day">23</div><div className="day">24</div><div className="day">25</div><div className="day">26</div><div className="day">27</div><div className="day">28</div><div className="day">29</div><div className="day">30</div><div className="day">31</div></div></div>);
}export default Calendar;
.calendar {border: 1px solid #aaa;padding: 10px;width: 300px;height: 250px;
}.header {display: flex;justify-content: space-between;align-items: center;height: 40px;
}.days {display: flex;flex-wrap: wrap;
}.empty, .day {width: calc(100% / 7);text-align: center;line-height: 30px;
}.day:hover {background-color: #ccc;cursor: pointer;
}
.day:hover, .selected {background-color: #22a4f1;cursor: pointer;
}
.m-10 {margin: 10px 0;
}
import { useState } from 'react'
import './App.css'
import Calendar from './component/Calendar'
function App() {return (<><h1>mini日历组件📅</h1><Calendar /></>)
}export default App
来看看效果👇
- 根据当前月份获取上月下月
结合useState
来获取日历组件的当前年份和月份,月份要加1因为月份是从0开始的
根据当前月获取上月下月
// 上月 const lastMonth = () =>{setDate(new Date(date.getFullYear(), date.getMonth() - 1, 1))}// 下月const nextMonth = () =>{setDate(new Date(date.getFullYear(), date.getMonth() + 1, 1))}
- 根据当前月份来渲染日期
const renderDays = () => {const days = []const daysCount = daysOfMonth(date.getFullYear(), date.getMonth())const firstDays = firstDayOfMonth(date.getFullYear(), date.getMonth())for(let i = 0; i<firstDays; i++){days.push(<div key={`empty-${i}`} className="empty"></div>)}for(let i = 1; i<=daysCount; i++){const handelClick = () =>{const current = new Date(date.getFullYear(), date.getMonth(), i)setDate(current)props.onChange(current)}if(i === date.getDate()){days.push(<div key={i} className="day selected" onClick={()=>handelClick()}>{i}</div>)}else{days.push(<div key={i} className="day" onClick={()=>handelClick()}>{i}</div>)}}return days}
定义一个renderDays函数用来渲染日期,提供一个days数组来动态的渲染日期,daysCount是当前月的天数,firstDays是当前月是从星期几开始的,先循环firstDays来添加空白项,再循环daysCount添加天,handelClick当点击某一天的时候选中并改变背景色。
- 添加默认值defaultValue和onChange方法
为Calendar
组件提供默认值
为state设置默认值
为Calendar
组件提供onChange方法,在点击某一天的时候调用onChange
回调将最新的值传递给onChange方法
Calendar.jsx组件全部代码
import { useState } from 'react';function Calendar(props) {const [date, setDate] = useState(new Date(props.defaultValue))console.log(date.toLocaleDateString())// 上月 const lastMonth = () =>{setDate(new Date(date.getFullYear(), date.getMonth() - 1, 1))}// 下月const nextMonth = () =>{setDate(new Date(date.getFullYear(), date.getMonth() + 1, 1))}
// 获取每个月有多少天
const daysOfMonth = (year, month) =>{return new Date(year, month + 1, 0).getDate()
}
// 获取每个月第一天是星期几
const firstDayOfMonth = (year, month) =>{return new Date(year, month, 1).getDay()
}
const renderDays = () => {const days = []const daysCount = daysOfMonth(date.getFullYear(), date.getMonth())const firstDays = firstDayOfMonth(date.getFullYear(), date.getMonth())for(let i = 0; i<firstDays; i++){days.push(<div key={`empty-${i}`} className="empty"></div>)}for(let i = 1; i<=daysCount; i++){const handelClick = () =>{const current = new Date(date.getFullYear(), date.getMonth(), i)setDate(current)props.onChange(current)}if(i === date.getDate()){days.push(<div key={i} className="day selected" onClick={()=>handelClick()}>{i}</div>)}else{days.push(<div key={i} className="day" onClick={()=>handelClick()}>{i}</div>)}}return days
}return (<div className="calendar"><div className="header"><button onClick={lastMonth}><</button><div>{ date.getFullYear() } 年 { date.getMonth() + 1 } 月</div><button onClick={nextMonth}>></button></div><div className="days"><div className="day">日</div><div className="day">一</div><div className="day">二</div><div className="day">三</div><div className="day">四</div><div className="day">五</div><div className="day">六</div>{renderDays()}</div></div>);
}export default Calendar;
App.jsx代码
import { useState } from 'react'
import './App.css'
import Calendar from './component/Calendar'
function App() {return (<><h1>mini日历组件📅</h1><Calendar defaultValue={'2024-07-19'} onChange={(date)=>{alert(date.toLocaleDateString())}} /><Calendar defaultValue={'2024-10-04'} /</>)
}export default App
以上就实现了一个mini版的日历组件😁
最后还是那句话:即使代码逻辑很简单,也值得记录下来。我的编程目标是避免重复造轮子!😊
如果觉得有用,就给我点个赞吧😁
探索更多有趣知识,欢迎关注我的微信公众号:小白Coding日志
,每天分享精彩内容,与你一同探寻知识的边界。一起开启知识新旅程!🚀📚
关注我的技术博客,探索前沿科技与实用开发技巧。一起携手走向代码的精彩世界!🚀💻 不错过每一篇精彩!
https://www.xiaobaicoding.com