今日頭條App的Topbar是一個典型的頻道管理和切換組件,自己前段時間研究了一番,在微信小程序上也實現(xiàn)了類似的效果。
我們先看具體效果好了 ↓↓↓
接下來,簡要說一下實現(xiàn)思路。
先看視圖層,Topbar橫向滾動對應(yīng)的WXML代碼如下:
- <scroll-view class="navbar" scroll-x="true" scroll-left="{{scrollNavbarLeft}}">
- <view class="navbar-item {{ navbarArray[item].type }}" id="{{ item }}" wx:for="{{ navbarShowIndexArray }}" catchtap="onTapNavbar">
- <view class="navbar-item-wrap">{{ navbarArray[item].text }}</view>
- </view>
- <view class="navbar-item visibility-hidden">
- <view class="navbar-item-wrap">空白</view>
- </view>
- </scroll-view>
- <view class="navbar-arrow-down" catchtap="showChannelSettingModal">
- <view class="navbar-arrow-down-wrap">
- <image class="navbar-arrow-icon" src="/images/index/icon_arrow_down.png"></image>
- </view>
- </view>
scroll-view負(fù)責(zé)Topbar中各個頻道的呈現(xiàn),所有頻道的相關(guān)數(shù)據(jù)都存儲在navbarArray這個對象數(shù)組里,而數(shù)組navbarShowIndexArray里存儲了要顯示頻道在數(shù)組navbarArray中的索引。
不難猜測,頻道是否選中高亮,與數(shù)組navbarArray有關(guān);頻道是否顯示,與數(shù)組navbarShowIndexArray有關(guān)。
點擊某個頻道名稱,就會觸發(fā)對應(yīng)頻道的切換操作。
view.navbar-arrow-down對應(yīng)的是右上角的向下箭頭,可采用fixed定位類型,點擊后彈出管理頻道的Modal.
- <view class="channel-setting-modal {{ channelSettingModalShow }}" hidden="{{ channelSettingModalHide }}">
- <view class="channel-show-text">
- <view class="channel-show-text-wrap">顯示頻道</view>
- </view>
- <view class="channel-item" wx:for="{{ navbarShowIndexArray }}">
- <view class="channel-item-wrap">
- <view class="channel-item-left">
- <image class="channel-item-icon-minus {{ !index || navbarShowIndexArray.length < 4 ? 'visibility-hidden' : '' }}" id="{{ item }}.0" src="/images/index/icon_minus.png" catchtap="hideChannel"></image>
- <view class="channel-item-text">{{ navbarArray[item].text }}</view>
- </view>
- <view class="channel-item-up {{ index < 2 ? 'visibility-hidden' : '' }}" id="{{ item }}.00" catchtap="upChannel">上移</view>
- </view>
- </view>
- <view class="channel-hide-text">
- <view class="channel-hide-text-wrap">隱藏頻道</view>
- </view>
- <view class="channel-item" wx:for="{{ navbarHideIndexArray }}">
- <view class="channel-item-wrap">
- <view class="channel-item-left">
- <image class="channel-item-icon-plus" id="{{ item }}.0" src="/images/index/icon_plus.png" catchtap="showChannel"></image>
- <view class="channel-item-text">{{ navbarArray[item].text }}</view>
- </view>
- <view class="channel-item-up visibility-hidden">上移</view>
- </view>
- </view>
- </view>
在這個管理頻道的Modal里,通過改變數(shù)組navbarShowIndexArray來控制頻道是否顯示和顯示順序,同時,需要另外一個數(shù)組navbarHideIndexArray來存儲隱藏的頻道。
Modal顯示的時候,Topbar需要被另一個寫有“頻道設(shè)置”字樣的Bar覆蓋。
- <view class="channel-setting {{ channelSettingShow }}">
- <view class="channel-setting-text">頻道設(shè)置</view>
- <view class="navbar-arrow-up" catchtap="hideChannelSettingModal">
- <image class="navbar-arrow-icon navbar-arrow-icon-up" src="/images/index/icon_arrow_up.png"></image>
- </view>
- </view>
然后,我們來看邏輯層的實現(xiàn)。初始化的部分data如下:
- data: {
- navbarArray: [{
- text: '推薦',
- type: 'navbar-item-active'
- }, {
- text: '熱點',
- type: ''
- }, {
- text: '視頻',
- type: ''
- }, {
- text: '圖片',
- type: ''
- }, {
- text: '段子',
- type: ''
- }, {
- text: '社會',
- type: ''
- }, {
- text: '娛樂',
- type: ''
- }, {
- text: '科技',
- type: ''
- }, {
- text: '體育',
- type: ''
- }, {
- text: '汽車',
- type: ''
- }, {
- text: '財經(jīng)',
- type: ''
- }, {
- text: '搞笑',
- type: ''
- }],
- navbarShowIndexArray: Array.from(Array(12).keys()),
- navbarHideIndexArray: [],
- channelSettingShow: '',
- channelSettingModalShow: '',
- channelSettingModalHide: true
- }
11的數(shù)組,剛好是數(shù)組navbarArray的所有元素的索引。顯然,初始化的結(jié)果是所有頻道都將顯示。
為了實現(xiàn)頻道個性化配置的保存,navbarShowIndexArray還需要通過小程序的數(shù)據(jù)緩存API儲存起來。
storeNavbarShowIndexArray: function() {
const that = this;
wx.setStorage({
key: 'navbarShowIndexArray',
data: that.data.navbarShowIndexArray
});
}
切換頻道的函數(shù)如下:
- switchChannel: function(targetChannelIndex) {
- this.getArticles(targetChannelIndex);
- let navbarArray = this.data.navbarArray;
- navbarArray.forEach((item, index, array) => {
- item.type = '';
- if (index === targetChannelIndex) {
- item.type = 'navbar-item-active';
- }
- });
- this.setData({
- navbarArray: navbarArray,
- currentChannelIndex: targetChannelIndex
- });
- }
這樣,頻道的管理和簡單切換我們就實現(xiàn)了。
但是,到此為止,頻道的切換只能通過點擊對應(yīng)Topbar中頻道那一小塊區(qū)域來實現(xiàn),要是在正文區(qū)域左滑和右滑也能切換頻道就好了。
一個容易想到的思路是,在正文區(qū)域綁定touch事件,通過坐標(biāo)判斷滑動方向,然后使Topbar中當(dāng)前頻道的上一個或下一個頻道高亮,同時,控制Topbar橫向滾動合適的偏移長度,以確保切換后的頻道能出現(xiàn)在視圖區(qū)域。
- onTouchstartArticles: function(e) {
- this.setData({
- 'startTouchs.x': e.changedTouches[0].clientX,
- 'startTouchs.y': e.changedTouches[0].clientY
- });
- },
- onTouchendArticles: function(e) {
- let deltaX = e.changedTouches[0].clientX - this.data.startTouchs.x;
- let deltaY = e.changedTouches[0].clientY - this.data.startTouchs.y;
- if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 10) {
- let deltaNavbarIndex = deltaX > 0 ? -1 : 1;
- let currentChannelIndex = this.data.currentChannelIndex;
- let navbarShowIndexArray = this.data.navbarShowIndexArray;
- let targetChannelIndexOfNavbarShowIndexArray = navbarShowIndexArray.indexOf(currentChannelIndex) + deltaNavbarIndex;
- let navbarShowIndexArrayLength = navbarShowIndexArray.length;
- if (targetChannelIndexOfNavbarShowIndexArray >= 0 && targetChannelIndexOfNavbarShowIndexArray <= navbarShowIndexArrayLength - 1) {
- let targetChannelIndex = navbarShowIndexArray[targetChannelIndexOfNavbarShowIndexArray];
- if (navbarShowIndexArrayLength > 6) {
- let scrollNavbarLeft;
- if (targetChannelIndexOfNavbarShowIndexArray < 5) {
- scrollNavbarLeft = 0;
- } else if (targetChannelIndexOfNavbarShowIndexArray === navbarShowIndexArrayLength - 1) {
- scrollNavbarLeft = this.rpx2px(110 * (navbarShowIndexArrayLength - 6));
- } else {
- scrollNavbarLeft = this.rpx2px(110 * (targetChannelIndexOfNavbarShowIndexArray - 4));
- }
- this.setData({
- scrollNavbarLeft: scrollNavbarLeft
- });
- }
- this.switchChannel(targetChannelIndex);
- }
- }
- }
新聞熱點
疑難解答