關於 Date APIjavascript
getFulleYear(); // 年
getMonth(); // 月, 0-11
getDate(); // 日,也就是幾號
getDay(); // 星期幾,0-6
new Date(2019,2,10); // 實際上就是 2019-03-10
new Date(2019,2,0); // 其實是 2019-02-28, 也就是2月份的最後一天
new Date(2019,2,-1); // 其實是 2019-02-27, 也就是2月份的倒數次日
var d = new Date();
d.setTime( new Date(2018,1,1).getTime() );
d.getFullYear(); // 2018 , setTime 容許傳入毫秒數來更改實例對象
複製代碼
咱們須要注意一點,new Date 時,month 和 date 是能夠傳入負數的。css
作成啥樣子呢,如圖: html
怎麼作呢,一步步來分析:vue
①:計算出當前月份有多少天,遍歷顯示出來java
②:點擊上一月,下一月時,改變月份,並從新執行第一步git
③:計算月初和月末分別對應星期幾,並補全月初和月末的空位。如上圖的 xx 位置。github
第1、二步是很好處理的,主要是第三步,有些複雜。app
我不想拼接 dom,因此就採用 vue來開發了,省時省力。dom
開始吧。函數
<div id="app">
<div class="calendar-wrapper">
<div class="calendar-toolbar">
<div class="prev" @click="prevMonth">上個月</div>
<div class="current">{{ currentDateStr }}</div>
<div class="next" @click="nextMonth">下個月</div>
</div>
<div class="calendar-inner">
<div class="calendar-item" v-for="(item, index) of calendarList" :key="index">
{{ item.date }}
</div>
</div>
</div>
</div>
複製代碼
css 我就略過了
感受前兩步是比較簡單的,我就直接貼js吧,加了部分註釋,但願不難讀。
new Vue({
el: '#app',
data() {
return {
current: {}, // 當前時間
calendarList: [], // 用於遍歷顯示
shareDate: new Date() // 享元模式,用來作優化的
}
},
computed: {
// 顯示當前時間
currentDateStr() {
let { year, month } = this.current;
return `${year}年${this.pad(month + 1)}月`;
}
},
mounted() {
this.init();
},
methods: {
init() {
// 初始化當前時間
this.setCurrent();
this.calendarCreator();
},
// 判斷當前月有多少天
getDaysByMonth(year, month) {
return new Date(year, month + 1, 0).getDate();
},
// 對小於 10 的數字,前面補 0
pad(str) {
return str < 10 ? `0${str}` : str;
},
// 點擊上一月
prevMonth() {
this.current.month--;
// 由於 month的變化 會超出 0-11 的範圍, 因此須要從新計算
this.correctCurrent();
// 生成新日期
this.calendarCreator();
},
// 點擊下一月
nextMonth() {
this.current.month++;
// 由於 month的變化 會超出 0-11 的範圍, 因此須要從新計算
this.correctCurrent();
// 生成新日期
this.calendarCreator();
},
// 格式化時間,與主邏輯無關
stringify(year, month, date) {
let str = [year, this.pad(month + 1), this.pad(date)].join('-');
return str;
},
// 設置或初始化 current
setCurrent(d = new Date()) {
let year = d.getFullYear();
let month = d.getMonth();
let date = d.getDate();
this.current = {
year,
month,
date
}
},
// 修正 current
correctCurrent() {
let { year, month, date } = this.current;
let maxDate = this.getDaysByMonth(year, month);
// 預防其餘月跳轉到2月,2月最多隻有29天,沒有30-31
date = Math.min(maxDate, date);
let instance = new Date(year, month, date);
this.setCurrent(instance);
},
// 生成日期
calendarCreator() {
// 一天有多少毫秒
const oneDayMS = 24 * 60 * 60 * 1000;
let list = [];
let { year, month } = this.current;
// 當前月,第一天和最後一天的毫秒數
let begin = new Date(year, month, 1).getTime();
let end = new Date(year, month + 1, 0).getTime();
while (begin <= end) {
// 享元模式,避免重複 new Date
this.shareDate.setTime(begin);
let year = this.shareDate.getFullYear();
let curMonth = this.shareDate.getMonth();
let date = this.shareDate.getDate();
list.push({
year: year,
month: curMonth,
date: date,
value: this.stringify(year, curMonth, date)
});
begin += oneDayMS;
}
this.calendarList = list;
}
},
});
複製代碼
這一步,咱們要作的有兩點:
①:把星期列表顯示出來,
②:調整 methods
中的 calendarCreator
函數,顯示正確的星期順序。
爲了解決這個問題,咱們須要增長兩個函數,判斷當前月第一天是星期幾,和最後一天是星期幾。這樣才能正確顯示。
咱們注意一下 calendarCreator
函數,裏面有個 begin
和 end
變量,分別表明當前月份第一天和最後一天的毫秒數。聰明的讀者應該已經發現,若是須要補全的話,咱們能夠在begin
的基礎上,再往前移動幾天(減去這幾天的毫秒數),在end
的基礎上,再日後移動幾天(加上這幾天的毫秒數),就能夠實現這個效果了。具體移動多少,須要咱們經過計算獲得。
咱們先完成 星期一至星期日的顯示
<div class="calendar-week">
<div class="week-item" v-for="item of weekList" :key="item">{{ item }}</div>
</div>
複製代碼
data() {
return {
// 省略部分代碼...
weekList: ['一', '二', '三', '四', '五', '六', '日'], // 新增
}
},
複製代碼
再往 methods
裏增長兩個函數:
// 當前月的第一天是星期幾
getFirstDayByMonths(year, month) {
return new Date(year, month, 1).getDay();
},
// 當前月的最後一天是星期幾
getLastDayByMonth(year, month) {
return new Date(year, month + 1, 0).getDay();
},
複製代碼
調整 calendarCreator
函數,主要是調整 begin
和 end
的計算方式:
calendarCreator() {
// 省略部分代碼...
// 當前月份第一天是星期幾, 0-6
let firstDay = this.getFirstDayByMonths(year, month);
// 填充多少天,由於我將星期日放到最後了,因此須要另外調整下
let prefixDaysLen = firstDay === 0 ? 6 : firstDay - 1;
// 向前移動以後的毫秒數
let begin = new Date(year, month, 1).getTime() - (oneDayMS * prefixDaysLen);
// 當前月份最後一天是星期幾, 0-6
let lastDay = this.getLastDayByMonth(year, month);
// 填充多少天,由於我將星期日放到最後了,因此須要另外調整下
let suffixDaysLen = lastDay === 0 ? 0 : 7 - lastDay;
// 向後移動以後的毫秒數
let end = new Date(year, month + 1, 0).getTime() + (oneDayMS * suffixDaysLen);
// 省略部分代碼...
}
複製代碼
對於不屬於當前月的日期,咱們須要將其置灰,因此須要調整下 calendarCreator
的 while
循環,
給 calendarList
數據加一個 disable
屬性,而後在 html
那裏記得加樣式處理:
list.push({
year: year,
month: curMonth,
date: date,
disable: curMonth !== month, // 新增 disable 屬性
value: this.stringify(year, curMonth, date)
});
複製代碼
:class="[item.disable ? 'disabled' : '']"
複製代碼
至此,一個簡單的日曆就開發完了。如圖:
css 我就不美化了,懶。。。
一些點擊事件呀,hover效果呀,啥的,看官能夠根據業務需求自行加上。
至於,改變年份,思想也是相似的。
但願能給有須要的讀者帶來幫助。
源碼在此 vue-calendar