Vue写一个日历
基礎回顧
關于 Date API
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 是可以傳入負數的。
分析
做成啥樣子呢,如圖:
怎么做呢,一步步來分析:
①:計算出當前月份有多少天,遍歷顯示出來
②:點擊上一月,下一月時,改變月份,并重新執行第一步
③:計算月初和月末分別對應星期幾,并補全月初和月末的空位。如上圖的 xx 位置。
第一、二步是很好處理的,主要是第三步,有些復雜。
我不想拼接 dom,所以就采用 vue來開發了,省時省力。
開始吧。
第一步和第二步
寫好 html 和 css
<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 邏輯
感覺前兩步是比較簡單的,我就直接貼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 的數字,前面補 0pad(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;},// 設置或初始化 currentsetCurrent(d = new Date()) {let year = d.getFullYear();let month = d.getMonth();let date = d.getDate();this.current = {year,month,date}},// 修正 currentcorrectCurrent() {let { year, month, date } = this.current;let maxDate = this.getDaysByMonth(year, month);// 預防其他月跳轉到2月,2月最多只有29天,沒有30-31date = 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 Datethis.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-6let firstDay = this.getFirstDayByMonths(year, month);// 填充多少天,因為我將星期日放到最后了,所以需要另外調整下 let prefixDaysLen = firstDay === 0 ? 6 : firstDay - 1;// 向前移動之后的毫秒數let begin = new Date(year, month, 1).getTime() - (oneDayMS * prefixDaysLen);// 當前月份最后一天是星期幾, 0-6let 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
參考
MDN
轉載于:https://juejin.im/post/5c837814e51d453d8023fe12
總結
- 上一篇: python闭包、装饰器
- 下一篇: js高级技巧之柯里化