当前位置: 首页 > article >正文

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

前言:

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

效果图:

一:编写

       1. 在page.json中写一个初始页面,pages中的第一个对象是默认展示第一个页面,所以一定要在写一个。

  "pages": [
        {
            "name": "index",
            "path": "pages/index/index",
            "style":{
                "navigationStyle":"custom"
            }
        },

        2.新建index文件

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

    <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.编写自定义导航栏,可以直接复制,修改路径为自己文件路径

<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文件中引入了自定义导航栏组件和各页面组件

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


http://www.kler.cn/a/372868.html

相关文章:

  • SDK如何测试
  • 油猴脚本-GPT问题导航侧边栏增强版
  • 数据结构 ——— 二叉树的概念及结构
  • 使用Fiddler Classic抓包工具批量下载音频资料
  • Qt——信号和槽
  • 泛微开发修炼之旅--48关于ecology重写消息卡片中的创建时间和创建人的解决方案及源码
  • mfc100.dll丢失的解决方法-电脑基础知识
  • uniapp的video视频属性打包app后层级过高
  • 【Java笔记】第十五章:IO流
  • (arxiv 2024)即插即用多尺度注意力聚合模块MSAA,即用即起飞
  • ubuntu双屏只显示一个屏幕另一个黑屏
  • PowerBI 根据条件选择获得不同的表格 因为IF和SWITCH只能返回标量而不能返回表格 Power BI
  • 《Python游戏编程入门》注-第4章3
  • Scala 的trait
  • 钉钉平台开发小程序
  • Linux 常用命令二
  • 空间音频技术
  • 计算机视觉常用数据集Foggy Cityscapes的介绍、下载、转为YOLO格式进行训练
  • WinUI AOT 发布
  • 输电线路云台变焦视频监控装置在智能识别和数据安全方面有哪些具体的优势和措施?
  • 【设计模式系列】代理模式(八)
  • python爬虫抓取豆瓣数据教程
  • redis:基本全局命令-键管理(1)
  • 同WiFi网络情况下,多个手机怎么实现不同城市的IP
  • MATLAB下的四个模型的IMM例程(CV、CT左转、CT右转、CA四个模型),附源代码可复制
  • yocto 下基于SDK的 tcpdump 移植