uni-app導航欄開發遇到的問題

 

uni-app 自帶原生導航欄,在pages.json裏配置。
原生導航的體驗更好,渲染新頁面時,原生導航欄的渲染無需等待新頁面dom加載,可以在新頁面進入動畫開始時就渲染。

原生導航還可以避免滾動條通頂,並方便的控制原生下拉刷新。
通過pages.json的配置,可以簡單的、跨端的、高性能的開發業務。

但原生導航欄的擴展能力有限的。尤其是微信下,沒有提供太多導航欄的配置。
在App下,pages.json裏每個頁面的app-plus下可以設置titleNView等更多參數,可以得到比微信小程序更豐富的擴展性。
另外,開發者也可以在必要時取消原生導航欄,使用view自行繪製導航欄。

原生導航欄的通用配置

原生導航欄的配置,均在pages.json裏,每個page下面的style配置中的navigationBar各個參數配置,即爲通用配置,小程序、app、h5均生效。參考https://uniapp.dcloud.io/collocation/pages?id=style

全局取消原生導航欄

在pages.json的globalStyle裏,有個navigationStyle設置,默認是default,即帶有原生導航欄。
也可以設置爲custom。
在設爲custom後,所有頁面都沒有原生導航。
但在微信小程序裏,右上角始終都有一個膠囊按鈕。
很多微信小遊戲界面上也沒原生導航欄,但有膠囊按鈕。
一般App裏不會使用這個參數配置。建議個別頁面單獨設置不使用原生導航,具體見下。

單獨去除原生導航欄

自微信客戶端 7.0.0 起、App端HBuilderX 2.0.3起,支持通過如下方法取消單獨一個頁面的原生導航欄。但小程序右上角膠囊按鈕仍然去不掉。頁面配置 navigationStyle 爲 custom:

複製代碼{  
    "path" : "pages/log/log",  
    "style" : {  
        "navigationStyle":"custom"  
    }  
}

原生導航欄在App側的擴展

微信小程序的設計裏,沒有給原生導航提供太多自定義能力。在開發App時是不夠用的。
pages.json裏,每個page下面的style下面還有一個子擴展節點:app-plus。
這個節點定義了在5+App環境下,也即iOS、Android環境下,增強的配置。
其中有一個子節點titleNView,這個是5+規範裏webview頁面的原生導航窗體規範。
參考https://uniapp.dcloud.io/collocation/pages?id=app-plus

App單獨去除原生導航欄

複製代碼{  
    "path": "pages/log/log",  
    "style": {  
        "navigationBarTitleText": "hello",  
        "app-plus": {  
            "titleNView": false  
        }  
    }  
}

在App去除原生導航欄後,小程序端側仍保有原生導航欄。

App去除導航欄後改變狀態欄樣式

App因爲默認爲沉浸式,去除導航欄後,頁面頂部會直通到狀態欄的區域,可能出現如下需求:

  • 改變狀態欄文字顏色:設置該頁面的 navigationBarTextStyle 屬性,可取值爲 black/white。如果想單獨設置顏色,App端可使用plus.navigator.setStatusBarStyle設置。部分低端Android手機(4.4)自身不支持設置狀態欄前景色。
  • 改變狀態欄背景顏色:通過繪製一個佔位的view固定放在狀態欄位置,設置此view的背景顏色,即可達到想要的效果,uni-app提供了一個狀態欄高度的css變量,具體參考:http://uniapp.dcloud.io/frame?id=css%E5%8F%98%E9%87%8F

以下爲示例:

複製代碼<!-- #ifdef APP-PLUS -->  
<view class="status_bar">  
    <view class="top_view"></view>  
</view>  
<!-- #endif -->
複製代碼.status_bar {  
    height: var(--status-bar-height);  
    width: 100%;  
    background-color: #F8F8F8;  
}  
.top_view {  
    height: var(--status-bar-height);  
    width: 100%;  
    position: fixed;  
    background-color: #F8F8F8;  
    top: 0;  
    z-index: 999;  
}

給原生導航欄添加自定義按鈕

注意:按鈕的點擊事件需要在頁面監聽onNavigationBarButtonTap事件

