vue3uniapp实现自定义拱形底部导航栏,解决首次闪烁问题

前言:

我最初在网上翻阅查找了很多方法,发现大家都是说在page.json中tabbar中添加:"custom": true,即可解决首次闪烁的问题,可是添加了我这边还是会闪烁,因此我这边改变了思路,使用了虚拟页面来解决此问题。

效果图:

一:编写

  1. 在page.json中写一个初始页面,pages中的第一个对象是默认展示第一个页面,所以一定要在写一个。
javascript 复制代码
  "pages": [
        {
            "name": "index",
            "path": "pages/index/index",
            "style":{
                "navigationStyle":"custom"
            }
        },

2.新建index文件

3.编写index文件,因为我有三个页面,底部会有三个选项,每一个组件对应一个页面,Tabbr是我自定义的底部导航栏

javascript 复制代码
    <view>
        <view>
            <view v-if="pageStatus[0]" class="page__container" :style="pageContainerStyle(0)">
                <scroll-view class="scroll-view" scroll-y>
                    <BasicPage />
                </scroll-view>
            </view>
            <view v-if="pageStatus[1]" class="page__container" :style="pageContainerStyle(1)">
                <scroll-view class="scroll-view" scroll-y>
                    <InquiriesPage />
                </scroll-view>
            </view>
            <view v-if="pageStatus[2]" class="page__container" :style="pageContainerStyle(2)">
                <scroll-view class="scroll-view" scroll-y>
                    <user />
                </scroll-view>
            </view>
        </view>
        //自定义底部组件,后面会讲怎么写
        <Tabbr :current-page="0" @change="change"></Tabbr>
    </view>

4.编写自定义导航栏,可以直接复制,修改路径为自己文件路径

javascript 复制代码
<template>
    <view class="tabbar-home">
        <!-- 拱形区域 -->
        <view class="arched"></view>
        <view class="arched-bg"></view>
        <!-- 盒子 -->
        <view class="tabbar-container">
            <view class="tabbar-item" v-for="(item, index) in tabbarList" :class="[item.centerItem ? ' center-item' : '']" @click="changeItem(item)">
                <view class="item-top">
                    <image :src="currentItem == item.id ? item.selectIcon : item.icon"></image>
                </view>
                <view class="item-bottom" :class="[currentItem == item.id ? 'item-active' : '']">
                    <text>{{ item.text }}</text>
                </view>
            </view>
        </view>
    </view>
</template>
<script setup>
import { defineProps, onMounted, ref } from 'vue'
const props = defineProps({
    currentPage: {
        type: Number,
        default: 0,
    },
})
const currentItem = ref(0)
const tabbarList = ref([
    {
        id: 0,
        path: '/pages/home/index',
        icon: '/static/images/tabBar/unhome.png',
        selectIcon: '/static/images/tabBar/homeSelect.png',
        text: '首页',
        centerItem: false,
    },
    {
        id: 1,
        path: '/pages/detail/index',
        icon: '/static/images/tabBar/unInquiries.png',
        selectIcon: '/static/images/tabBar/Inquiries.png',
        text: '问询',
        centerItem: true,
    },
    {
        id: 2,
        path: '/pages/user/index',
        icon: '/static/images/tabBar/unhome.png',
        selectIcon: '/static/images/tabBar/homeSelect.png',
        text: '我的',
        centerItem: false,
    },
])
const emit = defineEmits(['change'])
function changeItem(item) {
    currentItem.value = item.id
    emit('change', item)
    // uni.switchTab({
    //     url: item.path,
    // })
}

onMounted(() => {
    currentItem.value = props.currentPage
    // 非微信小程序需隐藏原生tabBar(微信小程序已通过"custom": true配置项隐藏原生tabbar)
    if (process.env.VUE_APP_PLATFORM != 'mp-weixin') {
        uni.hideTabBar()
    }
})
</script>

<style lang="scss" scoped>
.tabbar-home {
    z-index: 20090;
    height: 100rpx;
    position: fixed;
    left: 0;
    bottom: 0;
    box-shadow: 0rpx 0rpx 30rpx 0rpx rgba(0, 0, 0, 0.07);
    width: 100%;
    box-sizing: content-box;
    padding-bottom: env(safe-area-inset-bottom) !important;
}
view {
    padding: 0;
    margin: 0;
    box-sizing: border-box;
}

.tabbar-container {
    position: absolute;
    bottom: 0rpx;
    left: 50%;
    transform: translateX(-50%);
    width: 100%;
    /* box-shadow: 0 0 5px    #8d6c36; */
    display: flex;
    align-items: center;
    justify-content: space-around;
    padding: 5rpx 0;
    color: #8d6c36;
    height: 100%;
    padding-bottom: env(safe-area-inset-bottom) !important;
    box-shadow: 0rpx 0rpx 30rpx 0rpx rgba(0, 0, 0, 0.07);
    background-color: rgba(255, 255, 255, 1);
    z-index: inherit;
}

.tabbar-container .tabbar-item {
    width: 20%;
    height: 80rpx;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    text-align: center;
}

.tabbar-container .item-active {
    color: #01beff;
}

.tabbar-container .center-item {
    display: block;
    position: relative;
    margin-top: 20rpx;
}

.tabbar-container .tabbar-item .item-top {
    width: 70rpx;
    height: 70rpx;
    padding: 10rpx;
    background: #ffffff;
}

.tabbar-container .center-item .item-top {
    flex-shrink: 0;
    width: 100%;
    height: 140rpx;
    padding: 20rpx;
    position: absolute;
    top: -70rpx;
    /* left: calc(50% - 50rpx); */
    border-radius: 50%;
    /* box-shadow: 0 0 5px #999; */
    // background-color: #f3d9a6;
}

.tabbar-container .tabbar-item .item-top image {
    width: 40rpx;
    height: 40rpx;
}

.tabbar-container .center-item .item-top image {
    width: 70rpx;
    height: 70rpx;
}

.tabbar-container .tabbar-item .item-bottom {
    font-size: 28rpx;
    width: 100%;
}

.tabbar-container .center-item .item-bottom {
    position: absolute;
    bottom: 0;
}
.arched {
    width: 120rpx;
    height: 120rpx;
    left: 50%;
    top: -42rpx;

    position: absolute;
    transform: translateX(-50%);
    border-radius: 50%;
    box-shadow: 0rpx 0rpx 30rpx 0rpx rgba(0, 0, 0, 0.07);
    background-color: rgba(255, 255, 255, 1);
    // border: 2rpx solid rgba(0, 0, 0, 0.1);
    z-index: 20089;
}
.arched-bg {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    z-index: 20089;
    background-color: rgba(255, 255, 255, 1);
}
</style>

5.在index文件中引入了自定义导航栏组件和各页面组件

javascript 复制代码
<script setup lang="ts">
import Tabbr from '../Tabbar/index.vue'
import BasicPage from './sub-page/BasicPage/index.vue'
import InquiriesPage from './sub-page/Inquiries/index.vue'
import user from './sub-page/user/index.vue'
import { useOrderedChildren } from './sub-page/hooks'
const { children: items, addChild: addItem, removeChild: removeItem } = useOrderedChildren<any>()
const tabbarData = ref([
    {
        id: 0,
        path: '/pages/home/index',
        icon: '/static/images/tabBar/unhome.png',
        selectIcon: '/static/images/tabBar/homeSelect.png',
        text: '首页',
        centerItem: false,
    },
    {
        id: 1,
        path: '/pages/detail/index',
        icon: '/static/images/tabBar/unhome.png',
        selectIcon: '/static/images/tabBar/homeSelect.png',
        text: '问询',
        centerItem: true,
    },
    {
        id: 2,
        path: '/pages/user/index',
        icon: '/static/images/tabBar/unhome.png',
        selectIcon: '/static/images/tabBar/homeSelect.png',
        text: '我的',
        centerItem: false,
    },
])

// 记录每个子页面的状态
const pageStatus = ref(Array.from({ length: tabbarData.value.length }, () => false))
const currentIndex = ref(0)
const change = (item: any) => {
    pageStatus.value = pageStatus.value.map(() => false)
    if (!pageStatus.value?.[item.id as number]) {
        pageStatus.value[item.id as number] = true
        currentIndex.value = item.id
        nextTick(() => {
            items.value?.[item.id as number]?.onLoad?.()
        })
    }
}
const pageContainerStyle = computed<(index: number) => any>(() => {
    console.log('currentIndex', currentIndex.value)
    return (index: number) => {
        const style: any = {}

        if (index !== currentIndex.value) {
            style.display = 'none'
        }

        return style
    }
})
onLoad((options) => {
    const index = Number(options?.index || 0)
    pageStatus.value[index] = true
    nextTick(() => {
        currentIndex.value = index
    })
})
</script>

6.对了提个醒,自定义导航栏组件要写在page文件中~

相关推荐
斯~内克4 分钟前
React Router 完全指南:从基础到高级实践
前端·react.js·前端框架
m0_748232396 分钟前
qwenvl 以及qwenvl 2 模型架构理解
android·前端·后端
冲!!9 分钟前
vue3中ref和reactive响应式数据、ref模板引用(组合式和选项式区别)、组件ref的使用
前端·javascript·vue.js
匹马夕阳10 分钟前
React vs Vue3深度对比与使用场景分析
前端·react.js·前端框架
Swift社区37 分钟前
【Swift 算法实战】利用 KMP 算法高效求解最短回文串
vue.js·算法·leetcode
七灵微40 分钟前
【前端】简单原生实例合集html,css,js
前端·css·html
祈澈菇凉40 分钟前
2025年React Hooks的进阶面试题130题及其答案解析..
前端·react.js·前端框架
Neo Evolution1 小时前
每天一个Flutter开发小项目 (6) : 表单与验证的专业实践 - 构建预约应用
android·开发语言·前端·javascript·flutter
大橙子房1 小时前
AI学习第六天-python的基础使用-趣味图形
前端·python·学习
小金子J2 小时前
Vue 表单优化:下拉框值改变前的确认提示与还原逻辑实现
前端·javascript·vue.js·elementui