日历,可以上拉切换成周历,下拉切换成月历

2021-03-23  本文已影响0人  keknei

因为最近项目很多地方用日历,所以就写了一个,可以来回切月历和周历,效果图就是下面这两张

下拉月历状态 上拉周历状态
html部分
<div class="calender-box">
    <div class="calender-header">
      <span id="pre">上一月</span>
      <div class="date-box">
        <i id="year"></i>
        <i id="month"></i>
        <i id="date" style="display: none;"></i>
      </div>
      <span id="next">下一月</span>
    </div>

    <!-- 周 -->
    <div class="week-box">
      <ul>
        <li>星期日</li>
        <li>星期一</li>
        <li>星期二</li>
        <li>星期三</li>
        <li>星期四</li>
        <li>星期五</li>
        <li>星期六</li>
      </ul>
    </div>

    <!-- 日历 -->
    <div class="calender-content">
      <ul id="calender">
        <!-- <li><span class="opa">31</span></li>
        <li><span>1</span></li> -->
      </ul>
    </div>
  </div>
css部分
    ul,li{
      padding:0;
      margin:0;
      list-style:none;
    }
    .calender-box{
      width:1000px;
      margin:0 auto;
      margin-top:50px;
    }
    .calender-header{
      display:flex;
      justify-content:center;
    }
    .calender-header span{
      cursor:pointer;
      color:#14C48B;
    }
    .calender-header i{
      font-style:normal;
    }
    .date-box{
      margin:0 20px;
    }
    .week-box{
      margin-top:20px;
    }
    .week-box li{
      width:50px;
    }
    .week-box ul,.calender-content ul{
      display:flex;
      justify-content:center;
      flex-wrap:wrap;
      width:480px;
      margin:0 auto;
    }
    .calender-content ul{
      justify-content:flex-start;
      min-height:32px;
      max-height:192px;
      margin-top:20px;
      overflow:hidden;
      user-select:none; 
      -webkit-user-select:none; 
      -moz-user-select:none; 
      -ms-user-select:none; user-select:none;
    }
    .week-box li:first-child ~ li{
      margin-left:20px;;
    }
    .calender-content li{
      width:50px;
      text-align:center;
      line-height:32px;
      margin-left:20px;
      cursor:pointer;
    }
    .calender-content li:nth-child(7n+1){
      margin-left:0px;
    }
    .active{
      background:aqua;
    }
    .sign{
      background:aquamarine;
    }
    .opa{
      color:#ccc;
    }
