小白项目|用原生JS实现Windows日历

1,148 阅读11分钟

效果

日历效果网站

Step1 时钟绘制

首先完成时钟的绘制

生成刻度

//html代码
   <div id="wrap">
        <!-- 上面的日期时钟部分 -->
        <div class="date">
            <!-- Clock -->
            <div id="clock">
                <ul class="circle">
                    
                </ul>
                <div class="dot"></div>
                <div class="hour"></div>
                <div class="minute"></div>
                <div class="sec"></div>
            </div>
            <div id="now">
                <p></p>
                <p></p>
            </div>
        </div>
     
    </div>
//css
#wrap{
    margin:120px auto;
    width: 360px;
    height: 520px;
    background: rgba(255, 255, 255, .8);
    box-shadow: 0px 0px 10px #000;
    border-radius: 5px;
}
.date{
    position: relative;
    height: 91px;
}
#clock{
    position: absolute;
    width: 110px;
    height: 110px;
    border-radius: 50%;
    background: url('./images/clock-bg.jpg') no-repeat;
    background-size: 100% 100%;
    border: 5px solid #c0c0c0;
    box-shadow:0px 0px 5px 1px #000;
    position: relative;
    left: 28px;
    top: -72px;
}
.circle{
    list-style: none;
    margin: 0;
    padding: 0;
}
.degree{
    width: 2px;
    height: 10px;
    background: #000;
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    transform-origin: 0 50px;
}
.dot{
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background-color: #000;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%);
}
.hour{
    width: 40px;
    height: 30px;
    background: url('./images/hour.png') no-repeat;
    background-size: 100% 100%;
    left: 44px;
    top: 40px;
    transform:rotate(0deg);
    transform-origin: 10px 15px;
    position: absolute;
}
.minute{
    width: 3px;
    height: 35px;
    background-color: #000;
    left: 50%;
    top: 21px;
    position: absolute;
    transform: translate(-2px,0);
    transform-origin: bottom;
    border-radius: 50%;
}
.sec{
    width: 43px;
    height: 44px;
    background: url('./images/hand.png') no-repeat;
    position: absolute;
    left: 18px;
    top: 16px;  
    transform-origin: 37px 39px;
}

js的实现

思路 1.首先只是个圆,并没有刻度,需要生成12个刻度,这刻度就是跟着圆盘中心旋转360/12度。 2.修改时分秒指针的位置,根据当前时间计算旋转角度

			var ulList = document.querySelector('.circle');
            var hourDom = document.querySelector('.hour');
            var minuteDom = document.querySelector('.minute');
            var secDom = document.querySelector('.sec');
            var days = document.querySelectorAll('#now p');
            var deg = 30;
            //渲染刻度
             function renderDegree(){
                var html = ''
                for(var i=0;i<12;i++){
                	//按着圆中心旋转
                    html += '<li class="degree" style="transform:rotate('+deg*i+'deg)"></li>';
                }
                return html;
            }

2.修改指针位置

//指针运行
            function runPointer(){
                var date = new Date();
                var sec = date.getSeconds();
                //分钟加上秒数的些许偏移
                var minute = date.getMinutes() + sec/60;
                //小时加上分钟的偏移
                var hour = date.getHours() + minute/60;
                //秒每次旋转的度数
                var secDeg = sec*360/60 ;
                //分钟每次旋转的度数
                var minuteDeg = minute * 360/60 ;
                //小时每次旋转的度数  比如两点钟 自然是12点偏移60°
                var hourDeg = hour * 30;
                //这里减去90度是因为我的时针一开始处于三点的位置
                hourDom.style.transform = `rotate(${hourDeg-90}deg)`;
                minuteDom.style.transform = `rotate(${minuteDeg}deg)`;
                //这里加45°是因为秒针的图片是斜着的,让他摆正
                secDom.style.transform = `rotate(${secDeg+45}deg)`;
            }
          
//接着让他每秒钟运行,就可以正常跑起来了
 setInterval(runPointer,1000);

素材

获取当前月有多少天

这个知识我们先熟悉一下,下面会用到。 首先Date对象里的月份是从0开始的,这个需要知道,也就是0代表1月,以此类推。然后我们用实例化对象的值去获取当月有多少天。

var day1 = new Date(2020,4);//这里会输出Fri May 01 2020 00:00:00 GMT+0800 (中国标准时间)
 var dayNum = new Date(2020,4,0);//Thu Apr 30 2020 00:00:00 GMT+0800 (中国标准时间)
 //这里第一个参数是年份,第二个参数是你想要获取的月份,其实这里传4你获取到的是5月,
 //但是如果加了第三个参数0相当于要减去1天

如果还是有点不懂可以看看这个es规则

