React高仿钉钉无限循环日历组件

3 篇文章 1 订阅
1 篇文章 0 订阅

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%}
}

代码中有明确的注释;
有轮播图显示的逻辑味道

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值