最近在做公司內部的運營管理系統,因為與日歷密切相關,同時無需觸發條件直接顯示在頁面上,所以針對這樣的功能場景,我就用avalon快速實現了一個簡易日歷,畢竟也是第一次造日歷這種輪子,所以這里記錄下我當時的一些思路,一來做一些技術總結,二來也是給像我這樣第一次做日歷的前端工程師一些參考。
先來一起看下日歷的界面:
日歷功能包括:1.顯示當前月、上個月后幾天、下個月前幾天的日期,2.切換到上個月,3.切換到下一個月,4.回到今天,5.點擊某天彈出當天的年月日(如2014-10-26),同時不在當前月的日期以更淡的顏色與當前月進行一個區分。
因為使用avalon進行實現,了解MVVM思想的人都知道,mvvm不操作dom,這是框架內部做的事情,而我們僅僅要做的是操作Mode層,也即數據層。
這里我用一個數組存儲當前一個月的所有日期對象,每一天就是一個對象,這個對象包括年,月,日,時間戳,是否隸屬於當前月等。如下所示:
var caleadar = [ { year : 2014, // 年份 month : 10, // 月份 date : 1, // 日 time : 1414771200000, // 時間戳 flag : false // 是否隸屬於當前月 }, { year : 2014, month : 10, date : 2, time : 1414911123564, flag : true }, ..... ];
得到這樣一個保存當前月所有信息的數組之后,因為此時caleadar是一個一維數組,而日歷是每7天一行,所以這里要將caleadar轉化為一個二維數組,每一個子數組里面保存的是一周的日期信息,這樣就可以利用avalon的動態模板引擎進行雙重模板渲染,這樣一個日歷就出來了。
利用getWeeks函數我們就可以得到存儲有當前月所有日期信息的數組,如下:
function getWeeks(ooo) { var dateObj = new Date(); var year = ooo.getFullYear(); var month = ooo.getMonth(); //得到今天是幾月(0 ~ 11) var date = ooo.getDate(); //得到今天是幾號 (1 ~ 31) //var cur = new Date(year, month, date); var cur = new Date(year, month, 1); cur.setMonth(month + 1); //改為下一個月, cur.setDate(0);//由於日期是1 ~ 31, 0則是退到上一個月的最后一天,於是得到這個月的總天數 var num = cur.getDate(); var next = 6 - cur.getDay(); var dates = avalon.range(1, num + 1); dates = dates.map(function(d) { return dateObj.getFullYear() === year && dateObj.getMonth() === month && dateObj.getDate() === d ? { year: year, month: month, date: d, isToday: true, flag: true, time: new Date(year, month, d) - 0 } : { year: year, month: month, date: d, flag: true, time: new Date(year, month, d) - 0 }; }); cur.setMonth(month); cur.setDate(1); //得到當月的第一天 var prev = cur.getDay(); //0 ~ 6 cur.setDate(date); //還原 //補上上一個月的日期 if (month - 1 < 0) { year--; month = 11; for (var i = 0; i < prev; i++) { var curr = new Date(year, month, -1 * i); dates.unshift({ year: year, month: month, date: curr.getDate() + 1, flag: false, time: curr - 0 }) } } else { for (i = 0; i < prev; i++) { curr = new Date(year, month, -1 * i); dates.unshift({ year: year, month: month - 1, date: curr.getDate(), flag: false, time: curr - 0 }) } } //補上下一個月的日期 if (month + 1 == 12) { year++; month = 0; for (i = 0; i < next; i++) { curr = new Date(year, month, i + 1); dates.push({ year: year, month: month, date: curr.getDate(), flag: false, time: curr - 0 }) } } else { for (i = 0; i < next; i++) { curr = new Date(year, month + 1, i + 1); dates.push({ year: year, month: month + 1, date: curr.getDate(), flag: false, time: curr - 0 }) } } var ret = []; while (dates.length) {//每行七個分組 ret.push(dates.splice(0, 7)); } return ret; //一個二維數組 }
這里ooo是getWeeks的唯一參數,它是一個Date對象。
獲取到數據之后,直接在html里面進行數據渲染,日歷就出來了,相關日歷html如下:
<div class="game_calendar" ms-controller="caleadarID"> <div class="current_info"> <button type="button" ms-click="backToToday">today</button> <button type="button" ms-click="prevMonth">prev</button> <button type="button" ms-click="nextMonth">next</button> <span>{{cyear}}年{{cmonth + 1}}月</span> </div> <table width="100%" border="0" cellspacing="0" cellpadding="0" class="week_title"> <tbody> <tr valign="bottom"> <td>日</td> <td>一</td> <td>二</td> <td>三</td> <td>四</td> <td>五</td> <td>六</td> </tr> </tbody> </table> <table width="100%" cellpadding="0" cellspacing="1" class="calendar_main"> <tbody> <tr ms-repeat-el="calendar"> <td ms-class="day_style:elem.flag==true" valign="top" ms-repeat-elem="el" ms-click="alert(elem)"> <div class="day_mouse"> <div class="day_no" ms-if="elem.isToday!=true">{{elem.date}}</div> <div class="day_no" ms-class="today:elem.isToday==true" ms-if="elem.isToday==true">今天</div> </div> </td> </tr> </tbody> </table> </div>
我們可以看到,模板里面運用了大量的以ms-開頭的綁定屬性和{{}}插值表達式,有的是用來渲染樣式,有的是用來綁定事件。這樣,一個日歷就出來了。
接下來就是日歷的切換功能了,實際上上面三個功能本質都是一樣,只要傳入getWeeks的參數不同返回不同的數組信息就行。因此在程序初始化是我們先保存幾個變量,如下:
var dateObj = new Date(); vm.calendar = _private.getWeeks(dateObj); //存儲當月日歷 vm.cyear = dateObj.getFullYear(); //當前年份 vm.cmonth = dateObj.getMonth(); //當前月份 vm.cdate = dateObj.getDate(); //當前日
接下來就是相關處理邏輯:
//上一個月 vm.prevMonth = function () { vm.cdate = 1; --vm.cmonth; if (vm.cmonth < 0) { vm.cyear--; vm.cmonth = 11; } _private.change(); }; //下一個月 vm.nextMonth = function () { vm.cdate = 1; ++vm.cmonth; if (vm.cmonth == 12) { vm.cyear++; vm.cmonth = 0; } _private.change(); }; //回到今天 vm.backToToday = function () { var dateObj = new Date(); vm.cyear = dateObj.getFullYear(); vm.cmonth = dateObj.getMonth(); vm.cdate = dateObj.getDate(); _private.change(); };
改變當前年、月、日后,我們只需要調用change函數來改變caleadar數組,change函數如下:
vm.caleader = _private.getWeeks(new Date(vm.cyear, vm.cmonth, vm.cdate));
這樣,一個簡易日歷就實現了,從頭到尾,我們沒有操作一行dom,完全的model操作,是不是很爽?
這里為了讓大家看到更好的日歷效果,附上日歷源碼,歡迎下載,有任何疑問歡迎留言!