微信小程序电商实战-自定义顶部导航栏

梦想.jpg

感谢读者@Followyour的好心提醒,对于安卓机,下拉刷新确实无法做到导航栏不跟着下拉,特此做一下修正,并且自定义导航栏存在它的不足,比如部分安卓机型无法获取statusBarHeight,因此给予一个默认值20PX,但这并不能完全覆盖所有机型,还有一点是使用自定义导航栏对于web-view的兼容性未做测试

本文章是一个系列文章,以一个完整的可用于生产的实际项目探索微信小程序开发中我们经常会遇到的问题,希望能提供完美的解决方案,这次是本系列文章的第二篇了,以下列出该系列文章链接。

  1. 微信小程序及h5,基于taro,zoro最佳实践探索
  2. 微信小程序电商实战-登录模块设计

微信自6.6.0版本之后提供了自定义顶部导航栏的功能,这使得我们的全屏页面设计成为了可能

首先演示下最终的实现效果


微信小程序自定义导航栏演示.gif

我们实现了一个与微信之前的导航栏行为基本一致,样式可自定义的导航栏,接下来让我们一步一步实现它,这里主要需要考虑如下几点

  • 不同的手机,状态栏高度不同,需要进行相关适配
  • 当开启小程序下拉刷新时,如何让顶部导航不会跟着下拉(仅IOS有效)
  • 自定义导航栏封装成独立组件,实现仅需引入到页面,无需对页面样式做相关适配工作

该项目托管于github,有兴趣的可以直接查看源码,weapp-clover,如何运行项目源码请查看ztaro

要想实现自定义导航,首先我们需要配置navigationStyle为custom(src/app.js)

config = {
  window: {
    navigationStyle: 'custom'
  }
}

再实际情况中,我们往往需要对自定义导航进行各种各样的定制化,因此我们希望,封装一个最基本的导航栏,用于解决适配问题,其他样式的导航栏仅需对其进行二次封装,无需在关心适配问题,对于这个项目,我们封装组件如下:

ComponentBaseNavigation 导航栏基本组件,用于解决适配问题
ComponentHomeNavigation 引入基本导航组件,定制化首页导航栏组件
ComponentCommonNavigation 引入基本导航组件,定制化其他页面导航组件

ComponentBaseNavigation实现

对于适配不通手机顶部的状态栏高度,我们需要利用微信wx.getSystemInfo获取状态栏的高度,因此在user model中新增如下代码(src/models/user.js)

// 省略其他无关代码
import Taro from '@tarojs/taro'

export default {
  namespace: 'user',

  mixins: ['common'],

  state: {
    systemInfo: {},
  },

  async setup({ put }) {
    // 新增初始化获取用户手机系统相关信息,存储到redux全局状态中
    Taro.getSystemInfo().then(systemInfo =>
      put({ type: 'update', payload: { systemInfo } }),
    )
  }
}

实现组件逻辑(src/components/base/navigation/navigation.js)

import Taro, { Component } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { connect } from '@tarojs/redux'

import './navigation.scss'

@connect(({ user }) => ({
  // 链接redux中存储的状态栏高度到组件中
  statusBarHeight: user.systemInfo.statusBarHeight,
}))
class ComponentBaseNavigation extends Component {
  static defaultProps = {
    color: 'white',
    backgroundColor: '#2f3333',
  }

  render() {
    const { statusBarHeight, backgroundColor, color } = this.props

    const barStyle = {
      paddingTop: `${statusBarHeight || 20}px`,
      backgroundColor,
      color,
    }

    return (
      <View className="navigation">
        <View className="bar" style={barStyle}>
          {this.props.children}
        </View>
        <View className="placeholder" style={barStyle} />
      </View>
    )
  }
}

export default ComponentBaseNavigation

样式如下(src/components/base/navigation.scss)

// 大写的PX单位是为了告诉Taro,不要转换成单位rpx
// 通过测试和观察发现,微信顶部的胶囊宽高如下,并且各个屏幕下一致
// 因此采用PX单位
$capsule-padding: 6PX; // 胶囊的上下padding距离
$capsule-height: 32PX; // 胶囊的高度
$capsule-width: 88PX; // 胶囊的宽度

$navigation-height: $capsule-padding * 2 + $capsule-height;
$navigation-font-size: 15PX;
$navigation-icon-font-size: 25PX;
$navigation-box-shadow: 0 2PX 2PX #222;

.navigation {
  position: relative;
  background: transparent;
  
  .bar {
    position: fixed;
    top: 0;
    left: 0;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
    width: 100%;
    height: $navigation-height;
    z-index: 1;
    font-size: $navigation-font-size;
  }

  .placeholder {
    display: block;
    height: $navigation-height;
    background: transparent;
  }
}

