微信小程序日歷組件開發(fā)案例

今天我們一起寫一個微信小程序日歷組件

微信小程序日歷組件
https://github.com/749264345/wx-calendar

好簇秒,我們先看一下要實現(xiàn)的模樣诀浪,如下圖


52魔都

52魔都

由以上截圖我們可以看到

1.日歷可以通過按鈕【切換展示效果】改變?nèi)諝v的呈現(xiàn)效果,上圖是平鋪模式,下圖是收起滾動模式。
2.通過點擊具體的日期可以在頁面上顯示當(dāng)前選中的具體日期。
3.點擊【今天】快速回到當(dāng)日視圖笨腥。
4.點擊【?】和【?】切換月份。
上面的四點也是基本的交互需求勇垛,我們馬上開始脖母。
首先,我們先結(jié)構(gòu)后樣式闲孤,做出最基本的界面結(jié)構(gòu)
這邊我們把整體結(jié)構(gòu)分成上中下谆级,操作顯示區(qū),星期顯示區(qū)讼积,日期顯示區(qū)肥照。

<view class='calendar'>
    <!--顯示當(dāng)前年月日-->
    <view class='calendar-title'>
        <view class='item ctrl' bindtap='lastMonth'>{{lastMonth}}</view>
        <view class='item title'>{{title}}</view>
        <view class='item ctrl' bindtap='nextMonth'>{{nextMonth}}</view>
        <view class='item ctrl today' bindtap='today'>今天</view>
    </view>

    <!--星期-->
    <view class='calendar-week'>
        <view class='item'>{{item}}</view>
    </view>

    <!--日期-->
    <view class='calendar-container'>
        <!--上個月占位格子-->
        <view class='grid gray'>{{item}}</view>

        <!--當(dāng)月格子-->
        <view class='grid'>
            <view class="wrap">{{item.date}}</view>
        </view>

        <!--下個月占位格子-->
        <view class='grid gray'>{{item}}</view>
    </view>
</view>

這是我們基本的日歷結(jié)構(gòu),機(jī)智的小伙伴已經(jīng)從布局中知道我們實現(xiàn)的大致邏輯了勤众,是的舆绎,我們先獲取當(dāng)月有多少天,上月和下月有多少天们颜,這樣我們的日歷就出來了亿蒸。好,慢慢來掌桩,下面我們詳細(xì)說边锁,我們先寫上基本的樣式。

.calendar {
    width: 100%;
    text-align: center;
    font-size: 30rpx;
    box-sizing: border-box;
}

/* 標(biāo)題 */
.calendar-title {
    line-height: 70rpx;
    font-size: 30rpx;
    text-align: left;
    padding: 0 20rpx;
    box-sizing: border-box;
}

.calendar-title .ctrl {
    display: inline-block;
    padding: 0 20rpx;
    background: #f5f5f5;
    border-radius: 10rpx;
}

.calendar-title .item {
    display: inline-block;
    vertical-align: middle;
    line-height: 50rpx;
}

.calendar-title .title {
    min-width: 300rpx;
    text-align: center;
}

.calendar-title .today {
    float: right;
    margin-top: 10rpx;
}

/* 星期 */
.calendar-week {
    display: flex;
    text-align: center;
    padding: 20rpx 10rpx;
    box-sizing: border-box;
    border-top: 1rpx solid #e0e0e0;
    border-bottom: 1rpx solid #e0e0e0;
    background: #f5f5f5;
}

.calendar-week .item {
    flex: 1;
}

/* 日期 */
.calendar-container {
    display: flex;
    flex-wrap: wrap;
    padding: 20rpx 10rpx;
    box-sizing: border-box;
}

.calendar-container .grid {
    display: inline-block;
    width: 14.28571428571429%;
    line-height: 70rpx;
    position: relative;
    z-index: 1;
}

.calendar-container .grid.gray {
    color: #ccc;
}

.calendar-container .grid .wrap.select {
    background: rgb(49, 120, 228);
    border-radius: 10rpx;
    color: #fff;
    width: 80%;
    margin: 0 auto;
}

以上我們基本試下了日歷的界面波岛,下面我們來實現(xiàn)星期和日期的展示茅坛。
好,我們先顯示星期则拷,我們先在組件中定義一個數(shù)組贡蓖,用來遍歷顯示星期的標(biāo)題;

Component({
    properties: {
        //星期數(shù)組
        weekText: {
            type: Array,
            value: ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
        }
    },
    ...
})

我們將星期的標(biāo)題定義為可配置的模式煌茬,默認(rèn)顯示如上的文字斥铺,之后我們可以在組件外自定義,個性化顯示坛善。于是我們調(diào)整下wxml的代碼晾蜘。

    <!--遍歷星期-->
    <view class='calendar-week'>
        <view wx:for='{{weekText}}' class='item' wx:key='{{item}}'>{{item}}</view>
    </view>

