30行 js 帮你理解日历是如何生成的

javascript的日期对象Date 是编程中非常常用的对象之一,这篇文章旨在帮助大家熟悉日期对象,并理解使用日期对象来创建一个日历。

言归正传

先看看简单的效果,后面还有复杂的

这是简单版效果图,包括当前月,凸显今天

来看代码[javascript]

    var weeks = "一二三四五六日".split(''),
        monthDays = [31,28,31,30,31,30,31,31,30,31,30,31],
        pastMleft ={
                0:6,1:7,2:1,3:2,4:3,5:4,6:5
            };
    var time = new Date(),
        thisY = time.getFullYear(),
        thisM = time.getMonth(),
        thisD = time.getDate();
    function cal(){
        var realM = thisM + 1,
            firstDW = new Date(thisY,thisM,1).getDay(),
            thisMD = monthDays[thisM],
            pastMD = pastMleft[firstDW];
        var lists = [];
        monthDays[1] = (thisY%400 == 0 || (thisY%4 == 0 && thisY%100 == 0 ))?29:28

        for(var i=0,l=weeks.length; i<l; i++)
            lists.push('<span class="week">'+ weeks[i] +'</span>')
        for(var i=0; i < pastMD ; i++)
            lists.push('<span class="past"></span>')
        for(var i=1; i <= thisMD; i++){
            var str = i==thisD?'today':i<thisD?'now':'fur'
            lists.push('<span class="'+ str +'">'+ i +'</span>')
        }
        for(var i=0; i < 42-thisMD-pastMD ; i++)
            lists.push('<span class="next"></span>')
        $('#cal-wrap').html(lists.join(' ')) 
    }
    window.onload = cal;

这里是html

<div id="cal-wrap"></div>

这里是css样式

*{
    margin:0;padding:0;
}
#cal-wrap{
    width:280px;
    background:#fff;
    display: flex;
    flex-wrap:wrap;
    margin:50px auto;
}
#cal-wrap>span{
    width:38px;
    height:38px;
    margin:1px;
    line-height: 38px;
    text-align: center;
    color:#fff;
    font-size: 12px;
}
#cal-wrap>.week{
    color:#000;
}
.past,.next{
    background: #ededed;
}
.now,.fur{
    background:#5ea6e0;
}
.fur{
    opacity: 0.5;
}
.today{
    background: #ff7875;
}

下面来重点讲讲日历的生成过程

  • 首先,日历表共有49个单元格,其中,周一到周日占据七个单元格,剩下的是上个月的末尾几天,本月份的天数,以及下个月的月初几天
  • 所以第一步创建周一到周日,我们可以生成一个包含一到日的数组,后期遍历这个数组就能创建周一到周日
var weeks = "一二三四五六日".split('');//周一到周日
  • 求上个月遗留的几天
    这张图片帮你理解上个月遗留了几天
    根据这个推论,我们可以得到这样一个对象,当本月第一天是周一,上个月遗留七天;本月第一天周二上个月遗留一天…….本月第一天周日上个月遗留六天
var pastMleft ={
    0:6,1:7,2:1,3:2,4:3,5:4,6:5
};
  • 所以现在问题的关键变成了求本月的第一天是周几,看代码吧
var time = new Date(),
    thisY = time.getFullYear(),//当前年份
    thisM = time.getMonth(),   //当前月份,返回0-11
    realM = thisM + 1,         //真实的月份,当前月份返回0时,真实应该是1月份
    thisD = time.getDate();    //今天,返回今天所在的日期
var firstDW = new Date(thisY,thisM,1).getDay();//本月第一天是星期几,返回0-6,对应周日-周六
var pastMD = pastMleft[firstDW];//上个月遗留的天数
  • 接下来求本月的天数,谚语有云:一三五七八十腊,三十一天永不差,所以我们得到了一个数组
var monthDays = [31,28,31,30,31,30,31,31,30,31,30,31];
  • 然后要依据闰年或者平年的判断,来重置二月份的天数
monthDays[1] = (thisY%400 == 0 || (thisY%4 == 0 && thisY%100 == 0 ))?29:28
  • 那么本月的天数也就出来了
var thisMD = monthDays[thisM];//本月天数
  • 下个月初在本月的天数怎么求呢?
var nextMD = 49-7-pastMD-thisMD
  • 现在日历的主体内容已经完成,就差动手创建元素了
var lists = [];
for(var i=0,l=weeks.length; i<l; i++)
    lists.push('<span class="week">'+ weeks[i] +'</span>')
for(var i=0; i < pastMD; i++)
    lists.push('<span class="past"></span>')
for(var i=1; i <= thisMD; i++){
    var str = i==thisD?'today':i<thisD?'now':'fur'
    lists.push('<span class="'+ str +'">'+ i +'</span>')
}
for(var i=0; i < 42-pastMD-thisMD; i++)
    lists.push('<span class="next"></span>')
$('#cal-wrap').html(lists.join(' ')) 
  • 创建完成,看看网页里有没有我们创建的日历吧,最后把代码重置优化一下,将不变的变量放到一起声明复制,创建的过程放到一个函数里方便后期代码重复调用,就形成了我们之前的展示的代码
var weeks = "一二三四五六日".split(''),
    monthDays = [31,28,31,30,31,30,31,31,30,31,30,31],
    pastMleft ={
            0:6,1:7,2:1,3:2,4:3,5:4,6:5
        };
var time = new Date(),//默认以当前年月创建日历
    thisY = time.getFullYear(),
    thisM = time.getMonth(),
    thisD = time.getDate();
function cal(){
    var realM = thisM + 1,
        firstDW = new Date(thisY,thisM,1).getDay(),
        thisMD = monthDays[thisM],
        pastMD = pastMleft[firstDW];
    monthDays[1] = (thisY%400 == 0 || (thisY%4 == 0 && thisY%100 == 0 ))?29:28
    var lists = [];
    for(var i=0,l=weeks.length; i<l; i++)
        lists.push('<span class="week">'+ weeks[i] +'</span>')
    for(var i=0; i < pastMD ; i++)
        lists.push('<span class="past"></span>')
    for(var i=1; i <= thisMD; i++){
        var str = i==thisD?'today':i<thisD?'now':'fur'
        lists.push('<span class="'+ str +'">'+ i +'</span>')
    }
    for(var i=0; i < 42-thisMD-pastMD ; i++)
        lists.push('<span class="next"></span>')
    $('#cal-wrap').html(lists.join(' '))  
}
  • 现在来想一个问题,想把上个月的日期和下个月的日期也填充进来,需要怎么做?首先我们要知道上个月是哪个月份
var pastM = thisM - 1;
    pastM = pastM < 0?11:pastM;//上个月对应的月份
  • 那么上个月最后一天是几号呢?就是上个月总共多少天
var pastM_lastD = monthDays[pastM];
  • 然后来更改一下创建元素的代码
var lists = [];
for(var i=0,l=weeks.length; i<l; i++)
    lists.push('<span class="week">'+ weeks[i] +'</span>')
for(var i=0; i < pastMD; i++)//这里注意一下,我们创建span是从前往后,而上个月遗留日期是从后往前推算,所以要做个减法
    lists.push('<span class="past">'+ (pastM_lastD-pastMD+i+1) +'</span>')
for(var i=1; i <= thisMD; i++){
    var str = i==thisD?'today':i<thisD?'now':'fur'
    lists.push('<span class="'+ str +'">'+ i +'</span>')
}
for(var i=0; i < nextMD; i++)
    lists.push('<span class="next">'+ (i+1) +'</span>')
$('#cal-wrap').html(lists.join(' ')) 
  • 现在日历就比较齐全了,上个月,本月和下个月都有了,但是通常日历都有点击切换月份的效果,这个效果要怎么实现呢?首先我们先更改一下样式,看看添加了点击切换功能的日历

    点击左右两个尖角可以切换

  • css样式表里先追加一段样式

.cal-title{
    font-size: 14px;
    display: flex;
    justify-content: space-between;
    padding: 5px 10px;
    width: 100%;
    background: #5ea6e0;
}
.cal-left-btn,.cal-right-btn{
    cursor:pointer;
}
  • 对应的html代码
<div id="cal-wrap">
    <!-- <div class="cal-title">
        <span class="cal-left-btn"><</span>
        <span class="cal-title-content">2018-08</span>
        <span class="cal-right-btn">></span>
    </div> -->
</div>
  • 先整理一下之前的cal,加入了上个月和下个月的日期,以及日历的头部
function cal(){
     var pastM = thisM - 1,realM = thisM + 1;
     var firstDW = new Date(thisY,thisM,1).getDay(),
         thisMD = monthDays[thisM],
         pastMD = pastMleft[firstDW];
         nextMD = 42-thisMD-pastMD;
         pastM = pastM < 0?11:pastM
     var lists = [];
     var pastM_lastD = monthDays[pastM];
     lists.push(`<div class="cal-title">
             <span class="cal-left-btn"><</span>
             <span class="cal-title-content">${thisY}-${realM<10?0+''+realM:realM}</span>
             <span class="cal-right-btn">></span>
         </div>`)
     for(var i=0,l=weeks.length; i<l; i++)
         lists.push('<span class="week">'+ weeks[i] +'</span>')
     for(var i=0; i < pastMD; i++)
         lists.push('<span class="past">'+ (pastM_lastD-pastMD+i+1) +'</span>')
     for(var i=1; i <= thisMD; i++){
         var str = i==thisD?'today':i<thisD?'now':'fur'
         lists.push('<span class="'+ str +'">'+ i +'</span>')
     }
     for(var i=0; i < nextMD; i++)
         lists.push('<span class="next">'+ (i+1) +'</span>')
     $('#cal-wrap').html(lists.join(' '))  
 }
  • 理顺一下思路,上面代码已经让小伙伴们了解了日历是怎么创建出来的,其实创建日历做本质的两个变量就是年份和月份,也就是说,在点击事件当中,变化的是年份和月份,只要确定了年份和月份,再调用创建日历的代码就可以了,所以来写个点击事件吧
$('body')
    .on('click','.cal-left-btn',function(){
        thisM = thisM - 1;
        if(thisM<0){
            thisM = 11; thisY = thisY - 1
        }
        cal()
    })
    .on('click','.cal-right-btn',function(){
        thisM = thisM + 1;
        if(thisM>11){
            thisM = 0; thisY = thisY + 1
        }
        cal()
    })
  • 上边的事件里,每点击一次,我们就让月份自增一次或者自减一次,同时月份超出本年的时候控制年份变化,然后在点击事件里调用创建日历的代码,就可以了,这里是完整的js代码
var weeks = "一二三四五六日".split(''),
    monthDays = [31,28,31,30,31,30,31,31,30,31,30,31],
    pastMleft ={
        0:6,1:7,2:1,3:2,4:3,5:4,6:5
    };
var time = new Date(),
    thisY = time.getFullYear(),
    thisM = time.getMonth(),
    thisD = time.getDate();
function cal(){
    monthDays[1] = (thisY%400 == 0 || (thisY%4 == 0 && thisY%100 == 0 ))?29:28
    var pastM = thisM - 1,realM = thisM + 1;
    var firstDW = new Date(thisY,thisM,1).getDay(),
        thisMD = monthDays[thisM],
        pastMD = pastMleft[firstDW];
        nextMD = 42-thisMD-pastMD;
        pastM = pastM < 0?11:pastM
    var lists = [];
    var pastM_lastD = monthDays[pastM];
    lists.push(`<div class="cal-title">
            <span class="cal-left-btn"><</span>
            <span class="cal-title-content">${thisY}-${realM<10?0+''+realM:realM}</span>
            <span class="cal-right-btn">></span>
        </div>`)
    for(var i=0,l=weeks.length; i<l; i++)
        lists.push('<span class="week">'+ weeks[i] +'</span>')
    for(var i=0; i < pastMD; i++)
        lists.push('<span class="past">'+ (pastM_lastD-pastMD+i+1) +'</span>')
    for(var i=1; i <= thisMD; i++){
        var str = i==thisD?'today':i<thisD?'now':'fur'
        lists.push('<span class="'+ str +'">'+ i +'</span>')
    }
    for(var i=0; i < nextMD; i++)
        lists.push('<span class="next">'+ (i+1) +'</span>')
    $('#cal-wrap').html(lists.join(' '))  

}
$('body')
    .on('click','.cal-left-btn',function(){
        thisM = thisM - 1;
        if(thisM<0){
            thisM = 11; thisY = thisY - 1
        }
       cal()
    })
    .on('click','.cal-right-btn',function(){
        thisM = thisM + 1;
        if(thisM>11){
            thisM = 0; thisY = thisY + 1
        }
        cal()
    })
window.onload = cal;

猜你喜欢

转载自blog.csdn.net/zhai_865327/article/details/81980979