要解决我们先前提到的问题当开启小程序下拉刷新时,如何让顶部导航不会跟着下拉,仅仅只需设置.bar样式为position: fixed,这样当我们下拉刷新时导航栏就不会跟着动了,那为什么我们还需要.placeholder标签呢

如果你尝试着去掉它,并且运行查看效果时,你会发现,页面的内容会被顶部导航栏遮挡了,我们需要对每个页面进行额外的设置以使它如预期一样显示,比如给每个页面设置顶部padding,这样的消耗太大,因此我们专门设置placeholder标签占据与导航栏相同的高度,使页面不被遮挡,且无需额外处理

ComponentHomeNavigation实现

有了这样一个基础组件,我们要实现首页导航栏效果就变得相当的简单了,直接上代码(src/components/home/navigation/navigation.js)

import Taro, { Component } from '@tarojs/taro'
import { View, Image, Text } from '@tarojs/components'

import { noop } from '../../../utils/tools'
import ComponentBaseNavigation from '../../base/navigation/navigation'

import './navigation.scss'

class ComponentHomeNavigation extends Component {
  static defaultProps = {
    onSearch: noop,
  }

  render() {
    const { onSearch } = this.props

    return (
      <ComponentBaseNavigation>
        <View className="navigation">
          <Image className="logo" src="@oss/logo.png" />
          <View className="search" onClick={onSearch}>
            <View className="icon iconfont icon-search" />
            <Text className="text">搜索</Text>
          </View>
        </View>
      </ComponentBaseNavigation>
    )
  }
}

export default ComponentHomeNavigation

引入导航组件到首页中, 省略样式代码(src/pages/home/home.js)

import Taro, { Component } from '@tarojs/taro'
import { View } from '@tarojs/components'
import { dispatcher } from '@opcjs/zoro'

import ComponentCommonLogin from '../../components/common/login/login'
import ComponentCommonSlogan from '../../components/common/slogan/slogan'
// 引入导航组件
import ComponentHomeNavigation from '../../components/home/navigation/navigation'
import ComponentHomeCarousel from '../../components/home/carousel/carousel'
import ComponentHomeBrand from '../../components/home/brand/brand'

import './home.scss'

class PageHome extends Component {
  config = {
    enablePullDownRefresh: true,
  }

  state = {
    // 请到README.md中查看此参数说明
    __TAB_PAGE__: true, // eslint-disable-line
  }

  componentDidMount() {
    dispatcher.banner.getBannerInfo()
    dispatcher.brand.getHotBrandList()
  }

  onPullDownRefresh() {
    Promise.all([
      dispatcher.banner.getBannerInfo(),
      dispatcher.brand.getHotBrandList(),
    ])
      .then(Taro.stopPullDownRefresh)
      .catch(Taro.stopPullDownRefresh)
  }

  handleGoSearch = () => Taro.navigateTo({ url: '/pages/search/search' })

  render() {
    return (
      <View className="home">
        <ComponentCommonLogin />
        <ComponentHomeNavigation onSearch={this.handleGoSearch} />
        <ComponentHomeCarousel />
        <View class="content">
          <ComponentCommonSlogan />
          <ComponentHomeBrand />
        </View>
      </View>
    )
  }
}

export default PageHome

ComponentCommonNavigation实现

该组件的实现方式与首页基本一致,需要提的一点就是返回键的实现,我们该如何统一的判断该页面是否需要返回键呢,这里需要利用微信接口wx.getCurrentPages(),实现代码如下(src/components/common/navigation/navigation.js)

import Taro, { Component } from '@tarojs/taro'
import { View } from '@tarojs/components'
import classNames from 'classnames'

import ComponentBaseNavigation from '../../base/navigation/navigation'

import './navigation.scss'

class ComponentCommonNavigation extends Component {
  static defaultProps = {
    title: '',
  }

  state = {
    canBack: false,
  }

  componentDidMount() {
    // 获取当前页面是否需要返回键
    const canBack = Taro.getCurrentPages().length > 1
    this.setState({ canBack })
  }

  handleGoHome = () => Taro.switchTab({ url: '/pages/home/home' })
  
  handleGoBack = () => Taro.navigateBack()

  render() {
    const { title } = this.props
    const { canBack } = this.state

    return (
      <ComponentBaseNavigation>
        <View className={classNames('navigation', { padding: !canBack })}>
          <View className="tools">
            {canBack && (
              <View
                className="iconfont icon-arrow-left back"
                onClick={this.handleGoBack}
              />
            )}
            <View
              className="iconfont icon-home home"
              onClick={this.handleGoHome}
            />
          </View>
          <View className="title">{title}</View>
        </View>
      </ComponentBaseNavigation>
    )
  }
}

export default ComponentCommonNavigation

感谢观看,文笔不佳,不能完全表达出设计思路,代码是最好的表达,移步weapp-clover

本项目会持续完善,如有兴趣,请关注一波

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容