頁面監聽代碼如下:

複製代碼export default {  
    data() {  
        return {}  
    },  
    onNavigationBarButtonTap() {  
        console.log("點擊了自定義按鈕");  
    }  
}  

pages.json配置如下:

複製代碼{  
    "path": "pages/log/log",  
    "style": {  
        "navigationBarTitleText": "hello",  
        "app-plus": {  
            "titleNView": {  
                "buttons": [{  
                    "text": "\ue534",  
                    "fontSrc": "/static/uni.ttf",  
                    "fontSize": "22px"  
                }]  
            }  
        }  
    }  
}

buttons的text推薦使用字體圖標。
如果按鈕使用的漢字或英文較長,推薦把字體改小一點,或者調節按鈕寬度、padding等值。
配置button的背景顏色爲透明:background:'rgba(0,0,0,0)'

以上代碼在hello uni-app的模板-頂部導航標題欄中有示例。

原生導航欄自定義按鈕帶紅點和角標

複製代碼{  
    "path" : "nav-dot/nav-dot",  
    "style" : {  
        "navigationBarTitleText" : "導航欄帶紅點和角標",  
        "app-plus" : {  
            "titleNView" : {  
                "buttons" : [  
                    {  
                        "text" : "消息",  
                        "fontSize" : "14",  
                        "redDot" : true  
                    },  
                    {  
                        "text" : "關注",  
                        "fontSize" : "14",  
                        "badgeText" : "12"  
                    }  
                ]  
            }  
        }  
    }  
}

以上代碼在hello uni-app的模板-頂部導航標題欄中有示例。

原生導航欄自定義按鈕帶下拉選擇(城市選擇)

複製代碼{  
    "path" : "nav-city-dropdown/nav-city-dropdown",  
    "style" : {  
        "navigationBarTitleText" : "導航欄帶城市選擇",  
        "app-plus" : {  
            "titleNView" : {  
                "buttons" : [  
                    {  
                        "text" : "北京市",  
                        "fontSize" : "14",  
                        "select" : true,  
                        "width" : "auto"  
                    }  
                ]  
            }  
        }  
    }  
}

以上代碼在hello uni-app的模板-頂部導航標題欄中有示例。

導航欄上的原生搜索框

原生導航欄支持放置原生搜索框,可點擊直接彈出軟鍵盤,也可以點擊後跳轉到新頁面搜索。
因代碼較多,此處不列,請參考hello uni-app的模板-頂部導航標題欄示例。
如需動態修改searchInput,或者獲取searchInput的placehold,參考uni-app動態修改App端導航欄

配置原生導航欄的透明漸變

原生導航欄還支持透明漸變效果,頁面剛載入時沒有導航標題,頁面內容通頂到狀態欄裏,頁面向下滾動後標題欄漸變出現。

複製代碼{  
    "path": "pages/log/log",  
    "style": {  
        "navigationBarTitleText": "hello",  
        "app-plus": {  
            "titleNView": {  
                "type": "transparent"  
            }  
        }  
    }  
}

實際上可用的titleNView設置還有很多,詳細的api見http://www.html5plus.org/doc/zh_cn/webview.html#plus.webview.WebviewTitleNViewStyles

透明漸變的導航欄的button圖標有一個默認的灰色背景圈,防止背景圖和按鈕前景顏色相同導致按鈕無法看清。如果要去掉這個灰色背景圖,可以配置button的背景顏色爲透明:background:'rgba(0,0,0,0)'

原生導航欄繪製圖片

通過在titleNView裏配置tags,可以實現導航欄繪製圖片的效果:

複製代碼{  
    "path" : "nav-image/nav-image",  
    "style" : {  
        "app-plus" : {  
            "titleNView" : {  
                "titleText" : "",  
                "tags" : [  
                    {  
                        "tag" : "img",  
                        "src" : "/static/nav.png",  
                        "position" : {  
                            "left" : "auto",  
                            "top" : "auto",  
                            "width" : "110px",  
                            "height" : "26px"  
                        }  
                    }  
                ]  
            }  
        }  
    }  
}

通過配置 tags 除了可以繪製圖片,還可以繪製更多豐富的內容,如:richtext(富文本)、font(文本)、input(輸入框)、rect(矩形區域)。詳情參考:titleNViewtags

以上代碼在hello uni-app的模板-頂部導航標題欄中有示例。

通過setStyle方式動態修改原生導航欄樣式

如果需要js動態修改導航欄,uni有跨端的api可修改標題、背景色、前景色。這部分是app、小程序、h5都支持的,參考https://uniapp.dcloud.io/api/ui/navigationbar

對於app側擴展的設置,比如自己添加的buttons,則需使用plus的js api來動態設置。在App端可以通過得到webview對象,通過setStyle方法重新設置,包括修改webview對象的titleNview屬性,以達到修改標題欄按鈕文字及樣式的功能。
具體參考:https://ask.dcloud.net.cn/article/35374

App側使用subnvue自行繪製原生導航

nvue其實是weex上補充了uni的api。
uni-app支持使用nvue頁面,也就是weex原生引擎,繪製頂部的原生導航欄。
在hello uni-app的API-界面示例中,有subnvue示例,裏面頂部導航欄是漸變色的,這就是subnvue的原生導航欄。

在pages.json的配置如下:

複製代碼{  
    "path": "subnvue/subnvue",  
    "style": {  
        "app-plus": {  
            "titleNView": false,  
            "subNVues": [{  
                "id": "nav",  
                "path": "subnvue/subnvue/nav",  
                "type": "navigationBar"  
            }]  
        }  
    }  
}

App側使用plus.nativeObj.view自定義原生導航欄

注意:從HBuilderX 1.9.10起提供了subnvue,比使用plus.nativeObj.view自定義原生導航欄更加方便。詳見上一節。

titleNView提供的配置,雖然比微信多不少,但有時仍然無法滿足某些場景的需求,比如在titleNView中畫一個選項卡。
此時有3種處理方式。1. 使用plus.nativeObj.view的api自定義titleNView。2. 頁面採用nvue,即weex方式製作。3. 取消原生導航,使用view自行繪製(見上)。
本節先說方式1. 使用plus.nativeObj.view
plus.nativeObj是5+引擎提供的輕量原生渲染引擎,其中plus.nativeObj.view一個自定義性很強的對象,以下簡稱nview。
規範文檔是:www.html5plus.org/doc/zh_cn/nativeobj.html#plus.nativeObj.View
nview是一個基於canvas理念的繪製引擎,在一塊畫布上自行繪製、覆蓋、擦除。
nview可以畫出任何界面、線條、矩形、文字、圖片、包括原生的input輸入框。
其實我們所看到的各種界面對象控件,在計算機底層都是繪圖引擎基於draw字、draw圖、draw線條來做的。
與weex相比,nview並不夠強,nview沒有dom概念,不支持內部滾動。
其實titleNView,包括原生tabbar、cover-view,他們的底層實現都是基於nview的。

獲取當前頁面的titleNView對象,參考http://ask.dcloud.net.cn/article/35036
同時上述參考文章中還有一個給titleNView的右上角畫一個小紅點的例子。

開發者製作的示例,如何給原生導航欄頂部畫一個原生input:http://ask.dcloud.net.cn/article/35201

取消原生導航欄後,使用前端標籤組件模擬繪製導航欄

不管是全局取消原生導航欄,還是在App下某個頁面取消原生導航,如果還想自己繪製一些個性化的title,往往會使用view組件。
尤其是App的首頁,頂部經常有各種特殊設置,此時需要自己使用前端技術來繪製導航。

導航欄應該是由狀態欄和標題欄構成,狀態欄的高度爲 var(--status-bar-height) 此變量爲uni-app框架提供僅在在css生效,標題欄的高度設爲88px,整個狀態欄的高度應爲: calc(var(--status-bar-height) + 88px)
(upx主要針對寬度,高度無所謂還可以使用px)

複製代碼.title-contents{  
    height: calc(var(--status-bar-height) + 88px);  
}  
.status{  
    height: var(--status-bar-height);  
}  
.titles{  
    height: 88px;  
}

狀態欄和標題欄都應固定在頁面頂部,需設置 position:fixed,標題欄的top應爲狀態欄的高度

複製代碼.top-view{  
    width: 100%;  
    position: fixed;  
    top: 0;  
}  
.titles{  
    top: var(--status-bar-height);  
}

繪製的返回箭頭需要綁定點擊事件,返回上一個頁面

複製代碼<view class="titleLeftButton" @click="backButton"></view>  

methods:{  
    backButton(){  
        uni.navigateBack()  
    }  
}

以下爲導航欄組件的部分代碼

複製代碼<template>  
    <view class="title-contents">  
        <view class="top-view status" :style="{background:statusColor}"></view>  
        <view class="_top titles" :style="{background:statusColor}">  
            <view class="titleLeftButton" @click="backButton" v-if="showLeftButton"></view>  
            <view class="titleText" :class="titleClass">{{titleText}}</view>  
            <view class="titleRightButton" @click="rightButton" v-if="showRightButton"></view>  
        </view>  
    </view>  
</template>  
<script>  
    export default {  
        props:{  
            titleText:{  
                type:String,  
                default:""  
            },  
            statusColor:{  
                type:String,  
                default:"#8F8F94"  
            },  
            showLeftButton:{  
                type:Boolean,  
                default:true  
            },  
            showRightButton:{  
                type:Boolean,  
                default:false  
            }  
        },  
        methods:{  
            backButton(){  
                uni.navigateBack()  
            },  
            ...  
        }  
    }  
</script>  
<style>  
    ...  
    .top-view{  
        width: 100%;  
        position: fixed;  
        top: 0;  
    }  
</style>  

Ps:若頁面不需要標題欄,只需一個狀態欄的view佔位,那麼只需在頁面添加一個view即可不需要引入外部組件以免影響性能。

複製代碼<view class="status-contents">  
    <view class="status top-view"></view>  
</view>
複製代碼//css  
.status-contents{  
    height: var(--status-bar-height);  
}  
.top-view{  
    width: 100%;  
    position: fixed;  
    top: 0;  
}  
.status{  
    height:var(--status-bar-height);  
}

uni ui裏有前端實現的自定義導航欄組件,https://ext.dcloud.net.cn/plugin?id=52,hello uni-app的uni ui中也有示例。

注意事項

取消原生導航欄後,自己使用HTML自定義組件模擬導航欄,會有很多性能體驗問題:

  1. 加載不如原生導航快
  2. 下拉刷新無法從自定義的導航欄組件下面下拉
  3. 必須取消頁面的bounce效果,否則滾動到頂時再拖屏幕,在iOS上發現title也被拖下來了。
  4. 滾動條會通頂
    所以除非不得以,不要使用全局取消原生導航欄的做法。
    如必須使用,注意如下幾點:
  5. 涉及到導航欄高度的css儘量放置在App.vue裏面以提高渲染速度(css渲染順序:先渲染App.vue裏面的css,再渲染頁面css)
  6. 狀態欄顏色應設置默認顏色,若非必要,不建議修改其顏色
  7. 減少在組件中使用 :style="" 的使用以提高性能
  8. 下拉刷新使用circle方式,並設置offset,讓下拉刷新的圈從指定位置開始下拉,具體見pages.json配置文檔

有個高頻場景是App首頁的title自定義,如果實現的效果很個性化,那麼使用plus.nativeObj.view的方案會過於複雜,由於首頁並不存在新頁面進入立即渲染的壓力,所以App首頁如果要大幅定製,推薦使用前端view繪製,而不是使用plus.nativeObj.view。

如果把自定義導航封裝成組件,雖然多個頁面引入方便,但性能下降,因爲這種自定義組件的加載是晚於頁面基本元素的,會導致新頁面進入動畫時無法渲染title。
所以導航條這種要求在動畫期渲染的東西,儘量不要使用自定義組件方式。

在hello uni-app示例中有各種導航欄的源碼。
在擴展ui中有前端自定義導航欄。
在模板中有各種原生的導航欄。
大多數情況複製這些代碼就夠了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章