微信小程式電商實戰-你所困擾的自定義頂部導航欄都在這裡

夢想.jpg
本文章是一個系列文章,以一個完整的可用於生產的實際專案探索微信小程式開發中我們經常會遇到的問題,希望能提供完美的解決方案,這次是本系列文章的第二篇了,一下列出該系列文章連結。
- ofollow,noindex">微信小程式及h5,基於taro,zoro最佳實踐探索
- 微信小程式電商實戰-解決你的登陸難問題
微信自6.6.0版本之後提供了自定義底部導航欄的功能,這使得我們的全屏頁面設計成為了可能
首先演示下最終的實現效果

微信小程式自定義導航欄演示.gif
我們實現了一個與微信之前的導航欄行為基本一致,樣式可自定義的導航欄,接下來讓我們一步一步實現它,這裡主要需要考慮如下幾點
- 不同的手機,狀態列高度不同,需要進行相關適配
- 當開啟小程式下拉重新整理時,如何讓頂部導航不會跟著下拉
- 自定義導航欄封裝成獨立元件,實現僅需引入到頁面,無需對頁面樣式做相關適配工作
該專案託管於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}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
本專案會持續完善,如有興趣,請關注一波