純手工打造的日期選擇器 xy-date-picker 原理與使用

最近花了差不多快兩週的空閒時間打造了一個日期選擇組件,先看看效果

clipboard.png

可以說是一個經常要用到,很少人會主動去實現的一個組件,畢竟實現起來還是要一定的時間的,所以平時工作之餘就可以試着打造一些基礎組件庫,既可以鍛鍊自己的基本功,又可以爲公司爲社區做貢獻~

先看一下用法吧,推薦查看文檔,可以實時交互

安裝使用

通常有以下幾種安裝方式

  • npm
npm i xy-ui
  • cdn
<script type="module" src="https://unpkg.com/xy-ui/components/xy-pagination.js"></script>

<!--或者-->

<script type="module">
    import 'https://unpkg.com/xy-ui/components/xy-pagination.js'
</script>
  • github

直接從github拷貝源碼。

使用

<xy-date-picker></xy-date-picker>

一個標籤的事,開箱即用,可能是使用起來最方便的日期選擇器了。

實現原理

這裏簡單介紹一下日期的生成原理

要實現一個月的展示,一般分爲三個部分,分別爲前一個月,當前月,下一個月

clipboard.png

爲此,我們需要知道

1.前一個月的最後一天
2.當前月的天數及當前月第一天是周幾(便於擺放位置)

假如前一個月最後一天是31號,並且當月的第一天是週二及總天數30,那麼這個月的排列就確定了

30 31 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 1 2 3

首先是獲取一個月的最後一天,通常每個月的天數是固定的,只有二月不固定,有一種做法是把二月單獨列出來,通過計算閏年的方式來判斷是29還是28。

其實還有一種方式可能方便,我們可以直接利用日期的容錯機制,比如

new Date(2019,2,1) //2019年3月1日
new Date(2019,2,0) //2019年3月1日的前一天,也就是2019年2月的最後一天

然後獲取當天的日期,也就是當月天數

new Date(2019,2,0).getDate() //28

然後是獲取一個月的第一天是星期幾,這個比較容易

new Date(2019,2,1).getDay() //5,週五

然後我們可以通過這些信息組合出一個月份信息

function getDays(year,month){
    const lastdays = new Date(year,month-1,0).getDate();
    const days = new Date(year,month,0).getDate();
    const week = new Date(year,month-1,1).getDay();
    const prev = Array.from({length:week},(el,i)=>(month==0?year-1:year)+'-'+(month==0?12:month-1)+'-'+(lastdays+i-week+1));
    const current = Array.from({length:days},(el,i)=>year+'-'+month+'-'+(i+1));
    const next = Array.from({length:42 - days - week},(el,i)=>(month==12?year+1:year)+'-'+(month==12?1:month+1)+'-'+(i+1));
    return [...prev,...current,...next];
}

這裏簡單做了一個在控制檯輸出日曆

clipboard.png

源碼如下,小夥伴可以試試

function renderCalendar(d){ 
        const date = new Date(d||new Date); 
        const year = date.getFullYear(); 
        const month = date.getMonth(); 
        const day = date.getDate(); 
        const lastdays = new Date(year,month,0).getDate(); 
        const days = new Date(year,month+1,0).getDate(); 
        const week = new Date(year,month,1).getDay(); 
        const prev = Array.from({length:week},(el,i)=>([month==0?year-1:year,month==0?12:month,lastdays+i-week+1])); 
        const current = Array.from({length:days},(el,i)=>[year,month+1,i+1]); 
        const next = Array.from({length:42 - days - week},(el,i)=>([month==11?year+1:year,month==11?1:month+2,i+1])); 
        const final = [...prev,...current,...next]; 
        const now = Array.from({length:6},(el,i)=>final.slice(i * 7, i * 7 + 7)); 
        const s = `———————————————————————————————————— 
             ${year+' - '+(month+1+'').padStart(2,0)} 
%c———————————————————————————————————— 
| Su | Mo | Tu | We | Th | Fr | Sa | 
———————————————————————————————————— 
%c${now.map(el=>el.map(m=>(m[2]==1?'%c':'')+'| '+((m[2]+'').padStart(2,' ')+' ')).join('')+'|\n————————————————————————————————————\n').join('')} 
        ` 
        console.clear(); 
        console.log(s,"font-weight:bold;color:blue","color:#999","color:#000","color:#999") 
    }

以上就是日期生成原理了,進一步可以實現日期切換,單選,範圍選擇等功能。

屬性

xy-date-picker定義以下幾種屬性,結合使用適用性更廣。

默認值defaultvalue

可以給日期選擇器指定一個初始日期defaultvalue,取值爲合法的時間戳字符串DataString,默認爲當前日期。

支持形如以下的字符串,可參考Date.parse()

