QingUI是一個UI組件庫
目前擁有的組件:DatePicker, TimePicker, Paginator, Tree, Cascader, Checkbox, Radio, Switch, InputNumber, Input
ES6語法編寫,無依賴
原生模塊化,Chrome63以上支持,請開啓靜態服務器預覽效果
採用CSS變量配置樣式
辛苦造輪子,歡迎來github倉庫star:github.com/veedrin/qin…
四月份找工做,求內推,座標深圳javascript
去年年末項目中嘗試着寫過一個分頁的Angular組件,而後就有了寫QingUI的想法java
過程仍是很是有意思的git
接下來我會用幾篇文章分別介紹每一個組件的大概思路,請你們耐心等待github
這一篇介紹DatePicker日期選擇器緩存
最重要的,求star,求fork,求內推服務器
ES6的類語法糖很是順手,用ES6寫代碼就像吃德芙巧克力同樣哈哈佈局
而後DatePicker和TimePicker有一些公共的方法,我用一個公共類,讓它們倆繼承flex
extends就是繼承關鍵字ui
super是用來繼承父類的this對象的,它必須首先調用,不然沒法找到this
主要是爲了照顧對ES6不熟悉的人
class Common {
constructor() {}
}
class DatePicker extends Common {
constructor() {
super();
}
}
複製代碼
不知道你們觀察過沒有,每一頁日曆都是按星期來排的,展現當月的全部日期
因此,若是該月的1號不是星期一或者星期天呢?那還要從上個月抓幾天過來,把空補齊,月底也同樣
爲了方便,下面都以星期一爲一週起始日
當月全部日期容易,獲取當月有多少天,for循環加到模板裏
那月首補齊的呢?我要知道這個月1號是星期幾,還要知道上個月最後一天是多少號,而後少幾天,往前推幾天就行了
月底補齊的就簡單一點,我要知道這個月最後一天是星期幾,而後少幾天,從1號日後加幾天
const daysCountThisMonth = this.daysCountThisMonth();
const daysCountLastMonth = this.daysCountThisMonth(-1);
// weekie指的是星期幾(自創)
const weekieFirstDay = this.weekieOfSomedayThisMonth(1);
const weekieLastDay = this.weekieOfSomedayThisMonth(daysCountThisMonth);
if (weekieFirstDay > 1) {
for (let i = daysCountLastMonth - weekieFirstDay + 2; i <= daysCountLastMonth; i++) {
tpl += `<span class="day-disable">${i}</span>`;
}
}
for (let i = 1; i <= daysCountThisMonth; i++) {
tpl += `<span class="day">${i}</span>`;
}
if (weekieLastDay < 7) {
for (let i = 1; i <= 7 - weekieLastDay; i++) {
tpl += `<span class="day-disable">${i}</span>`;
}
}
複製代碼
這個月有多少天怎麼算?
月份參數傳入下個月,日期參數傳入0,就能夠得到當月最後一天是多少號了
我這裏this.M
就是當月,而程序的月份是要減1的,因此就至關於下個月了
某一天是星期幾好求,我就不列出來了
function daysCountThisMonth(num = 0) {
return new Date(this.Y, this.M + num, 0).getDate();
}
複製代碼
至此,加上flex佈局,一個靜態的日曆就作出來了
還有一個小細節,我選中的日期要高亮,當天也要有一個背景色,它們倆在初始的時候還應該是重合的
當天嘛,我緩存的年月和實時獲取的年月相等,再把實時獲取的日期和i比對,就是當天,加個today
的class
而若是實時獲取的日期和緩存的日期還相等,那麼這天不只是當天,仍是用戶選中的日子,加個today active
的class
剩下的就是用戶選中的不是當天,和徹底普通的日子
因爲選擇年月的時候,日曆都要從新渲染,因此咱們只能根據這些條件來判斷,看起來確實有些複雜
for (let i = 1; i <= daysCountThisMonth; i++) {
if (this.Y === Y && this.M === M && i === D && i !== this.D) {
tpl += `<span class="day today">${i}</span>`;
} else if (this.Y === Y && this.M === M && i === D && i === this.D) {
tpl += `<span class="day today active">${i}</span>`;
} else if (i === this.D) {
tpl += `<span class="day active">${i}</span>`;
} else {
tpl += `<span class="day">${i}</span>`;
}
}
複製代碼
我以前覺得實例化時把當前年月日緩存起來,而後再把用戶選中的年月日緩存起來,二者一對比,就能夠渲染了
其實我沒有注意到一個問題,就是使用日曆是有可能跨天的,理論上,若是永遠不關機,跨年均可以
若是我在午夜12點左右操做QingUI,那緩存的日期就有可能不許確,由於已經跨天了
至於我是如何發現這個BUG的,你猜?
因此每次渲染都要實時獲取當前年月日
ES6的解構賦值可讓這個操做很是優美
function nowDate() {
const date = new Date();
return [date.getFullYear(), date.getMonth() + 1, date.getDate()];
}
const [Y, M, D] = nowDate();
複製代碼
話說就由於這個BUG,我幾乎重構了整個組件
最後,用戶選中高亮是經過添加class的方式實現的,高亮新的日子,而後把舊的日子高亮去掉
注意,這種操做是不須要從新渲染的,因此通常作法是,把全部日子循環一遍,去掉高亮,而後添加新的高亮
若是咱們將上一個高亮的日子緩存起來呢?是否是就不用每次for循環了,因而就有了this.oldD
選擇年月有兩種方式,一種是點開面板,直接選擇,一種是點擊三角,每次增1或減1
點擊三角的時候,我設置成1月再減1,就會變成年份減1,月份變成12月,也就是說月份是能夠一直點的
// 置灰時獲取不到元素
if ($monthPrev) {
$monthPrev.addEventListener('click', function(event) {
event.stopPropagation();
self.M--;
if (self.M > 0) {
self.yearAndMonthChange('month');
} else {
self.M = 12;
self.Y--;
self.yearAndMonthChange('both');
}
});
}
複製代碼
可真的是這樣嗎?還記得有一個配置項yearRange
嗎?
若是年份到了頭,就置灰不能再點了,這是我遇到的第二個坑
若是年份到了頭,年份的減三角就要置灰,可是月份的減三角仍是能夠點
直到月份變成1,那麼年份和月份的減三角都置灰了
加三角的邏輯也同樣
這個邏輯,大家理一理
const [left, right] = this.yearRange;
let tpl = ` <div class="bar"> <div class="bar-item"> <span class="${this.Y > left ? 'angle year-prev' : 'angle disabled'}">◀</span> <span class="year-pop"></span> <span class="${this.Y < right ? 'angle year-next' : 'angle disabled'}">▶</span> </div> <div class="bar-item"> <span class="${this.Y === left && this.M === 1 ? 'angle disabled' : 'angle month-prev'}">◀</span> <span class="month-pop"></span> <span class="${this.Y === right && this.M === 12 ? 'angle disabled' : 'angle month-next'}">▶</span> </div> </div> `;
複製代碼
選擇年份和月份面板,我以前是作成pop彈窗加滾動條的,發現體驗很糟糕,因而參考ElementUI作到了日曆面板上
這就帶來一個問題,顯示年份面板的時候,日曆其實是清除了,選擇完之後再從新渲染日曆,繼續重構...
月份簡單,一個面板就顯示完了
年份有可能一個面板顯示不完,但再怎麼樣,也比年份和月份聯動的狀況要簡單是吧
我把用戶選擇的年份緩存起來,由於我但願把用戶選中過的年份放在比較中間的位置,他下次再選的時候,能夠從這裏繼續
再結合yearRange
,效果是這樣的
const [left, right] = this.yearRange;
const start = this.anchor - 4 > left ? this.anchor - 4 : left;
const end = this.anchor + 7 < right ? this.anchor + 7 : right;
const [Y, , ] = this.nowDate();
let tpl = `<div class="title">${this.lang === 'en' ? 'Choose a Year' : '選擇年份'}</div>`;
if (this.anchor - 4 > left) {
tpl += '<div class="prev">◀</div>';
} else {
tpl += '<div class="prev-disabled">◀</div>';
}
tpl += '<div class="year-wrap">';
for (let i = start; i <= end; i++) {
if (i !== Y) {
tpl += `<span class="year">${i}</span>`;
} else {
tpl += `<span class="year thisyear">${i}</span>`;
}
}
tpl += '</div>';
if (this.anchor + 7 < right) {
tpl += '<div class="next">▶</div>';
} else {
tpl += '<div class="next-disabled">▶</div>';
}
複製代碼
DatePicker比較核心的邏輯就在這裏了
400行左右的代碼,每一個人均可以嘗試着寫一遍,頗有意思的
下一篇文章介紹TimePicker,敬請期待
最後,求star,求fork,求內推