7.Find a value t such that YearFromTime(t) == ym and MonthFromTime(t) == mn) and DateFromTime(t) == 1; but if this is not possible (because some argument is out of range), return NaN. 找到一個值t,使YearFromTime(t)== ym和MonthFromTime(t)== mn)和DateFromTime(t)== 1;但是如果不可能(由於某些參數超出範圍),則返回NaN。 8.Return Day(t) + dt − 1. 返還 日(t)+ dt − 1。

还是以new Date(2020,4,1)这个当例子,这里是5月的第一天,这个是根据第1条规则,然后根据第2条规则,1号+1-1所以就是5月1号。如果new Date(2020,4,0)就是5月1号+0-1所以就是往前挪一天。

显示日期

在这里插入图片描述
这里我们先把这个给实现了,这个还蛮简单的,就是获取当前的年月日还有周几嘛。 这里还是先实例化一个日期对象,然后用到日期的几个函数,这里介绍一下。

getDate() 从 Date 对象返回一个月中的某一天 (1 ~ 31)。
getDay() 从 Date 对象返回一周中的某一天 (0 ~ 6)。0代表周日
getMonth() 从 Date 对象返回月份 (0 ~ 11)。0为1月,按此递增
getFullYear() 从 Date 对象以四位数字返回年份。

好了,知道这几个函数我们就可以实现这个东西了,大家可以不看下面自己实现一下。

var days = document.querySelectorAll('#now p');//这里是获取年月日和周几这两行p标签
var weekArr = ['日','一','二','三','四','五','六'];//定义一个周数组用于渲染
//显示日期
function getDay(){
     var date = new Date();//实例化日期对象
     var year = date.getFullYear();//获取年份
     var month = date.getMonth()+1;//获取月份,因为月份从零开始所以要加1
     var day = date.getDate();//获取日
     var dateStr = year+'年'+addZero(month)+'月'+addZero(day)+'日';//这里用到一个自己写的补零函数,只是为了让小于10月的显示0X月。
     var week = date.getDay();//获取周几
     //年月日
     days[0].innerHTML = dateStr;
     days[1].innerHTML = '星期'+weekArr[week];//这里就可以从定义的周数组取出是周几了,0对应周日
 }
 //补零函数
 function addZero(num){
     return num<10 ? '0'+num : ''+num;
 }
 getDay();

创建年月日视图

创建年视图

先看效果图来写个思路

效果图
可以看到年视图由当前年份(10年)+前一个10年的最后四年+下一个10年的前两年。

先来获取当前10年 这里先用2019年来打个比喻因为他不是整年,这个十年的开头是2010。因此可以使用Math.floor(2019/100) 这样可以拿到那年的起始10年,获得这个起始年份就可以完成这三部分。

//渲染年份
            //显示16个年份,10个今年的,最前面有前十年的(4个),后面十年的2个
            function renderYear(){
            	//获取当下10年初始年份
                var nowYearBegin = Math.floor(yearNow/10)*10;
                var insertUl = '<ul class="year">';
                //一共16个格子
                for(i=0;i<16;i++){
                    if(i<4){
                    //显示上个10年最后4个
                        insertUl += '<li class="year-cell other-yaer">'+(nowYearBegin+i-4)+'</li>';
                    }else if(i-4<10){
                    //显示今个10年
                        insertUl += '<li class="year-cell">'+(nowYearBegin+i-4)+'</li>';
                    }else{
                    //下个10年的前两个
                        insertUl += '<li class="year-cell  other-yaer">'+(nowYearBegin+i-4)+'</li>';
                    }
                }
                insertUl += '</ul>';
                main.innerHTML = insertUl;
            }

月视图

这个比较简单,直接上代码

          function renderMonth(){
                var insertUl = '<ul class="month">';
                for (var i =1;i<=12;i++){
                    insertUl += '<li class="month-cell">'+i+'月</li>';
                }
                insertUl += '</ul>';
                main.innerHTML = insertUl;
            }

日视图

创建日期视图思路 1.一共有42个格子 2.日期视图由上个月的,今个月的,下个月的组成 3.首先找到这个月第一天处于周几,这样就可以知道上个月要填多少天进去,下个月就看剩下多少个格子,把下个月的填进去。

 function renderDayView(){
        var lastMonth = monthNow+1;
           if(lastMonth == 12){
               lastMonth = 0;
           }
           //获取这个月有多少天
           var dayNum = new Date(yearNow,lastMonth,0).getDate();
           //获取这个月第一天第一天周几
           var weekIndex = new Date(yearNow,monthNow,1).getDay();
           //获取上个月最后一天
           var prevMonthDay = new Date(yearNow,monthNow,0).getDate();
           
           //拼接ul
           var insertUl = '<ul class="date">';
           for(var i=0;i< showDay; i++){
               //拼接上个月的,这个月第一天周几决定上个月可以放多少天
               if(i<weekIndex){
                   insertUl += '<li class="date-cell other-date">'+ (prevMonthDay+i+1 - weekIndex ) +'</li>';
               }else if(i-weekIndex<dayNum){
                   //今个月的
                   insertUl += '<li class="date-cell">'+ (i +1 - weekIndex ) +'</li>';
               }else{
                   //下个月的
                   insertUl += '<li class="date-cell other-date">'+  (i - dayNum-weekIndex+1) +'</li>';
               }
           }
           main.innerHTML = insertUl;
  }