這樣我們就能看到我們想要的效果邻眷。


52魔都

下面我們開始日期的顯示,我們先獲取當(dāng)月有幾天剔交,這里的核心代碼是

new Date(year, month, date).getDate();

http://www.w3school.com.cn

由此我們做如下嘗試


52魔都

我們?nèi)缙讷@得了返回值肆饶,而當(dāng)我們傳入日期為0時返回了31為當(dāng)月的全部天數(shù)。

由于JavaScript中day的范圍為1~31中的值岖常,所以當(dāng)設(shè)為0時驯镊,會向前 一天,也即表示上個月的最后一天竭鞍,通過這種方式可以得到每個月份的天數(shù)板惑。

知道了獲取當(dāng)月天數(shù)的原理,我們還需要知道當(dāng)月1號是星期幾偎快。
我們使用如下的方法:

new Date(Date.UTC(year, month-1, date)).getDay();

http://www.w3school.com.cn

我們同樣在控制臺做出調(diào)試冯乘;


52魔都

需要注意的是,上面的month是實際的月份滨砍,而下面這個方法需要在實際的月份上減去1往湿。
于是我們獲取0-6之間的值,分別對應(yīng)周日~周六惋戏。
值為6是周六领追,值為0是周日。
由于日歷的第一天是周日响逢,周日對應(yīng)的是0绒窑,于是傳入每月1日,返回值為多少舔亭,就是星期幾些膨,也就說明當(dāng)月1日前面空幾格。
知道了當(dāng)月就幾天钦铺,當(dāng)月前面有幾天订雾,我們做一下算法就可以得出,當(dāng)月后面有幾天矛洞,于是我們建立如下函數(shù):

    // 組件的初始數(shù)據(jù)
    data: {
        //當(dāng)月格子
        thisMonthDays: [],
        //上月格子
        empytGridsBefore: [],
        //下月格子
        empytGridsAfter: [],
},

methods: {
        //獲取當(dāng)月天數(shù)
        getThisMonthDays: function (year, month) {
            return new Date(year, month, 0).getDate();
        },
        // 繪制當(dāng)月天數(shù)占的格子
        createDays: function (year, month) {
            let thisMonthDays = [],
                days = this.getThisMonthDays(year, month);
            for (let i = 1; i <= days; i++) {
                thisMonthDays.push({
                    date: i,
                    dateFormat: this.zero(i),
                    monthFormat: this.zero(month),
                    week: this.data.weekText[new Date(Date.UTC(year, month - 1, i)).getDay()]
                });
            }
            this.setData({
                thisMonthDays
            })
        },
        //獲取當(dāng)月空出的天數(shù)
        createEmptyGrids: function (year, month) {
            let week = new Date(Date.UTC(year, month - 1, 1)).getDay(),
                empytGridsBefore = [],
                empytGridsAfter = [],
                emptyDays = (week == 0 ? 7 : week);
            //當(dāng)月天數(shù)
            var thisMonthDays = this.getThisMonthDays(year, month);
            //上月天數(shù)
            var preMonthDays = month - 1 < 0 
                ? this.getThisMonthDays(year - 1, 12) 
                : this.getThisMonthDays(year, month - 1);

            //空出日期
            for (let i = 1; i <= emptyDays; i++) {
                empytGridsBefore.push(preMonthDays - (emptyDays - i));
            }

            var after = (42 - thisMonthDays - emptyDays) - 7 >= 0 
                        ? (42 - thisMonthDays - emptyDays) - 7 
                        : (42 - thisMonthDays - emptyDays);
            for (let i = 1; i <= after; i++) {
                empytGridsAfter.push(i);
            }
            this.setData({
                empytGridsAfter,
                empytGridsBefore
            })
        },

        //補(bǔ)全0
        zero: function (i) {
            return i >= 10 ? i : '0' + i;
        },
}

我們同樣修改下wxml代碼洼哎,同時我們?yōu)樯显拢略抡颖荆裉熵停齻€按鈕添加相關(guān)事件監(jiān)聽。

    <!--顯示當(dāng)前年月日-->
    <view class='calendar-title'>
        <view class='item ctrl' bindtap='lastMonth'>{{lastMonth}}</view>
        <view class='item title'>{{title}}</view>
        <view class='item ctrl' bindtap='nextMonth'>{{nextMonth}}</view>
        <view class='item ctrl today' bindtap='today'>今天</view>
    </view>
<!--上個月占位格子-->
<view class='grid gray' wx:for='{{empytGridsBefore}}' wx:key='{{item}}'>{{item}}</view>