"2019-2-28"
"2019-02-28"
"2019/2/28"
"2019,2,28"
"2019 2 28"
"Feb 28 2019"
//...其他日期格式
//以上均表示2019年2月28日。
<xy-date-picker defaultvalue="2019-2-28"></xy-date-picker>

clipboard.png

類型type

支持設置日期選擇類型,可選擇date(默認)、monthyear,分別實現日期選擇器、月選擇器、年選擇器。

<xy-date-picker></xy-date-picker>
<xy-date-picker type="month"></xy-date-picker>
<xy-date-picker type="year"></xy-date-picker>

clipboard.pngclipboard.pngclipboard.png

value、日期date

設置或返回日期選擇器的value屬性值。值爲當前類型下的日期,格式形如2019-10-10

返回日期的標準格式date,可以將值轉換成任意格式的日期。

//value
"2019-08-31"
//date
"Sat Aug 31 2019 14:54:05 GMT+0800 (中國標準時間)"

可以通過JavaScript設置或獲取。

date.value; //獲取
date.date; //獲取
date.value = '2019-10-10';
//原生屬性操作
date.getAttribute('value');
date.getAttribute('date');
date.setAttribute('value','2019-10-10');

最小值min、最大值max

設置日期選擇範圍,超出範圍之外的不可選中,格式同defaultvalue

<xy-date-picker min="2019-8-20" max="2019-12-21"></xy-date-picker>
<xy-date-picker type="month" min="2019-5" max="2019-12"></xy-date-picker>
<xy-date-picker type="year" min="2018" max="2050"></xy-date-picker>

clipboard.pngclipboard.pngclipboard.png

禁用disabled

通過disabled可以禁用,禁用後無法打開日期選擇器。

<xy-date-picker disabled></xy-date-picker>

clipboard.png

方向

通過dir可以設置日期選擇器方向,默認爲bottomleft,可以取值toprightbottomlefttoplefttoprightrighttoprightbottombottomleftbottomrightlefttopleftbottom。當你的日期選擇器位於屏幕邊緣時可以調整該屬性。

<xy-date-picker dir="righttop"></xy-date-picker>

clipboard.png

範圍選擇range

添加range屬性可以實現日期範圍選擇。

<xy-date-picker range></xy-date-picker>

clipboard.png

可以指定一個默認範圍defaultvalue,格式形如2019-10-10~2019-12-31,用~鏈接。默認爲當前日期。

<xy-date-picker range defaultvalue="2019-10-10~2019-12-31"></xy-date-picker>

範圍選擇模式下的valuedate均爲數組

//value
["2019-05-15", "2019-12-26"]
//date
["Wed May 15 2019 08:00:00 GMT+0800 (中國標準時間)", "Thu Dec 26 2019 08:00:00 GMT+0800 (中國標準時間)"]

事件event

當選好日期後,按確定按鈕可以觸發change回調。

<xy-date-picker onchange="console.log(event)"></xy-date-picker>

clipboard.png

event:{
    detail:{
        value,
        date
    }
}

其他觸發方式

date.onchange = function(ev){
    console.log(this.value);
    console.log(this.date);
    console.log(ev.target.value);
    console.log(ev.detail.value);
    console.log(ev.detail.date);
}

date.addEventListener('change',function(ev){
    console.log(this.value);
    console.log(this.date);
    console.log(ev.target.value);
    console.log(ev.detail.value);
    console.log(ev.detail.date);
})

其他

xy-date-picker內部基於xy-popoverxy-date-pane實現。

<xy-popover >
    <xy-button class="date-btn"></xy-button>
    <xy-popcon>
        <xy-date-pane id="color-pane"></xy-date-pane>
        <div class="pop-footer">
            <xy-button id="btn-cancel">取消</xy-button>
            <xy-button type="primary" id="btn-submit">確認</xy-button>
        </div>
    </xy-popcon>
</xy-popover>

其中,xy-date-pane爲日期選擇面板,可獨立使用,如果你只是想要一個日曆,可以使用這個組件。

<xy-date-pane></xy-date-pane>

clipboard.png

事件和屬性與xy-date-picker一致。

小節

總體來說,xy-date-picker使用起來還是很容易的,無需寫大量腳本,就像使用一個input,大部分情況只需知道一個onchange事件就可以了。

其實xy-ui的組件設計都是靠近原生的,沒有那些自創的事件,比如這裏的onchange有些日期組件庫就喜歡搞成datechange什麼的,雖然語義很好,但是不夠原生,不夠統一,用戶完全無感,不看文檔完全不知道下手,反倒是原生更具有代表性,規範性,可以說拿到這個組件,往頁面上一放,根本無需看api文檔即可上手大部分功能。

目前xy-ui已經完成了大部分常見組件,後續有其他會陸續補充,希望有需要的小夥伴可以馬上用起來,也希望可前往github給顆star,謝謝~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章