切换视图

也就是日视图切换到月视图再切换到年视图。

 			//获取切换按钮
           var optionBtn = document.querySelector('#option');
           var optionType = 0;//定义0为日视图 1为月视图 2为年视图
           var optionFlag;
           optionBtn.onclick = function(){
               optionFlag = optionType+1;
               if(optionFlag > 2){
               	//超出之后保持在年视图
                   optionType = 2;
               }else{
                   createView();
               }
           }
           //渲染视图
           //加进出动画效果在从日到年,还有另外从年到日
           function createView(){
           	 //这里是为了在一开始打开页面时候没有点击切换按钮情况去调用日视图渲染
               if(optionFlag == undefined){
                   optionBtn.innerHTML = checkYear+'年'+addZero(checkMonth+1)+'月';
                   renderDayView(checkMonth,checkYear,main);
                   return;
               }
               //从日视图进月或月视图进年视图
               if(optionType<optionFlag){
               	//动画效果div
                   bigMain.innerHTML = '<div class="toHide board"></div><div class="toShow board"></div>';
                   var dom = document.querySelectorAll('.board');
                   switch(optionFlag){
                       case 1:
                           //日视图切换到月份视图
                           optionBtn.innerHTML = checkYear+'年';
                           //月视图放在渐入div
                           renderMonth(checkYear,dom[1]);
                           //日视图放在逐渐消失的div
                           renderDayView(checkMonth,checkYear,dom[0]);
                           //记录当前视图处于哪个视图
                           optionType = optionFlag;
                           break;
                       case 2:
                           //月视图切换到年时图
                           var YearBegin = Math.floor(checkYear/10)*10;
                           optionBtn.innerHTML = YearBegin+'-'+(YearBegin+9)+'年';
                           //年视图准备进入
                           renderYear(checkYear,dom[1]);
                           //月视图逐渐消失
                           renderMonth(checkYear,dom[0]);
                            //记录当前视图处于哪个视图
                           optionType = optionFlag;
                           break;
                   }
               }else{
               //这种情况是出现在点击某个年份或者某个月份时候进入新视图
                   bigMain.innerHTML = '<div class="toBlow board"></div><div class="toNarrow board"></div>';
                   var dom = document.querySelectorAll('.board');
                   switch(optionFlag){
                       case 0:
                           //月视图切换到日视图
                           optionBtn.innerHTML = checkYear+'年'+addZero(checkMonth+1)+'月';
                           renderMonth(checkYear,dom[0]);
                           renderDayView(checkMonth,checkYear,dom[1]);
                           optionType = optionFlag;
                           break;
                       case 1:
                           //年视图切换到月时图
                           optionBtn.innerHTML = checkYear+'年';
                           renderMonth(checkYear,dom[1]);
                           renderYear(checkYear,dom[0]);
                           optionType = optionFlag;
                           break;
                   }
               }
           }