<!--當(dāng)月格子-->
<view class='grid' wx:for='{{thisMonthDays}}' wx:key='{{indx}}'>
   <view class='self' wx:if="{{ format === year+'-'+item.monthFormat+'-'+item.dateFormat }}"></view>
   <view class="wrap {{ select === year+'-'+item.monthFormat+'-'+item.dateFormat ? 'select' :''}}" bindtap='select' data-date='{{item.date}}'>{{item.date}}</view>
</view>

<!--下個月占位格子-->
<view class='grid gray' wx:for='{{empytGridsAfter}}' wx:key='{{item}}'>{{item}}</view>

相關(guān)的事件監(jiān)聽:

//默認(rèn)選中當(dāng)天 并初始化組件
today: function () {
    let DATE = this.data.defaultValue ? new Date(this.data.defaultValue) : new Date(),
        year = DATE.getFullYear(),
        month = DATE.getMonth() + 1,
        date = DATE.getDate(),
        select = year + '-' + this.zero(month) + '-' + this.zero(date);

    this.setData({
        format: select,
        select: select,
        year: year,
        month: month,
        date: date,
        YEAR: year,
        MONTH: month,
        DATE: date,
    })

    //初始化日歷組件UI
    this.display(year, month, date);

    //發(fā)送事件監(jiān)聽
    this.triggerEvent('select', select);
},
//上個月
lastMonth: function () {
    let month = this.data.month == 1 ? 12 : this.data.month - 1;
    let year = this.data.month == 1 ? this.data.year - 1 : this.data.year;
    //初始化日歷組件UI
    this.display(year, month, 0);
},
//下個月
nextMonth: function () {
    let month = this.data.month == 12 ? 1 : this.data.month + 1;
    let year = this.data.month == 12 ? this.data.year + 1 : this.data.year;
    //初始化日歷組件UI
    this.display(year, month, 0);
},

代碼中我們使用this.display(year, month, 0)為組件統(tǒng)一初始化抽兆;
同時在today函數(shù)中我們添加事件監(jiān)聽函數(shù)识补,將選中的日期發(fā)送到頁面,我們通過事件訂閱來獲取的相關(guān)值辫红。

<Calendar id="Calendar" bind:select="select"></Calendar>
//組件監(jiān)聽事件
select(e) {
    this.setData({
        selectVal:e.detail
    })
},

最后我們?yōu)榍袚Q顯示效果的按鈕添加事件:

    toggleType(){
        this.selectComponent('#Calendar').toggleType();
    }

組件中對應(yīng)的方法凭涂,每當(dāng)切換展示效果祝辣,組件都需要初始化

        //切換展示
        toggleType(){
            this.setData({
                toggleType: this.data.toggleType == 'mini' ? 'large' :'mini'
            })
            //初始化日歷組件UI
            this.display(this.data.year, this.data.month, this.data.date);
        },

以上基本上是小程序日歷組件實現(xiàn)的基本邏輯,介于篇幅太長還有很多實現(xiàn)上的細(xì)節(jié)不在此一一細(xì)說导盅,大家可以移步我的github

微信小程序日歷組件
https://github.com/749264345/wx-calendar

上文中有不足之處较幌,請給出建議或更優(yōu)的實現(xiàn)方案揍瑟,謝謝~
最后祝大家五一快樂~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末白翻,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绢片,更是在濱河造成了極大的恐慌滤馍,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件底循,死亡現(xiàn)場離奇詭異巢株,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)熙涤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門阁苞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人祠挫,你說我怎么就攤上這事那槽。” “怎么了等舔?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵骚灸,是天一觀的道長。 經(jīng)常有香客問我慌植,道長甚牲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任蝶柿,我火速辦了婚禮丈钙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘交汤。我一直安慰自己雏赦,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布蜻展。 她就那樣靜靜地躺著喉誊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纵顾。 梳的紋絲不亂的頭發(fā)上伍茄,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機(jī)與錄音施逾,去河邊找鬼敷矫。 笑死例获,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的曹仗。 我是一名探鬼主播榨汤,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼怎茫!你這毒婦竟也來了收壕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤轨蛤,失蹤者是張志新(化名)和其女友劉穎蜜宪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祥山,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡圃验,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缝呕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澳窑。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖供常,靈堂內(nèi)的尸體忽然破棺而出摊聋,到底是詐尸還是另有隱情,我是刑警寧澤话侧,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布栗精,位于F島的核電站,受9級特大地震影響瞻鹏,放射性物質(zhì)發(fā)生泄漏悲立。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一新博、第九天 我趴在偏房一處隱蔽的房頂上張望薪夕。 院中可真熱鬧,春花似錦赫悄、人聲如沸原献。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姑隅。三九已至,卻和暖如春倔撞,著一層夾襖步出監(jiān)牢的瞬間讲仰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工痪蝇, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留鄙陡,地道東北人冕房。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像趁矾,于是被迫代替她去往敵國和親耙册。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容