美文网首页
日历,可以上拉切换成周历,下拉切换成月历

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

作者: keknei | 来源:发表于2021-03-23 15:44 被阅读0次

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

    下拉月历状态 上拉周历状态
    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;
      }
    

    相关文章

      网友评论

          本文标题:日历,可以上拉切换成周历,下拉切换成月历

          本文链接:https://www.haomeiwen.com/subject/ruyfhltx.html