Научить вас писать компонент календаря апплета WeChat

Апплет WeChat

Сегодня мы напишем компонент календаря апплета WeChat.

Компонент календаря апплета WeChatGitHub.com/749264345/i…

Хорошо, давайте посмотрим, чего мы хотим достичь, как показано ниже.

52魔都
52魔都
На скриншоте выше мы видим, что

1. Календарь может изменить эффект презентации календаря с помощью кнопки [Switch Display Effect].Выше изображение — мозаичный режим, а нижнее изображение — свернутый режим прокрутки. 2. Нажав на конкретную дату, можно отобразить выбранную в данный момент конкретную дату на странице. 3. Нажмите [Сегодня], чтобы быстро вернуться к просмотру текущего дня. 4. Нажмите【◀】и【▶】, чтобы переключить месяц. Вышеупомянутые четыре пункта также являются основными требованиями к взаимодействию, мы начнем немедленно. Прежде всего, мы сначала структурируем, а затем стилизуем, чтобы сделать самую простую структуру интерфейса. Здесь мы делим общую структуру на верхнюю, среднюю и нижнюю, область отображения операций, область отображения недели и область отображения даты.

<view class='calendar'>
    <!--显示当前年月日-->
    <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>

        <!--当月格子-->
        <view class='grid'>
            <view class="wrap">{{item.date}}</view>
        </view>

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

Это наша базовая структура календаря.Умные друзья уже знают общую логику нашей реализации из макета.Да, мы сначала получаем количество дней в текущем месяце, и количество дней в предыдущем месяце и следующем месяце, так что что наш календарь выйдет. . Что ж, не торопитесь, давайте подробно рассмотрим ниже, давайте сначала напишем основные стили.

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

/* 标题 */
.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;
}

Выше мы в основном попробовали интерфейс календаря, давайте реализуем отображение недели и даты. Хорошо, давайте сначала отобразим неделю, давайте определим массив в компоненте для обхода заголовка недели;

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

Мы определяем заголовок недели как настраиваемый режим, который отображает приведенный выше текст по умолчанию, а затем мы можем настроить и персонализировать его вне компонента. Итак, настраиваем код wxml.

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

Таким образом, мы можем увидеть желаемый эффект.

52魔都
Ниже запускаем отображение даты, сначала получаем количество дней в текущем месяце, код ядра тут такой

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

http://www.w3school.com.cn

Поэтому пробуем следующее

52魔都
Мы получили возвращаемое значение, как и ожидалось, и когда мы передали дату 0, она вернула 31 как полное количество дней в месяце.

Поскольку диапазон дней в JavaScript представляет собой значение от 1 до 31, когда он установлен на 0, он будет двигаться вперед на один день, что означает последний день предыдущего месяца Таким образом, количество дней в каждом месяце может быть получен.

Зная принцип получения количества дней в текущем месяце, нам также необходимо знать день недели 1-го числа текущего месяца. Мы используем следующий метод:

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

http://www.w3school.com.cn
Мы также отлаживаем в консоли;
52魔都
Следует отметить, что указанный выше месяц является фактическим месяцем, а приведенный ниже метод требует вычитания 1 из фактического месяца. Таким образом, мы получаем значение от 0 до 6, соответствующее воскресенью и субботе соответственно. Значение 6 соответствует субботе, а значение 0 — воскресенью. Так как первый день календаря — воскресенье, а воскресенье соответствует 0, то передается 1-й день месяца, а возвращаемое значение — день недели, что означает количество пробелов перед 1-м днем месяц. Зная, что в текущем месяце всего несколько дней, и есть несколько дней перед текущим месяцем, мы можем получить это, выполнив алгоритм, и есть несколько дней в конце текущего месяца, поэтому мы создаем следующую функцию:

    // 组件的初始数据
    data: {
        //当月格子
        thisMonthDays: [],
        //上月格子
        empytGridsBefore: [],
        //下月格子
        empytGridsAfter: [],
},

methods: {
        //获取当月天数
        getThisMonthDays: function (year, month) {
            return new Date(year, month, 0).getDate();
        },
        // 绘制当月天数占的格子
        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
            })
        },
        //获取当月空出的天数
        createEmptyGrids: function (year, month) {
            let week = new Date(Date.UTC(year, month - 1, 1)).getDay(),
                empytGridsBefore = [],
                empytGridsAfter = [],
                emptyDays = (week == 0 ? 7 : week);
            //当月天数
            var thisMonthDays = this.getThisMonthDays(year, month);
            //上月天数
            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
            })
        },

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

Также модифицируем код wxml, а заодно добавляем соответствующие обработчики событий за прошлый месяц, следующий месяц и сегодня, три кнопки.

    <!--显示当前年月日-->
    <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>

<!--当月格子-->
<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>

Слушатели связанных событий:

//默认选中当天 并初始化组件
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);

    //发送事件监听
    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) для единообразной инициализации компонента; В то же время в сегодняшней функции мы добавляем функцию прослушивания событий для отправки выбранной даты на страницу, и мы получаем соответствующее значение через подписку на событие.

<Calendar id="Calendar" bind:select="select"></Calendar>
//组件监听事件
select(e) {
    this.setData({
        selectVal:e.detail
    })
},

Наконец, мы добавляем событие для кнопки, которая переключает эффект отображения:

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

Соответствующий метод в компоненте, всякий раз, когда переключается эффект отображения, компонент необходимо инициализировать.

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

Вышеприведенное в основном представляет собой базовую логику, реализованную компонентом календаря апплета.Поскольку место слишком длинное, есть много деталей реализации, которые здесь не описаны.Вы можете перейти на мой github.

Компонент календаря апплета WeChatGitHub.com/749264345/i…

В приведенном выше есть недостатки, пожалуйста, дайте предложения или лучшие реализации, спасибо ~ Наконец, я желаю вам всем счастливой пятницы~