美文网首页微信小程序开发实战小程序微信小程序开发
如何在微信小程序中实现今日头条App那样的Topbar

如何在微信小程序中实现今日头条App那样的Topbar

作者: 剪影Boy | 来源:发表于2017-03-08 16:25 被阅读1557次

    今日头条App的Topbar是一个典型的频道管理和切换组件,自己前段时间研究了一番,在微信小程序上也实现了类似的效果。

    我们先看具体效果好了 ↓↓↓

    wx-topbar-screenshot-1 wx-topbar-screenshot-2 wx-topbar-screenshot-3

    这个项目(wx-topbar)已经放在GitHub上了——点此前往,欢迎学习交流。

    接下来,简要说一下实现思路。

    先看视图层,Topbar横向滚动对应的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负责Topbar中各个频道的呈现,所有频道的相关数据都存储在navbarArray这个对象数组里,而数组navbarShowIndexArray里存储了要显示频道在数组navbarArray中的索引。

    不难猜测,频道是否选中高亮,与数组navbarArray有关;频道是否显示,与数组navbarShowIndexArray有关。

    点击某个频道名称,就会触发对应频道的切换操作。

    view.navbar-arrow-down对应的是右上角的向下箭头,可采用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里,通过改变数组navbarShowIndexArray来控制频道是否显示和显示顺序,同时,需要另外一个数组navbarHideIndexArray来存储隐藏的频道。

    Modal显示的时候,Topbar需要被另一个写有“频道设置”字样的Bar覆盖。

    <view class="channel-setting {{ channelSettingShow }}">
        <view class="channel-setting-text">频道设置</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>
    

    然后,我们来看逻辑层的实现。初始化的部分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: '财经',
            type: ''
        }, {
            text: '搞笑',
            type: ''
        }],
        navbarShowIndexArray: Array.from(Array(12).keys()),
        navbarHideIndexArray: [],
        channelSettingShow: '',
        channelSettingModalShow: '',
        channelSettingModalHide: true
    }
    

    navbar-item-active是一个可使频道高亮的ClassnavbarShowIndexArray初始化的结果是一个0到11的数组,刚好是数组navbarArray的所有元素的索引。显然,初始化的结果是所有频道都将显示。

    为了实现频道个性化配置的保存,navbarShowIndexArray还需要通过小程序的数据缓存API储存起来。

    storeNavbarShowIndexArray: function() {
        wx.setStorage({
            key: 'navbarShowIndexArray',
            data: this.data.navbarShowIndexArray
        });
    }
    

    切换频道的函数如下:

    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
        });
    }
    

    这样,频道的管理和简单切换我们就实现了。

    但是,到此为止,频道的切换只能通过点击对应Topbar中频道那一小块区域来实现,要是在正文区域左滑和右滑也能切换频道就好了。

    一个容易想到的思路是,在正文区域绑定touch事件,通过坐标判断滑动方向,然后使Topbar中当前频道的上一个或下一个频道高亮,同时,控制Topbar横向滚动合适的偏移长度,以确保切换后的频道能出现在视图区域。

    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);
            }
        }
    }
    

    更多的技术细节,请移步GitHub——点此前往

    个人技术博客 biebu.xin,原文链接——如何在微信小程序中实现今日头条App那样的Topbar

    相关文章

      网友评论

      • e33964b7998b:非常好!
        有个小问题,topbar每一个tab的宽度可以自适应么?
        e33964b7998b:@剪影Boy 您好!我目前试过之后发现并不可以自适应宽度,但是我在试一试可不可以修改.以达到自适应的目的.
        剪影Boy:@贵棠 应该是可以的
      • 地瓜_7f59:你好,大神写的今日头条小程序不能下拉刷新,这个怎么解决,
        剪影Boy:看官方文档排查问题:https://mp.weixin.qq.com/debug/wxadoc/dev/api/pulldown.html#wxstartpulldownrefresh
      • 知晓程序:你好!我们是爱范儿旗下专注于小程序生态的公众号知晓程序(微信号 zxcx0101)。我们很赞赏你的文章,希望能获得转载授权。授权后,你的文章将会在知晓程序社区(minapp.com)、爱范儿、AppSo 等渠道发布。此外,由于第三方同步抓取功能,您的内容也可能会被同步发表到今日头条、搜狐、网易号等,我们会注明来源和作者姓名。

        非常感谢~~~
        剪影Boy:@知晓程序 可以
      • 艾逸涵:不错,学习啦

      本文标题:如何在微信小程序中实现今日头条App那样的Topbar

      本文链接:https://www.haomeiwen.com/subject/bdgbgttx.html