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文件中~

相关推荐
PleaSure乐事17 分钟前
Ant-Dseign-Pro如何去国际化及删除oneapi.json后出现程序直接结束问题的解决方案
前端·javascript·react.js·前端框架·json·oneapi·antdesignpro
榴莲千丞36 分钟前
第七章利用CSS和多媒体美化页面
前端·css·1024程序员节
奶糖 肥晨1 小时前
vue的路由的两种模式 hash与history 详细讲解
前端·vue.js·哈希算法
奶糖 肥晨1 小时前
react基础之reactHooks
前端·javascript·react.js
放逐者-保持本心,方可放逐1 小时前
vue3-ref 和 reactive
前端·javascript·vue.js
星河路漫漫1 小时前
ES6面试题:(第二天)
开发语言·前端·javascript
喝旺仔la2 小时前
Django+Vue全栈开发旅游网项目景点详情
前端·javascript·vue.js
snakeshe10102 小时前
深入理解 Web Workers:提升 Web 应用性能的利器
前端
May_Xu_2 小时前
vue3+less使用主题定制(多主题定制)可切换主题
前端·javascript·vue.js·vue·less·css3