基於uniapp自定義Navbar+Tabbar組件「兼容H5+小程序+App端Nvue」


uni-app跨端自定義navbar+tabbar組件|沉浸式導航條|仿咸魚凸起標簽欄

在跨端項目開發中,uniapp是個不錯的框架。采用vue.js和小程序語法結構,使得入門開發更容易。擁有非常豐富的插件生態。支持編譯到h5、小程序及App等多個終端平台。

如上圖:編譯到h5+小程序+App端效果

◆ 准備

在項目根目錄components下新建ua-navbar和ua-tabbar組件。

在main.js中全局引入組件。

// 引入自定義組件
import NavBar from './components/ua-navbar/index.vue'
import TabBar from './components/ua-tabbar/index.vue'
Vue.component('navbar', NavBar)
Vue.component('tabbar', TabBar)

HBuilderX 2.5.5起支持easycom組件模式。大家也可以根據需要改為此種引入模式,會更加方便。

傳統vue組件,需要安裝、引用、注冊,三個步驟后才能使用組件。easycom將其精簡為一步。 只要組件安裝在項目的components目錄下,並符合components/組件名稱/組件名稱.vue目錄結構。就可以不用引用、注冊,直接在頁面中使用。

◆ uniapp獲取手機狀態條

如果項目中導航欄采用自定義模式 "globalStyle": { "navigationStyle": "custom" }  那么狀態欄就需要重新計算了。

在App.vue中全局設置

/**
 * @Desc     uniapp獲取狀態欄信息
 * @Time     andy by 2021/7/6
 * @About    Q:282310962  wx:xy190310
 */
<script>
    import Vue from 'vue'
    
    export default {
        globalData: {
            // 全局設置狀態欄和導航欄高度
            statusBarH: 0,
            customBarH: 0,
        },
        onLaunch: function() {
            uni.getSystemInfo({
                success: (e) => {
                    // 獲取手機狀態欄高度
                    let statusBar = e.statusBarHeight
                    let customBar
                    
                    // #ifndef MP
                    customBar = statusBar + (e.platform == 'android' ? 50 : 45)
                    // #endif
                    
                    // #ifdef MP-WEIXIN
                    // 獲取膠囊按鈕的布局位置信息
                    let menu = wx.getMenuButtonBoundingClientRect()
                    // 導航欄高度 = 膠囊下距離 + 膠囊上距離 - 狀態欄高度
                    customBar = menu.bottom + menu.top - statusBar
                    // #endif
                    
                    // #ifdef MP-ALIPAY
                    customBar = statusBar + e.titleBarHeight
                    // #endif
                    
                    // 注意:此方法不支持原生Nvue頁面
                    Vue.prototype.statusBarH = statusBar
                    Vue.prototype.customBarH = customBar
                    
                    // 支持nvue頁面寫法(兼容H5/小程序/APP/APP-Nvue)
                    this.globalData.statusBarH = statusBar
                    this.globalData.customBarH = customBar
                }
            })
        },
        // ...
    }
</script>

◆ uniapp自定義沉浸式導航條

<!-- 導航條模板 -->
<template>
    <view class="ua__navbar">
        <view class="ua__navbar-wrap" :class="{'custom': custom, 'fixed': fixed || transparent}"
            :style="{'height': customBarH + 'px', 'padding-top': (custom ? statusBarH : 0) + 'px', 'background': bgcolor, 'color': color, 'z-index': zIndex}">
            <!-- //左側 (返回) -->
            <view class="action navbar-action__left" v-if="back && back!='false'" @click="onBack">
                <template v-if="$slots.back">
                    <slot name="back" />
                </template>
                <template v-else><text class="iconfont nvuefont"
                        :style="{'color': color}">{{'\ue84c'}}</text></template>
                <slot name="backText" />
            </view>
            <slot name="left" />

            <!-- //標題 -->
            <view v-if="!search" class="navbar-title" :class="{'center': center}">
                <template v-if="$slots.title">
                    <slot name="title" />
                </template>
                <template v-else><text :style="{'color': color}">{{title}}</text></template>
            </view>

            <!-- //搜索框 -->
            <view v-if="search" class="action navbar-action__search">
                <slot name="search" />
            </view>

            <!-- //右側 -->
            <view class="action navbar-action__right">
                <slot name="right" />
            </view>
        </view>
    </view>
