在前端工作中,日历应该还是比较常见的,虽然不是特别的难但还是有点算法在里面的,想不通就做不出来。上面这个案例完全是手工写的,所以样式可能看着有点不太好看,但是我的审美也就到这个地步了。嘿嘿!
看到这个案例,脑子中应该立马想到 九九乘法表 那个案例的写法。九九乘法表的案例写在了这篇文章的最后,React 语法基本使用介绍。核心思想就是二维数组。
每个日历页面都是六行七列,分别显示着上个月,这个月,和下个月的日期。转换成代码就是一个大数组,里面包含四十二个元素。接下来该探讨怎么求得分别对应日期。
我 react 代码对应的目录结构
Calendar 是 APP 的组件,APP是 main 的组件,main.js 打包放入 index.html。
在main.js 的变量存储区创建时间得到当前的年份和月份。
this.state = {
year : (new Date()).getFullYear(),
// 月份是从零开始的所以加一
month : (new Date()).getMonth() + 1
}
在 calendar.js 里面构建业务,现在先让 四十二 个数字进去构建一个日历页面。
//一个大数组存放四十二个元素
var createDateArr = [];
// 上月的天数
const predays = new Date(this.props.year,this.props.month - 1,0).getDate();
// 本月的天数
const nowdays = new Date(this.props.year,this.props.month,0).getDate();
// 本月的第一天是星期几,是几就留上个月几天在日历上
const nowweek = new Date(this.props.year,this.props.month - 1,1).getDay();
// 根据本月第一天星期几计算上个月末尾有几天在本月开头的显示
let count = nowweek;
//当count == 0 时,也就是上个月没有天数放在当前页面,这时下个月会出现十几天不美观
// 所以我们进行调整,让上个月占满第一行
if(count == 0)count = 7;
while(count){
count--;
createDateArr.push({
// 根据月份查出上个月是不是还是这一年不是就减一,这里的年份主要给农历用
year : this.props.month > 1 ? this.props.year : this.props.year - 1,
month : this.props.month > 1 ? this.props.month - 1 : 12,
day : predays - count
});
}
// 把本月的天数在放进数组
for(let i = 1;i <= nowdays;i++){
createDateArr.push({
year : this.props.year,
month : this.props.month,
day : i
});
}
上面的算法思想就是先算出本月第一天星期几,星期几对应的数字就是上个月有几天展现出来。例如今天 2019 年 7 月 1 号 星期一,那上个月就留一天。因为后来还的添加农历,所以必须的记住上个月的年份,就像一月份的上个月是去年的十二月,这点的注意。
然后把本月的天数在放进数组,这时就剩下下个月开头的天数了。补齐四十二天,同时也要注意年份和月份的问题。
// 日历一共42天不够补齐下个月开头作为本月的结尾
let k = 1;
while(createDateArr.length!=42){
createDateArr.push({
year : this.props.month < 12 ? this.props.year : this.props.year + 1,
month : this.props.month < 12 ? this.props.month + 1 : 0,
day : k++
});
}
核心的代码就是上面创建一个大数组的过程,接下来把大数组和表格标签结合在一起,有点二维数组的思想,来创建表格放进数据。
const dateArr = ()=>{
var arr = [];
for(let i = 0;i < 6;i++){
let temp = [];
for(let j = 0;j < 7;j++){
// 引入动态类,来判断当天这一天来加个背景色
temp.push(<td key={j} className = {classnames({
bgc:(new Date()).getFullYear() == createDateArr[i * 7 + j].year && (new Date()).getMonth()+1 == createDateArr[i * 7 + j].month && (new Date()).getDate() == createDateArr[i * 7 + j].day? 1 : 0
})}>{
(()=>{
let o = createDateArr[i * 7 + j];
let y = o.year;
let m = o.month;
let d = o.day;
let lunar = solarLunar.solar2lunar(y,m,d).dayCn;
let term = solarLunar.solar2lunar(y,m,d).term;
// 如果有节气就显示节气,否则就显示阴历
return <div><h4>{o.day}</h4><p className={classnames({bg:term})}>{term ? term : lunar}</p></div>
})()
// IIFE 提取<h4>{createDateArr[i * 7 + j].day}</h4>{solarLunar.solar2lunar(createDateArr[i * 7 + j].year, createDateArr[i * 7 + j].month, createDateArr[i * 7 + j].day).dayCn}
}</td>)
}
arr.push(<tr key={i}>{temp}</tr>);
}
return arr;
}
基本上就能实现了,这时的在 APP.js 里面进行事件绑定,达到动态更改日历的效果,至于农历实现就要借入 npm 社区的 solarlunar 这个阴阳历转换了,很简单 npm 安装,引包在表格的 td 这个标签处实现。其他的都是边边角的问题了。不会的话请看下方我的源代码。
源代码数据:
App.js 文件:
import React,{Component} from "react";
import Calendar from "./calendar/Calendar";
export default class App extends Component {
constructor(){
super();
this.state = {
year : (new Date()).getFullYear(),
// 月份是从零开始的所以加一
month : (new Date()).getMonth() + 1
}
}
changeYear(e){
this.setState({
year : parseInt(e.target.value)
})
}
changeMonth(e){
this.setState({
month : parseInt(e.target.value)
})
console.log(parseInt(e.target.value))
}
render(){
var selectYearArr = [];
for(let i = 1900;i < 2100;i++){
selectYearArr.push(i);
}
var selectMonthArr = [1,2,3,4,5,6,7,8,9,10,11,12];
return (
<div>
<main>
<span>
<select value = {`${this.state.year} 年`} onChange = {(event)=>{
this.changeYear(event)
}}>
{selectYearArr.map((item,index)=><option key={index}>{item} 年</option>)}
</select>
</span>
<span>
<select value = {`${this.state.month} 月`} onChange = {(event)=>{
this.changeMonth(event)
}}>
{selectMonthArr.map((item,index)=><option key={index}>{item} 月</option>)}
</select>
</span>
<Calendar year = {this.state.year} month = {this.state.month}></Calendar>
</main>
</div>
)
}
}
calendar.js文件
import React,{Component} from "react";
import solarLunar from 'solarLunar';
import classnames from "classnames";
export default class Calendar extends Component{
constructor(){
super();
}
render(){
//一个大数组存放四十二个元素
var createDateArr = [];
// 上月的天数
const predays = new Date(this.props.year,this.props.month - 1,0).getDate();
// 本月的天数
const nowdays = new Date(this.props.year,this.props.month,0).getDate();
// 本月的第一天是星期几,是几就留上个月几天在日历上
const nowweek = new Date(this.props.year,this.props.month - 1,1).getDay();
// 根据本月第一天星期几计算上个月末尾有几天在本月开头的显示
let count = nowweek;
//当count == 0 时,也就是上个月没有天数放在当前页面,这时下个月会出现十几天不美观
// 所以我们进行调整,让上个月占满第一行
if(count == 0)count = 7;
while(count){
count--;
createDateArr.push({
// 根据月份查出上个月是不是还是这一年不是就减一,这里的年份主要给农历用
year : this.props.month > 1 ? this.props.year : this.props.year - 1,
month : this.props.month > 1 ? this.props.month - 1 : 12,
day : predays - count
});
}
// 把本月的天数在放进数组
for(let i = 1;i <= nowdays;i++){
createDateArr.push({
year : this.props.year,
month : this.props.month,
day : i
});
}
// 日历一共42天不够补齐下个月开头作为本月的结尾
let k = 1;
while(createDateArr.length!=42){
createDateArr.push({
year : this.props.month < 12 ? this.props.year : this.props.year + 1,
month : this.props.month < 12 ? this.props.month + 1 : 0,
day : k++
});
}
const dateArr = ()=>{
var arr = [];
for(let i = 0;i < 6;i++){
let temp = [];
for(let j = 0;j < 7;j++){
// 引入动态类,来判断当天这一天来加个
temp.push(<td key={j} className = {classnames({
bgc:(new Date()).getFullYear() == createDateArr[i * 7 + j].year && (new Date()).getMonth()+1 == createDateArr[i * 7 + j].month && (new Date()).getDate() == createDateArr[i * 7 + j].day? 1 : 0
})}>{
(()=>{
let o = createDateArr[i * 7 + j];
let y = o.year;
let m = o.month;
let d = o.day;
let lunar = solarLunar.solar2lunar(y,m,d).dayCn;
let term = solarLunar.solar2lunar(y,m,d).term;
// 如果有节气就显示节气,否则就显示阴历
return <div><h4>{o.day}</h4><p className={classnames({bg:term})}>{term ? term : lunar}</p></div>
})()
// IIFE 提取<h4>{createDateArr[i * 7 + j].day}</h4>{solarLunar.solar2lunar(createDateArr[i * 7 + j].year, createDateArr[i * 7 + j].month, createDateArr[i * 7 + j].day).dayCn}
}</td>)
}
arr.push(<tr key={i}>{temp}</tr>);
}
return arr;
}
// 日历的头部
var headArr = ["日","一","二","三","四","五","六"];
return (
<div>
<table>
<thead>
<tr>
{headArr.map((item,index)=><td key = {index}>{item}</td>)}
</tr>
</thead>
<tbody>
{dateArr()}
</tbody>
</table>
</div>
)
}
}
样式:less.less
body{
*{
margin:0;
padding:0;
}
@bg:background-color;
@{bg}:#f6f6f6;
main{
margin:30px auto;
width: 424px;
background-color:#5eaeff;
}
span {
display:inline-block;
padding:20px;
}
span:nth-child(2)::after{
content:"中华万年历";
color: #fff;
padding-left: 40px;
font-size: 20px;
}
select {
width: 70px;
height: 20px;
border:1px solid #909;
vertical-align: bottom;
}
table{
border-collapse: collapse;
background-color:white;
border: 2px solid #5aacff;
cursor:pointer;
tbody td {
height: 60px;
width: 60px;
font-size:14px;
vertical-align:top;
color: #999;
text-align:center;
}
tbody tr {
border-top: 1px solid #c8cacc;
}
thead td{
text-align:center;
height: 40px;
border-bottom: 1px solid #5af;
}
h4{
color:#090;
padding:6px 0;
}
.bg{
color:#fb0;
}
.bgc{
background-color:#0f0;
}
}
}
网友评论