最近有個 Ionic 專案需要把按鍵換頁式的日曆改成垂直滾動式,在這紀錄一下修改過程。
元件規劃 原本的 Calendar Componet 長成下圖的樣子,預設顯示目前月份,可以點擊按鈕,切換上、下個月的視圖。
計畫是把該功能拆分成兩個 Component,最後再組合起來,功能分別如下
month-view:顯示某月份日曆 scroll-calendar:控制卷軸與動態顯示 month-view 操作情境為 scroll-calendar 顯示目前,與近幾個月的日曆,當卷軸往上或往下時,自動長出後續的 month-view;下圖用 IOS 的日曆來解釋。
展示用的專案架構。
實作開始 步驟(一) month-view 顯示某月份日期 將原本的 calendar 改成 month-view,控制顯示月份的month
參數加上@Input
裝飾器,使其可以接收到 scroll-calendar 傳入的月份。
1 2 3 4 5 6 7 export class MonthViewComponent implements OnInit { @Input("month" ) month: moment.Moment = moment(); }
為了顯示目前月份與前後 n 個月,在這裡定義目前為current
,n 為buffer
,組出這個區間的資料後,用迴圈顯示 month-view。
1 2 3 4 5 6 7 8 9 10 11 12 13 export class ScrollCalendarPage implements OnInit { constructor () {} current: moment.Moment = moment(); buffer = 4 ; months: Array <moment.Moment> = []; ngOnInit() { for (let i = -this .buffer; i <= this .buffer; i++) { this .months.push(this .current.clone().add(i, "months" )); } } }
1 2 3 4 5 6 7 8 9 <ion-header > <ion-toolbar color ="primary" > <ion-title > scroll-calendar</ion-title > </ion-toolbar > </ion-header > <ion-content #content > <app-month-view *ngFor ="let month of months" [month ]="month" > </app-month-view > </ion-content >
這時應該要能正確顯示正負四個月的 month-view。
可以注意到一開始的卷軸會在最上方,而不是當前月份的位置(二月),若要做到比較好的效果,可以在迴圈時添加 month-view 元件的id
,並配合 content 的 scrollToPoint,將卷軸移至指定 month-view。
1 2 3 4 5 6 7 <ion-content #content > <app-month-view *ngFor ="let month of months; let index = index;" [month ]="month" id ="monthView_{{index}}" > </app-month-view > </ion-content >
1 2 3 4 5 6 7 8 9 10 export class ScrollCalendarPage implements OnInit { @ViewChild("content" , { static : true }) contentElement: IonContent; ionViewWillEnter() { let currentMonthView = document .getElementById(`monthView_${this .buffer} ` ); this .contentElement.scrollToPoint(0 , currentMonthView.offsetTop); } }
要做到無限滾動卷軸也很簡單,對迴圈的資料來源months
做點變動就行了;這邊監聽卷軸事件,當卷軸往下滾時,若 scrollTop 高於目前月份(month-view)的offsetTop
,則對 months 插入最後一筆資料,同時,為了避免頁面內容過於肥大,也刪除第一筆資料;卷軸往上也做相對應的處理。
1 2 3 4 5 6 7 8 <ion-content #content scrollEvents ="true" (ionScroll )="onScroll($event)" (ionScrollEnd )="onScroll($event)" > </ion-content >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 export class ScrollCalendarPage implements OnInit { onScroll = (event: CustomEvent ) => { if ( event.detail.scrollTop < document .getElementById(`monthView_${this .buffer} ` ).offsetTop ) { this .current.subtract(1 , "months" ); this .months.unshift(this .current.clone().subtract(this .buffer, "months" )); this .months.pop(); return ; } if ( event.detail.scrollTop > document .getElementById(`monthView_${this .buffer + 1 } ` ).offsetTop ) { this .current.add(1 , "months" ); this .months.splice(0 , 1 ); this .months.push(this .current.clone().add(this .buffer, "months" )); return ; } }; }
Demo 可以看到這個頁面永遠只會顯示buffer * 2 + 1
個 month-view,一個簡單的垂直滾動日曆就這樣完成了。