</template>
<script>
    export default {
        props: {
            // 是否采用自定義導航模式
            custom: { type: [Boolean, String], default: false },
            // 是否返回
            back: { type: [Boolean, String], default: true },
            // 標題
            title: { type: String, default: '' },
            // 標題顏色
            color: { type: String, default: '#353535' },
            // 背景色
            bgcolor: { type: String, default: '#fff' },
            // 標題是否居中
            center: { type: [Boolean, String], default: false },
            // 搜索框
            search: { type: [Boolean, String], default: false },
            // 是否固定導航
            fixed: { type: [Boolean, String], default: false },
            // 是否背景透明
            transparent: { type: [Boolean, String], default: false },
            // 設置層疊
            zIndex: { type: [Number, String], default: '2022' },
        },
        data() {
            return {
                statusBarH: 0,
                customBarH: 0,
            }
        },
        beforeCreate() {
            // #ifdef APP-NVUE
            var domModule = weex.requireModule('dom');
            domModule.addRule('fontFace', {
                'fontFamily': "nvueIcon",
                'src': "url('/static/fonts/iconfont.ttf')"
            });
            // #endif
        },
        created() {
            const app = getApp()
            // 獲取狀態欄和導航條高度
            this.statusBarH = app.globalData.statusBarH
            this.customBarH = this.custom ? app.globalData.customBarH : app.globalData.customBarH - this.statusBarH
        },
        methods: {
            onBack() {
                uni.navigateBack({
                    delta: 1
                })
            }
        }
    }
</script>

支持自定義背景色(漸變)、文字顏色、標題居中、搜索框、透明沉浸式、是否固定及層級等功能。

也可以根據自定義插槽來實現一些城市選擇、按鈕、圓點提示、圖片等功能。

<navbar :back="true" title="標題內容" bgcolor="#09f" color="#fff" fixed zIndex="1010" />

<navbar custom bgcolor="linear-gradient(to right, #ff007f, #0000ff)" color="#55ffff" center transparent zIndex="3003">
    <template slot="back"><text class="iconfont icon-arrL"></text></template>
    <template slot="backText"><text>我的</text></template>
    <template slot="title"><image src="/static/img2.jpg" style="height:20px;width:20px;" /> Admin</template>
    <template slot="right">
            <view class="ml-20" @click="handleAdd"><text class="iconfont icon-tianjia"></text></view>
            <view class="ml-20"><text class="iconfont icon-msg"></text></view>
    </template>
</navbar>

 

◆ uniapp自定義底部標簽欄導航

<!-- 標簽欄模板 -->
<template>
    <view class="ua__tabbar" :class="{'fixed': fixed}">
        <view class="ua__tabbar-wrap flexbox flex-alignc" :style="{'background': bgcolor}">
            <view v-for="(item, index) in tabs" :key="index" class="ua__tabbar-item flexbox flex-col" :class="currentTabIndex == index ? 'on' : ''" @click="switchTabs(index, item)">
                <view v-if="item.icon||item.img" class="ua__tabbar-icon" :class="{'dock': item.dock}">
                    <template v-if="item.dock">
                        <view class="dock-bg flexbox" :style="{'background': item.dockBg ? item.dockBg : activeColor}">
                            <text v-if="item.icon" class="iconfont nvuefont" :class="item.icon" :style="{'color': (currentTabIndex == index && !item.dock ? activeColor : color), 'font-size': item.iconSize}">{{item.icon.charAt(1) == '' ? item.icon : ''}}</text>
                            <image v-if="item.img" class="iconimg" :src="currentTabIndex == index && item.activeImg ? item.activeImg : item.img" :style="{'font-size': item.iconSize}" />
                        </view>
                    </template>
                    <template v-else>
                        <text v-if="item.icon" class="iconfont nvuefont" :class="item.icon" :style="{'color': (currentTabIndex == index && !item.dock ? activeColor : color), 'font-size': item.iconSize}">{{item.icon.charAt(1) == '' ? item.icon : ''}}</text>
                        <image v-if="item.img" class="iconimg" :src="currentTabIndex == index && item.activeImg ? item.activeImg : item.img" :style="{'font-size': item.iconSize}" />
                    </template>
                    <text v-if="item.badge" class="vui__badge ua__tabbar-icon__badge">{{item.badge}}</text>
                    <text v-if="item.dot" class="vui__badge-dot ua__tabbar-icon__badgeDot"></text>
                </view>
                <view v-if="item.title&&!item.dock" class="ua__tabbar-title">
                    <text class="ua__tabbar-title__text" :style="{'color': (currentTabIndex == index ? activeColor: color)}">{{item.title}}</text>
                    <template v-if="!item.icon&&!item.img">
                        <text v-if="item.badge" class="vui__badge ua__tabbar-title__badge">{{item.badge}}</text>
                        <text v-if="item.dot" class="vui__badge-dot ua__tabbar-title__badgeDot"></text>
                    </template>
                </view>
            </view>
        </view>
    </view>