js部分
  //获取需要显示日历UI的元素
  let calenderDom=document.querySelector("#calender");
  //获取点击上一月下一月的元素
  let preDom=document.querySelector("#pre");
  let nextDom=document.querySelector("#next");
  //获取显示年月日的元素
  let yearDom=document.querySelector("#year");
  let monthDom=document.querySelector("#month");
  let dateDom=document.querySelector("#date");
  // 因为要显示上个月的和下个月的个别天数,并且每行7天,所以需要6行才能显示全,也就是42个天数

  //是否是周模式,默认是月历模式
  let isWeek=false;
  //如果是周模式,选中的是月历哪一行
  let lineNum=0;

  //获取当前年当前月的1号是星期几
  function getWeekDay(year,month){
    let date=new Date(year,month,1);
    return date.getDay();
  }

  //获取显示当前月 天数
  /*
      year是年
      month是月
      flag如果是true 是当前月,false表示上个月或者下个月,来区别显示DOM UI上的颜色
  */
  function getMonthCount(year,month,flag=true){
    let firstDate=new Date(year,month,1);//当月第一天
    let lastDate=new Date(year,month+1,0);//当月最后一天
    let countDate=lastDate.getDate()-firstDate.getDate()+1;
    return Array.from(new Array(countDate),(item,value)=>{
      return {
        year,
        month,
        flag,
        num:value+1
      }
    });
  } 

  //每个月的几号在月历第几行
  function getCalenderLine(year,month,date){
    let preCountNum=getPreMonthCount(year,month);//获取上个月的天数
    let weekDay=getWeekDay(year,month);//当前月的1号星期几
    let preCount=Array.from(new Array(preCountNum.length),(item,value)=>value+1);//上个月的天数数组
    let preArr=preCount.slice(-weekDay);//上个月取的天数正好是当前月1号星期几前面的天数

    //每个月的几号在第几行
    return parseInt((parseInt(date-1)+preArr.length)/7)+1;
  } 

  //获取下个月 天数
  function getNextMonthCount(year,month){
    if(month===11){
      return getMonthCount(year+1,0,false);
    }else{
      return getMonthCount(year,month+1,false);
    }
  } 

  //获取上个月 天数
  function getPreMonthCount(year,month){
    if(month===0){
      return getMonthCount(year-1,11,false);
    }else{
      return getMonthCount(year,month-1,false);
    }
  } 

  

  //初始化当前月历今天日期
  let initDate=new Date();
  let initYear=initDate.getFullYear();
  let initMonth=initDate.getMonth();
  let initNum=initDate.getDate();
  //点击后日历的年 月 日,默认是初始化的日期
  let activeYear=initYear;
  let activeMonth=initMonth;
  let activeNum=initNum;
  //日历更新后的年月日
  let currentYear=initYear;
  let currentMonth=initMonth;
  let currentNum=initNum;
  //渲染当前日
  dateDom.innerHTML=zeroize(initNum,2)+'日';
  //初始化日历
  renderCalender(initYear,initMonth);
  //初始化打开后当天日期是在日历的第几行
  //lineNum=getCalenderLine(initYear,initMonth,initNum);
 
  //渲染月历的方法
  function renderCalender(year,month){
    //初始化当前月历天数
    let initCount=getMonthCount(year,month);
    //初始化上个月的月历天数
    let preCount=getPreMonthCount(year,month);
    //初始化下个月的月历天数
    let nextCount=getNextMonthCount(year,month);
    //当前月的1号星期几
    let weekDay=getWeekDay(year,month);
    //合并要显示的月历天数
    let resArr=[];//合并天数的数组
    //上个月需要的天数
    let preArr=[];
    if(weekDay==0){//如果是0,表示是星期日,就是最开头,所以是不需要上个月的天数的
      
    }else{
      preArr=preCount.slice(-weekDay);//上个月取的天数正好是当前月1号星期几前面的天数
    }
    //下个月需要的天数  42减去当前月的天数和上个月的天数
    let nextArr=nextCount.slice(0,42-initCount.length-preArr.length);
    //所有需要显示的天数
    resArr=[].concat(preArr,initCount,nextArr);
    //将所需要的天数渲染到dom中
    let str=""; 
    resArr.forEach((item)=>{
      //渲染当年当月当天的颜色
      if(initYear==item.year&&initMonth==item.month&&initNum==item.num){
        str+=`<li class="active" data-year="${item.year}" data-month="${item.month}" data-num="${item.num}"><span>${zeroize(item.num,2)}</span></li>`;
      }else if(activeYear==item.year&&activeMonth==item.month&&activeNum==item.num){//记录点击后的日历
        str+=`<li class="sign" data-year="${item.year}" data-month="${item.month}" data-num="${item.num}"><span>${zeroize(item.num,2)}</span></li>`;
      }else if(item.flag){//是当前月,UI颜色正常
        str+=`<li data-year="${item.year}" data-month="${item.month}" data-num="${item.num}"><span>${zeroize(item.num,2)}</span></li>`;
      }else{//是上个月或者下个月,UI颜色灰色
        str+=`<li data-year="${item.year}" data-month="${item.month}" data-num="${item.num}"><span class="opa">${zeroize(item.num,2)}</span></li>`;
      }
    });
    //渲染日历dom
    calenderDom.innerHTML=str;

    //给日历日期添加添加点击事件
    let liArr=document.querySelectorAll("#calender li");
    liArr.forEach(item=>{
      item.addEventListener("click",()=>{
        liArr.forEach(item=>{//清楚标记class
          item.classList.remove("sign");
        });
        item.classList.add("sign");
        //可以处理函数,比如去请求接口  dataset里是标签上的属性有year month num
        callbackCalender(item.dataset);
        
      });
    });

    //渲染年月日
    yearDom.innerHTML=year+'年';
    monthDom.innerHTML=zeroize(month+1,2)+'月';
  }
  
  //渲染周历的方法
  /*
    flag:如果是从月历状态切换成周历状态就是false,是在周历状态下操作的是true
  */
  function renderWeekCaleder(flag){
    let tDate=null;
    if(currentYear!=activeYear || currentMonth!=activeMonth){//如果点击日历的月份和显式的月份不一致,就用显示的月份的1号
      tDate=flag ? new Date(currentYear,currentMonth,currentNum) : new Date(currentYear,currentMonth,1);
    }else if(currentYear==activeYear || currentMonth==activeMonth){
      tDate=flag ? new Date(currentYear,currentMonth,currentNum) : new Date(currentYear,currentMonth,activeNum);
    }

    //算出当前周的第一天是那一天,当前日期减去当前日期星期几就是本周的第一天的日期
    let firstDate=currentNum-tDate.getDay();
    if(currentYear!=activeYear || currentMonth!=activeMonth){//如果点击日历的月份和显式的月份不一致,就用显示的月份的1号
      firstDate=flag ? currentNum-tDate.getDay() : 1-tDate.getDay();
    }else if(currentYear==activeYear || currentMonth==activeMonth){
      firstDate=flag ? currentNum-tDate.getDay() : activeNum-tDate.getDay();
    }
    //循环渲染出本周七天的日期
    let str="";
    for(let i=firstDate;i<(firstDate+7);i++){
      let oDate=new Date(currentYear,currentMonth,i);
      let wYear=oDate.getFullYear();
      let wMonth=oDate.getMonth();
      let wDate=oDate.getDate();
      let flag=true;
      if(wMonth!=currentMonth){//如果点击的日期的月份和循环的日期的月份不一样
        flag=false
      }

      if(initYear==wYear&&initMonth==wMonth&&initNum==wDate){//渲染当年当月当天的颜色
        str+=`<li class="active" data-year="${wYear}" data-month="${wMonth}" data-num="${wDate,2}"><span>${zeroize(wDate,2)}</span></li>`;
      }else if(activeYear==wYear&&activeMonth==wMonth&&activeNum==wDate){//记录点击后的日历
        str+=`<li class="sign" data-year="${wYear}" data-month="${wMonth}" data-num="${wDate,2}"><span>${zeroize(wDate,2)}</span></li>`;
      }else if(flag){//是当前月,UI颜色正常
        str+=`<li data-year="${wYear}" data-month="${wMonth}" data-num="${wDate}"><span>${zeroize(wDate,2)}</span></li>`;
      }else{//是上个月或者下个月,UI颜色灰色
        str+=`<li data-year="${wYear}" data-month="${wMonth}" data-num="${wDate}"><span class="opa">${zeroize(wDate,2)}</span></li>`;
      }
    } 
    //渲染日历dom
    calenderDom.innerHTML=str;

    //给日历日期添加添加点击事件
    let liArr=document.querySelectorAll("#calender li");
    liArr.forEach(item=>{
      item.addEventListener("click",()=>{
        liArr.forEach(item=>{//清楚标记class
          item.classList.remove("sign");
        });
        item.classList.add("sign");
        //可以处理函数,比如去请求接口  dataset里是标签上的属性有year month num
        callbackCalender(item.dataset);
      });
    });

    //渲染年月日
    yearDom.innerHTML=currentYear+'年';
    monthDom.innerHTML=zeroize(currentMonth+1,2)+'月';
  }

  //点击上一月或者上一周
  preDom.addEventListener("click",()=>{
    if(isWeek){//如果是周日历
      let date=new Date(currentYear,currentMonth,parseInt(currentNum)-7);
      currentYear=date.getFullYear();
      currentMonth=date.getMonth();
      currentNum=date.getDate();
      renderWeekCaleder(true);
    }else{//是月历
      if(currentMonth==0){
        currentMonth=11;
        currentYear-=1;
        renderCalender(currentYear,currentMonth);
      }else{
        currentMonth-=1;
        renderCalender(currentYear,currentMonth);
      }
    }
  });
  //点击下一月或者下一周
  nextDom.addEventListener("click",()=>{
    if(isWeek){//如果是周日历
      let date=new Date(currentYear,currentMonth,parseInt(currentNum)+7);
      currentYear=date.getFullYear();
      currentMonth=date.getMonth();
      currentNum=date.getDate();
      renderWeekCaleder(true);
    }else{//是月历
      if(currentMonth==11){
        currentMonth=0;
        currentYear+=1;
        renderCalender(currentYear,currentMonth);
      }else{
        currentMonth+=1;
        renderCalender(currentYear,currentMonth);
      }
    }
    
  });

  //点击日历其中的日期后的操作
  function callbackCalender(obj){
    activeYear=obj.year;
    activeMonth=obj.month;
    activeNum=obj.num;
    currentNum=obj.num;
    console.log(obj.year,obj.month,obj.num)
    //lineNum=getCalenderLine(obj.year,obj.month,obj.num);//该日期是第几行

    if(activeMonth!=currentMonth){//点击是下个月或者上个月才更新日历
      currentMonth=parseInt(activeMonth);
      currentYear=parseInt(activeYear);
      if(isWeek){//如果是周日历
        renderWeekCaleder(true);
      }else{//是月历
        renderCalender(parseInt(activeYear),parseInt(activeMonth));
      }
    }
  }
  //上下拖拽日历down
  calenderDom.addEventListener("mousedown",(ev)=>{
    let downY=ev.clientY;
    //鼠标移动日历move
    function eventMove(ev){
      let moveY=ev.clientY;
      let disY=moveY-downY;

      if(!isWeek && disY<0){//月历模式并且鼠标是向上移动的
        calenderDom.style.height=(192-Math.abs(disY))+"px";
      }else if(isWeek && disY>0){//周模式并且鼠标是向下移动的
        calenderDom.style.height=Math.abs(disY)+"px";
      }
      
      //防止选中文字
      window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
    }
    document.addEventListener("mousemove",eventMove);
    
    //鼠标点击日历后up
    function eventUp(ev){
      let moveY=ev.clientY;
      let disY=moveY-downY;
      if(!isWeek && disY<0){//月历模式并且鼠标是向上移动的
        if(Math.abs(disY)<50){//如果鼠标移动距离小于50px,则回到原来的月历状态
          calenderDom.style.height=192+"px";
        }else{//大于50px,就收起来变成周日历
          if(isWeek)return;//如果本来就是周历的话,直接return掉即可
          isWeek=true;
          calenderDom.style.height=32+"px";
          preDom.innerHTML="上一周";
          nextDom.innerHTML="下一周";
          //渲染周日历
          //如果是从月历状态切换成周历状态并且当前月份没有选中的日期,那么应该将当前日期设置为显示的月份的1号
          if(currentYear!=activeYear || currentMonth!=activeMonth){
            currentNum=1;
          }else{
            currentNum=activeNum;
          }
          renderWeekCaleder(false);
        }
      }else if(isWeek && disY>0){//周模式并且鼠标是向下移动的
        if(Math.abs(disY)<10){//如果鼠标移动距离小于10px,就收起来变成周日历
          calenderDom.style.height=32+"px";
        }else{//大于10px,原来的月历状态
          if(!isWeek)return;//如果本来就是月历的话,直接return掉即可
          isWeek=false;
          preDom.innerHTML="上一月";
          nextDom.innerHTML="下一月";
          calenderDom.style.height=192+"px";
          //渲染月历
          renderCalender(currentYear,currentMonth);
        }
      }
      document.removeEventListener("mousemove",eventMove);
      document.removeEventListener("mouseup",eventUp);
    }
    document.addEventListener("mouseup",eventUp);
  });
  
  //补零
  function zeroize(num,n){
    num=num.toString()
    while(num.length<n){
      num="0"+num;
    }
    return num;
  }
上一篇下一篇

猜你喜欢

热点阅读