感謝讀者@Followyour的好心提醒,對于安卓機,下拉刷新確實無法做到導(dǎo)航欄不跟著下拉义锥,特此做一下修正,并且自定義導(dǎo)航欄存在它的不足岩灭,比如部分安卓機型無法獲取statusBarHeight拌倍,因此給予一個默認(rèn)值20PX,但這并不能完全覆蓋所有機型噪径,還有一點是使用自定義導(dǎo)航欄對于web-view的兼容性未做測試
本文章是一個系列文章柱恤,以一個完整的可用于生產(chǎn)的實際項目探索微信小程序開發(fā)中我們經(jīng)常會遇到的問題,希望能提供完美的解決方案找爱,這次是本系列文章的第二篇了梗顺,以下列出該系列文章鏈接。
微信自6.6.0版本之后提供了自定義頂部導(dǎo)航欄的功能,這使得我們的全屏頁面設(shè)計成為了可能
首先演示下最終的實現(xiàn)效果
我們實現(xiàn)了一個與微信之前的導(dǎo)航欄行為基本一致吮播,樣式可自定義的導(dǎo)航欄变屁,接下來讓我們一步一步實現(xiàn)它,這里主要需要考慮如下幾點
- 不同的手機意狠,狀態(tài)欄高度不同粟关,需要進(jìn)行相關(guān)適配
- 當(dāng)開啟小程序下拉刷新時,如何讓頂部導(dǎo)航不會跟著下拉(僅IOS有效)
- 自定義導(dǎo)航欄封裝成獨立組件摄职,實現(xiàn)僅需引入到頁面誊役,無需對頁面樣式做相關(guān)適配工作
該項目托管于github,有興趣的可以直接查看源碼谷市,weapp-clover蛔垢,如何運行項目源碼請查看ztaro
要想實現(xiàn)自定義導(dǎo)航,首先我們需要配置navigationStyle為custom(src/app.js)
config = {
window: {
navigationStyle: 'custom'
}
}
再實際情況中迫悠,我們往往需要對自定義導(dǎo)航進(jìn)行各種各樣的定制化鹏漆,因此我們希望,封裝一個最基本的導(dǎo)航欄创泄,用于解決適配問題艺玲,其他樣式的導(dǎo)航欄僅需對其進(jìn)行二次封裝,無需在關(guān)心適配問題鞠抑,對于這個項目饭聚,我們封裝組件如下:
ComponentBaseNavigation 導(dǎo)航欄基本組件,用于解決適配問題
ComponentHomeNavigation 引入基本導(dǎo)航組件搁拙,定制化首頁導(dǎo)航欄組件
ComponentCommonNavigation 引入基本導(dǎo)航組件秒梳,定制化其他頁面導(dǎo)航組件
ComponentBaseNavigation實現(xiàn)
對于適配不通手機頂部的狀態(tài)欄高度法绵,我們需要利用微信wx.getSystemInfo獲取狀態(tài)欄的高度,因此在user model中新增如下代碼(src/models/user.js)
// 省略其他無關(guān)代碼
import Taro from '@tarojs/taro'
export default {
namespace: 'user',
mixins: ['common'],
state: {
systemInfo: {},
},
async setup({ put }) {
// 新增初始化獲取用戶手機系統(tǒng)相關(guān)信息酪碘,存儲到redux全局狀態(tài)中
Taro.getSystemInfo().then(systemInfo =>
put({ type: 'update', payload: { systemInfo } }),
)
}
}
實現(xiàn)組件邏輯(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中存儲的狀態(tài)欄高度到組件中
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朋譬,不要轉(zhuǎn)換成單位rpx
// 通過測試和觀察發(fā)現(xiàn),微信頂部的膠囊寬高如下兴垦,并且各個屏幕下一致
// 因此采用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;
}
}
要解決我們先前提到的問題當(dāng)開啟小程序下拉刷新時徙赢,如何讓頂部導(dǎo)航不會跟著下拉
,僅僅只需設(shè)置.bar樣式為position: fixed探越,這樣當(dāng)我們下拉刷新時導(dǎo)航欄就不會跟著動了狡赐,那為什么我們還需要.placeholder標(biāo)簽?zāi)?/p>
如果你嘗試著去掉它,并且運行查看效果時扶关,你會發(fā)現(xiàn)阴汇,頁面的內(nèi)容會被頂部導(dǎo)航欄遮擋了,我們需要對每個頁面進(jìn)行額外的設(shè)置以使它如預(yù)期一樣顯示节槐,比如給每個頁面設(shè)置頂部padding,這樣的消耗太大拐纱,因此我們專門設(shè)置placeholder標(biāo)簽占據(jù)與導(dǎo)航欄相同的高度铜异,使頁面不被遮擋,且無需額外處理
ComponentHomeNavigation實現(xiàn)
有了這樣一個基礎(chǔ)組件秸架,我們要實現(xiàn)首頁導(dǎo)航欄效果就變得相當(dāng)?shù)暮唵瘟俗嶙苯由洗a(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
引入導(dǎo)航組件到首頁中, 省略樣式代碼(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'
// 引入導(dǎo)航組件
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中查看此參數(shù)說明
__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實現(xiàn)
該組件的實現(xiàn)方式與首頁基本一致,需要提的一點就是返回鍵的實現(xiàn)东抹,我們該如何統(tǒng)一的判斷該頁面是否需要返回鍵呢蚂子,這里需要利用微信接口wx.getCurrentPages(),實現(xiàn)代碼如下(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() {
// 獲取當(dāng)前頁面是否需要返回鍵
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
感謝觀看缭黔,文筆不佳食茎,不能完全表達(dá)出設(shè)計思路,代碼是最好的表達(dá)馏谨,移步weapp-clover
本項目會持續(xù)完善别渔,如有興趣,請關(guān)注一波