</template>
<script>
    export default {
        props: {
            current: { type: [Number, String], default: 0 },
            // 背景色
            bgcolor: { type: String, default: '#fff' },
            // 顏色
            color: { type: String, default: '#9d9ea5' },
            // 激活顏色
            activeColor: { type: String, default: '#ff007f' },
            // 是否固定
            fixed: { type: [Boolean, String], default: false },
            // tab選項
            tabs: {
                type: Array,
                default: () => []
            },
        },
        data() {
            return {
                currentTabIndex: this.current
            }
        },
        beforeCreate() {
            // #ifdef APP-NVUE
            var domModule = weex.requireModule('dom');
            domModule.addRule('fontFace', {
                'fontFamily': "nvueIcon",
                'src': "url('/static/fonts/iconfont.ttf')"
            });
            // #endif
        },
        created() {
            /* uniapp獲取當前頁面路徑 (App、小程序、H5通用) */
            let pages = getCurrentPages() //獲取頁面棧數組
            let page = pages[pages.length - 1] //獲取當前頁面對象
            let route = page.route //獲取當前頁面路由
            this.selectRoute(route)
        },
        methods: {
            // 匹配當前路由頁面
            selectRoute(curPath) {
                curPath = curPath.substr(0, 1) == '/' ? curPath : '/' + curPath
                this.tabs.map((item, index) => {
                    if(item.path == curPath) {
                        this.currentTabIndex = index
                    }
                })
            },
            switchTabs(index, item) {
                if(item.path) {
                    // this.$router.push(item.path)
                    uni.navigateTo({
                        url: item.path
                    })
                }else {
                    this.currentTabIndex = index
                    this.$emit('click', index)
                }
            }
        }
    }