这里面的renderDayView()、renderMonth()、renderYear()三个方法进行了稍微的改进去适合加入动画效果以及适应切换视图后的变量调整,接下来看看调整后

		//年,月,渲染节点
       function renderDayView(month,year,dom){
               var lastMonth = month+1;
               if(lastMonth == 12){
                   lastMonth = 0;
               }
               //获取这个月有多少天
               var dayNum = new Date(year,lastMonth,0).getDate();
               //获取这个月第一天第一天周几
               var weekIndex = new Date(year,month,1).getDay();
               //获取上个月最后一天
               var prevMonthDay = new Date(year,month,0).getDate();
               
               //拼接ul
               var insertUl = '<ul class="date">';
               for(var i=0;i< showDay; i++){
                   //拼接上个月的
                   if(i<weekIndex){
                       insertUl += '<li class="date-cell other-date">'+ (prevMonthDay+i+1 - weekIndex ) +'</li>';
                   }else if(i-weekIndex<dayNum){
                       //今个月的
                       if(i +1 - weekIndex == dateNow && year == yearNow && month == monthNow){
                           // 就是·今日·
                           insertUl += '<li class="date-cell active">'+ (i +1 - weekIndex ) +'</li>';
                       }else{
                           insertUl += '<li class="date-cell">'+ (i +1 - weekIndex ) +'</li>';
                       }
                   }else{
                       //下个月的
                       insertUl += '<li class="date-cell other-date">'+  (i - dayNum-weekIndex+1) +'</li>';
                   }
               }
               dom.innerHTML = insertUl;
           }
           //渲染月份  参数 年,渲染节点
           function renderMonth(year,dom){
               var insertUl = '<ul class="month">';
               for (var i =1;i<=12;i++){
                   if(i==monthNow+1 && year == yearNow){
                       insertUl += '<li class="month-cell active">'+i+'月</li>';
                   }else{
                       insertUl += '<li class="month-cell">'+i+'月</li>';
                   }
               }
               insertUl += '</ul>';
               dom.innerHTML = insertUl;
               var li = dom.querySelectorAll("li");
               li.forEach(function(item){
                   item.onclick = function(){
                       //更换月份
                       checkMonth = parseInt(this.innerHTML)-1;
                       //比较视图为0,这样在createView方法里可以走月视图进入日视图
                       optionFlag = 0;
                       createView();
                   };
               });
           }
           //渲染年份
           //显示16个年份,10个今年的,最前面有前十年的(4个),后面十年的2个
           function renderYear(year,dom){
               var nowYearBegin = Math.floor(year/10)*10;
               var insertUl = '<ul class="year">';
               for(i=0;i<16;i++){
                   if(i<4){
                       insertUl += '<li class="year-cell other-yaer">'+(nowYearBegin+i-4)+'</li>';
                   }else if(i-4<10){
                       if(nowYearBegin+i-4 == year){
                           insertUl += '<li class="year-cell active">'+(nowYearBegin+i-4)+'</li>';
                       }else{
                           insertUl += '<li class="year-cell">'+(nowYearBegin+i-4)+'</li>';
                       }
                   }else{
                       insertUl += '<li class="year-cell  other-yaer">'+(nowYearBegin+i-4)+'</li>';
                   }
               }
               insertUl += '</ul>';
               dom.innerHTML = insertUl;
               var li = dom.querySelectorAll("li");
               li.forEach(function(item){
                   item.onclick = function(){
                       //更换年份
                       checkYear = parseInt(this.innerHTML);
                       //同理
                       optionFlag = 1;
                       createView();
                   };
               });
           }

现在已经可以随意切换视图并且时间也正确了最后一步就是按上下去切换上一年/月/日或下一年/月/日。

上滑下滑效果

            //上下切换
            var prev = document.querySelector('#prev');
            var next = document.querySelector('#next');
            //上滑
            prev.onclick = function(){
                slideView(-1);
            };
            //下滑
            next.onclick = function(){
                slideView(1);
            }
            function slideView(index){
                if(index<0){
                    //上滑
                    bigMain.innerHTML = '<div class="bottomOut board"></div><div class="board toBottom"></div>';
                    var board = bigMain.querySelectorAll(".board");
                    viewMove(index,board);
                }else{
                    //下滑
                    bigMain.innerHTML = '<div class="topOut board"></div><div class="board toTop"></div>';
                    var board = bigMain.querySelectorAll(".board");
                    viewMove(index,board);
                }
            }
            //上滑下滑视图对象封装
            function viewMove(type,board){
                //先看处于什么视图
                switch(optionType){
                        case 0:
                            renderDayView(checkMonth,checkYear,board[0]);
                            //日视图 
                            if(type<0){
                                checkMonth--;
                            }else{
                                // 月份加1
                                checkMonth++;
                            }
                            //实例化切换后的对象
                            slideDate(checkMonth,checkYear);
                            optionBtn.innerHTML = checkYear+'年'+addZero(checkMonth+1)+'月';
                            renderDayView(checkMonth,checkYear,board[1]);
                            break;
                        case 1:
                            //月视图
                            renderMonth(checkYear,board[0]);
                            if(type<0){
                                checkYear--;
                            }else{
                                checkYear++;
                            }
                            //实例化切换后的对象
                            slideDate(checkMonth,checkYear);
                            optionBtn.innerHTML = checkYear+'年';
                            renderMonth(checkYear,board[1]);
                            break;
                        case 2:
                            //年时图
                            renderYear(checkYear,board[0]);
                            if(type<0){
                                checkYear = checkYear-10;
                            }else{
                                checkYear = checkYear+10;
                            }
                            //实例化切换后的对象
                            slideDate(checkMonth,checkYear);
                            var YearBegin = Math.floor(checkYear/10)*10;
                            optionBtn.innerHTML = YearBegin+'-'+(YearBegin+9)+'年';
                            renderYear(checkYear,board[1]);
                }
            }
            //重新实例化日期对象
            function slideDate(month,year){
                var date = new Date(year,month);
                checkYear = date.getFullYear();
                checkMonth = date.getMonth();
            }

这个项目就到此为止了,学了js的一些基本知识和关于日期的知识后可以拿来练一练手。这个项目的源代码源代码链接 在掘金的第一篇文章,写的不好的还望各位指出,如果觉得可以的,可以点个关注