React仿钉钉日历组件
React仿钉钉日历组件 支持左右滑动事件,切换月历和周历的左右滑动切换
##不喜欢默迹,大家应该可以直接看懂,如果看不明白,可以私下找我交流
看视频
H5高仿钉钉日历
/**
* @author jeastStone;
* @description 日历组件
*
*/
import { Icon } from 'antd-mobile';
import { setTimeout } from 'core-js';
import React, { Component } from 'react';
import {CalendarUtil} from './calendar';
import './index.less'
import { GetCNDate } from "@/components/smart/du/Week";
import { active } from 'sortablejs';
import Angle from "./angle";
class Calendar extends Component{
constructor(props, context) {
super(props, context);
this.state={
//划动X坐标
startX:undefined,
startY:undefined,
//划动结束点坐标
endX:undefined,
endY:undefined,
//当前或参数日期
playDecoration:null,
//是历类型以 true:月,false:周
calendarType:false,
//是否开始划动
transFlag:false,
//参数日期
current:new Date(),
//选中的日期
activeDate:new Date(),
//第几周
activeWeekNo:undefined,
activeIndex:1,
};
}
componentWillMount() {
}
componentDidMount() {
}
componentWillReceiveProps(nextProps) {
}
/**
*
* @param {*} current
* 日期格式简单格式化
*/
getFormatDate(current)
{
current=new Date(current);
let currentYear=current.getFullYear();
let currentMonth=current.getMonth();
let currentDay=current.getDate();
return {year:currentYear,month:currentMonth,day:currentDay};
}
/**
*
* @param {
* } dateTime
*
* @out 输出 年月日转换
*
*/
getFormatDateTime(dateTime)
{
dateTime=new Date(dateTime);
let currentYear=dateTime.getFullYear();
let currentMonth=dateTime.getMonth();
let currentDay=dateTime.getDate();
// return `${currentYear}-${currentMonth+1}-${currentDay}`;
return {year:currentYear,month:currentMonth,day:currentDay}
}
/**
* 是否同年同月
* @param {Date} d1 Date
* @param {Date} d2
* return bool
*/
checkIsSameMonth=(d1,d2)=>
{
d1=new Date(d1);
d2=new Date(d2);
const date1Str=`${d1.getFullYear()}-${d1.getMonth()}`;
const date2Str=`${d2.getFullYear()}-${d2.getMonth()}`;
return date1Str===date2Str;
}
/**
* 选中日历事件
* @param {*} date
* @param {*} weekNo
* 当前日是历以及当前所在行号
*/
changeDateHandle=(e,date,weekNo)=>
{
e.stopPropagation();
this.state.activeDate=new Date(date.year,date.month,date.day);
this.state.activeWeekNo=weekNo;
if(!this.checkIsSameMonth(this.state.current,this.state.activeDate))
{
this.state.current=this.state.activeDate;
}
this.setState({
activeDate: new Date(date.year,date.month,date.day),
activeWeekNo:weekNo
},()=>{console.log(this.state)});
}
/**
*
* @param {*} date
* date:2020-01-11
*/
createCalendarDate(date=new Date())
{
let paramDate=this.getFormatDate(date);
let preDate=this.getFormatDate(new Date(paramDate.year,paramDate.month-1));
let nextDate=this.getFormatDate(new Date(paramDate.year,paramDate.month+1));
let nextMonth=this.createDate(nextDate);
let preMonth=this.createDate(preDate);
let currenMonth=this.createDate(paramDate);
return [preMonth,currenMonth,nextMonth];
}
createDate(date)
{
let currentInfo=date;
let FstWeekDay=new Date(currentInfo.year,currentInfo.month,1).getDay();
const RowsLen=6;//最大行数
const ColsLen=7;//最大列数
let dateArray=[];
let dateList=[];
let j=0;
let fistNo=0;
for(let i=0; i<RowsLen*ColsLen; i++)
{
if(i%ColsLen==0 && i!=0)
{
j++;
}
if(!dateArray[j] )
{
dateArray[j]=[];
}
if(i<FstWeekDay)
{
fistNo=0;
}else{
fistNo++;
}
let myDate;
if(i<FstWeekDay)
{
myDate=this.getFormatDateTime(new Date(currentInfo.year,currentInfo.month,1-(FstWeekDay-i)));
}else{
myDate=this.getFormatDateTime(new Date(currentInfo.year,currentInfo.month,fistNo))
}
dateList.push(myDate);
dateArray[j].push(myDate);
}
return {dateList,dateArray};
}
/**
* 初化始化日历
* @param {
* } currentDate
*
*
*/
initDateCanledar=(currentDate,activeDate)=>
{
let currentInfo=this.getFormatDate(currentDate);
let currentTotalDay=CalendarUtil.getMonthDay(currentInfo.year,currentInfo.month);
let activeInfo=this.getFormatDate(activeDate);
let FstWeekDay=new Date(currentInfo.year,currentInfo.month,1).getDay();
const RowsLen=6;//最大行数
const ColsLen=7;//最大列数
let dateArray=[];
let dateList=[];
let j=0;
let fistNo=0;
for(let i=0; i<RowsLen*ColsLen; i++)
{
if(i%ColsLen==0 && i!=0)
{
j++;
}
if(!dateArray[j] )
{
dateArray[j]=[];
}
if(i<FstWeekDay)
{
fistNo=0;
}else{
fistNo++;
}
let myDate;
if(i<FstWeekDay)
{
myDate=this.getFormatDateTime(new Date(currentInfo.year,currentInfo.month,1-(FstWeekDay-i)));
}else{
myDate=this.getFormatDateTime(new Date(currentInfo.year,currentInfo.month,fistNo))
}
dateArray[j].push(myDate);
const formatDate=`${myDate.year}-${myDate.month}-${myDate.day}`;
dateList.push(formatDate);
}
return {dateArray,dateList};
}
/**
* 创建日历View层
* @param {*} currentDate
* @param {*} activeDate
*/
createCalendar(currentDate,activeDate)
{
const dateArray=currentDate.dateArray;
return dateArray.map((item,index)=>{
return <div className="cols" key={`week-${index}`} >
{
item.map((child)=>{
let itemDay=`${child.year}-${child.month+1}-${child.day}`;
const chinaYear=GetCNDate(new Date(child.year,child.month+1,child.day));
let active=this.getFormatDateTime(activeDate?activeDate:new Date());
let today=this.getFormatDateTime(new Date());
let activeClass=(itemDay==`${active.year}-${active.month+1}-${active.day}`)?'activeDate':'';
let isTodayClass=(itemDay==`${today.year}-${today.month+1}-${today.day}`)?'todayClass':'';
return <div className={`dayInfo ${isTodayClass} ${activeClass}`} key={itemDay} onClick={(e)=>{
this.changeDateHandle(e,child,index)
}}>
<span className="dayNo"> {child.day}</span>
<span className="description"> {chinaYear}</span>
</div>
})
}
</div>
})
}
/**
* @copyright jeastStone
* 创建Head星期层
*/
createWeekHead()
{
const weekList=['日','一','二','三','四','五','六'];
return weekList.map((item,index)=>{
return <span key={`head-${index}`}>{item}</span>
})
}
/**
* 当前月分View
* @param {*} dateInfo
*/
createMonth(dateInfo)
{
let currentDate=this.getFormatDate(dateInfo);
return <React.Fragment>
<span>{currentDate.month+1}</span>
</React.Fragment>
}
/**
* 划动事件开始
* 记录起始点坐标
* @param {*} e
*/
startHandle=(e)=>{
this.state.startX=e.touches[0].screenX;
this.state.startY=e.touches[0].screenY;
}
/**
* 划动进行中,便于动画处理
* @param {*} e
* @param {*} calendarType
*/
moveHandle=(e)=>{
const {startX,startY,endX,endY}=this.state;
this.state.transFlag=true;
let result=e.touches[0].screenX-startX>e.touches[0].screenX/3;
//如果距离大于100则切换
let isDragFlag=Math.abs(startX-e.touches[0].screenX)>e.touches[0].screenX/3;
let isDragUpFlag=Math.abs(startY-e.touches[0].screenY)>50;
let showWeekResult=e.touches[0].screenY-startY>50;
let decoration="";
// let newAgle =new Angle();
// let roateAngle=newAgle.getAngle(this.state.startX,this.state.startY,e.touches[0].screenX,e.touches[0].screenY);
// let newDecoration=0;
if(!isDragFlag)
{
return;
}
// //小于45度为纵向,大于45多为横向
// if(roateAngle<45)
// {
// newDecoration=1;
// }else{
// newDecoration=2;
// }
if(result)
{
decoration='transRight';
}else{
decoration='transLeft';
}
this.state.playDecoration=decoration;
}
endHandle=(e)=>
{
const {transFlag,playDecoration,current,calendarType,activeDate}=this.state;
if (!transFlag || playDecoration==='') {
return
}
let currentDate=this.getFormatDate(current);
let resultMonth=playDecoration=='transRight'?-1:1;
let myCurrent=new Date(currentDate.year,currentDate.month+resultMonth,currentDate.day);
let myActiveDate=current;
if(!calendarType )
{
//周
let newActiveDate=this.getFormatDate(activeDate?activeDate:current);
myCurrent=new Date(newActiveDate.year,newActiveDate.month,newActiveDate.day+7);
myActiveDate=new Date(newActiveDate.year,newActiveDate.month,newActiveDate.day+7);
}
this.setState({
startX:0,
transFlag:false,
current:myCurrent,
activeDate:myActiveDate,
})
}
/**
*
* @param {*} type
* 周月历显示切换
*/
showType(type)
{
this.setState({
calendarType:!type
})
}
/**
* 获取当前日历所在的行号
* @param {Date} activeDate 选中的日期
* @param {Date} current 当前参数日期,默日值为当天
*/
getWeekNo=(activeDate,current,dateList)=>{
activeDate=activeDate?activeDate:current;
const listDate=dateList||this.initDateCanledar(activeDate).dateList;
let activeFd=this.getFormatDate(activeDate);
let activeStr=`${activeFd.year}-${activeFd.month+1}-${activeFd.day}`;
let myIndex=0;
myIndex=listDate.findIndex((value)=>`${value.year}-${value.month+1}-${value.day}`==activeStr);
if(myIndex<0)
{
myIndex=0;
}
return Math.floor(myIndex/7);
}
/**
* 动态切换样式
* @param {*} calendarType
* @param {*} weekIndex
*/
getTopStyle(calendarType,weekIndex)
{
let topStyles={};
if(!calendarType)
{
topStyles={"top":weekIndex*-40,height:'40px'}
}
return topStyles;
}
animationHandle=()=>{
this.setState({
playDecoration:'',
transFlag:false
})
}
render()
{
const {current,activeDate,calendarType,transFlag,playDecoration}=this.state;
const currentInfo=this.getFormatDate(current);//默认信息
const activeInfo=this.getFormatDate(activeDate);//选中的信息
const monthDateList=this.createCalendarDate(current);
const weekIndex=this.getWeekNo(activeDate,current,monthDateList[1].dateList);//当前所在层
const topStyles=this.getTopStyle(calendarType,weekIndex,);
let transForm=-375;
return <React.Fragment>
<div className="calendarContent"
style={{height:calendarType?'330px':'120px'}}
>
<div className={`wrapBox`}>
<div className="weekHead">{this.createWeekHead()}</div>
<div className={`wrapContainer ${this.state.playDecoration}`}
onTouchStart={this.startHandle}
onTouchMove={(e)=>this.moveHandle(e)}
onTouchEnd={(e)=>this.endHandle(e)}
onAnimationEnd={this.animationHandle}
style={{left:transForm}}>
<div className={`tables`} style={topStyles}>
{this.createCalendar(monthDateList[0],activeDate)}
</div>
<div className={`tables`} style={topStyles}>
{this.createCalendar(monthDateList[1],activeDate)}
</div>
<div className={`tables`} style={topStyles}>
{this.createCalendar(monthDateList[2],activeDate)}
</div>
</div>
<div>{(!calendarType && transFlag) ?'第几周':''}{calendarType}</div>
<div className="celsMonth" style={{display:calendarType?'block':'none'}}>{this.createMonth(current)}</div>
</div>
<div className="footBox" onClick={()=>this.showType(calendarType)}>显示全部
<div>{`${currentInfo.year}/${currentInfo.month+1}/${currentInfo.day}`}-{`${activeInfo.year}/${activeInfo.month+1}/${activeInfo.day}`}</div>
</div>
</div>
</React.Fragment>
}
}
部分同学反映缺少了CSS部分,CSS不是我的强项,贴过来更方便大家直观体验;
.transLeft{
animation:mymove 1s 1;
-webkit-animation:transLeft 1s 1; /*Safari and Chrome*/
}
.transRight{
animation:transRight 1s 1;
-webkit-animation:transRight 1s 1; /*Safari and Chrome*/
}
@keyframes transLeft
{
from {left:0}
to {left:-100%}
}
@-webkit-keyframes transLeft /*Safari and Chrome*/
{
from {left:0}
to {left:-100%}
}
@keyframes transRight
{
from {left:-200%}
to {left:-100%}
}
@-webkit-keyframes transRight /*Safari and Chrome*/
{
from {left:-200%}
to {left:-100%}
}
代码中有明确的注释;
有轮播图显示的逻辑味道