</script>
<style scoped>
    .nvuefont {font-family: nvueIcon;}
    .ua__tabbar {
        /* #ifndef APP-NVUE */
        display:-webkit-box; display:-webkit-flex; display:flex; display:-ms-flexbox;
        /* #endif */
        flex-direction: row;
    }
    .ua__tabbar-wrap {flex: 1; flex-direction: row; background-color: #fff; color: #333; height: 110rpx; position: relative; z-index: 2021;}
    .ua__tabbar.fixed{padding-top: 110rpx;}
    .ua__tabbar.fixed .ua__tabbar-wrap{
        /* #ifdef APP-NVUE */
        left: 0; right: 0;
        /* #endif */
        /* #ifndef APP-NVUE */
        width: 100%;
        /* #endif */
        max-width: 750px; position: fixed; bottom: 0;
    }
    .ua__tabbar-item{flex: 1; align-items: center; justify-content: center; height: 110rpx; position: relative;}
    /* 圖標 */
    .ua__tabbar-icon{
        /* #ifdef APP-NVUE */
        padding: 0 20rpx;
        /* #endif */
        display: flex; align-items: center; justify-content: center; margin: 0 auto; height: 50rpx; position: relative; border:1px dashed red;
    }
    /* dock菜單 */
    .ua__tabbar-item .dock {
        /* #ifdef APP-NVUE */
        height: 200rpx;
        /* #endif */
        /* #ifndef APP-NVUE */
        position: static;
        /* #endif */
        border: 1px solid green;
    }
    .ua__tabbar-item .dock .dock-bg {
        background-color: #f57b15; border-radius: 1000rpx; 
        align-items: center; justify-content: center; height: 100rpx; width: 100rpx; 
        /* #ifdef APP-NVUE */
        box-shadow: 0 0 6px rgba(0,0,0,.3);
        /* #endif */
        /* #ifndef APP-NVUE */
        box-shadow: 0 8px 12px rgba(0,0,0,.3);
        position: absolute; top: -50rpx; left: 50%; transform: translateX(-50%);
        /* #endif */
    }
    .ua__tabbar-item .dock .iconfont {color: #fff!important;}
    /* 字體圖標/圖片 */
    .ua__tabbar-item .iconfont{color:#9d9ea5; font-size: 45rpx; transition: color .3s;}
    .ua__tabbar-item .iconimg{display: block; font-size: 40rpx; height: 1em; width: 1em;}
    .ua__tabbar-item.on .iconfont{color:#f57b15;}
    /* 標題 */
    .ua__tabbar-title{
        /* #ifdef APP-NVUE */
        padding: 0 20rpx;
        /* #endif */
        position: relative; transition: color .3s; border: 1px solid blue;
    }
    .ua__tabbar-title__text {color: #9d9ea5; font-size: 30rpx; }
    .ua__tabbar-item.on .ua__tabbar-title__text{color: #f57b15;}
</style>

支持自定義背景色(漸變)、文字顏色|選中顏色、是否固定、是否dock凸起按鈕等功能。

<tabbar bgcolor="linear-gradient(to top, rgba(0, 255, 127, 0.9), transparent)" color="#eee" activeColor="#ff0" fixed @click="handleTabClicked"
    :tabs="[
        {
            path: `/pages/index/index`,
            icon: `icon-home`,
            title: `首頁`,
            badge: 38,
        },
        {
            icon: `icon-tianjia`,
            dock: true,
            dockBg: `#ff007f`,
            iconSize: `30px`,
        },
        {
            icon: `\ue606`,
            dot: true,
            title: `錢包`,
        },
    ]"
 />

tabs選項里面的參數

path: '/pages/index/index'   自定義跳轉頁面
icon: 'icon-home'            iconfont圖標 支持icon-xxx和`\ue642`寫法,在nvue頁面必須寫成`\ue642`格式
title: '首頁'                 標題
img: 'http://...'            自定義圖片地址
activeImg: ''                自定義選中圖片
dock: true                   底部中間凸起按鈕
dockBg: '#f90'               凸起按鈕背景色(不設置則為activeColor)
iconSize: '30px'             圖標/圖片大小
badge: 18                    小紅點數字
dot: true                    小圓點

根據項目需要,可以設置多個子標簽欄菜單。

注意:在nvue頁面,icon圖標則需要使用 '\ue642' 這種unicode寫法。

<tabbar bgcolor="linear-gradient(to right, #00ffff, #00ff7f)" color="#fff" active-color="#ff007f" fixed="true" @click="tabbarClicked"
    :tabs="[
        { path: `/pages/index/index`, icon: `icon-search`, title: `首頁`, badge: 6 },
        { icon: `\ue644`, dot: true },
        { img: `/static/logo.png`, title: `發布`, dock: true, dockBg: `#ff007f`, iconSize: `30px` },
        { img: `/static/img1.jpg`, activeImg: `/static/img2.jpg`, title: `圖片`, dot: true },
        { path: `/pages/ucenter/index`, icon: `icon-search`, title: `我` },
    ]"
/>

ending,基於uniapp模擬導航條/底部tabbar組件就介紹到這里。希望以上分享對大家有所幫助!😃

💝最后

開發不易,希望各位小伙伴們多多支持下哈~~☕️☕️

目前這兩個組件均已經上傳到uniapp插件市場,大家可以一次性拿走使用!

https://ext.dcloud.net.cn/plugin?id=5593

https://ext.dcloud.net.cn/plugin?id=5592

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM