uniapp custom bottom navigation bar

When working on a project, it is often encountered that the style on the UI diagram cannot be implemented in the existing framework. For example, the font of the bottom navigation bar can be changed into a gradient when it is selected. The css style can be directly modified in the browser, but it is found that it does not take effect at all when running to the real machine. After consulting the official documents, it is found that these attribute changes are not supported at all on the app side

The following is the process of making the bottom navigation bar in the project

1. First, use the official bottom navigation bar. See my document for details https://blog.csdn.net/weixin_50606255/article/details/116270949

When using this bottom navigation bar, you cannot change the font color to gradient (app side)

 2. To achieve the effect of UI design, you need to customize the bottom navigation bar, where the code can be copied and used directly;

<template>
	<view class="uni-tabbar">
		<view class="uni-tabbar__item" v-for="(item,index) in tabbar" :key="index" @tap="changeTab(item)">
			<!-- The font icon is used above to solve the effect that the icon will flash when switching pages. After all, each page will flash, which is not very good-looking. You can switch to the following image mode -->
			<view v-if="true" class="uni-tabbar__bd">
				<view class="uni-tabbar__icon">
					<img v-if="item.pagePath == pagePath" class="uni-w-42 uni-h-42" :src="item.selectedIconPath" />
					<img v-else class="uni-w-42 uni-h-42" :src="item.iconPath" />
				</view>
			</view>
			<view class="uni-tabbar__label">
				{{item.text}}
			</view>
		</view>
	</view>
</template>
<script>
	export default {
		props: {
			pagePath: null
		},
		data() {
			return {
				page: 'contact',
				showPage: false,
				containerHeight: 400,
				tabbar: [{
						"pagePath": "/pages/home/home",
						"iconPath": "static/img/tob_front_icon_normal.png", //The tab icon path is not selected
						"selectedIconPath": "static/img/tob_front_icon_selected.png", //Select the tab icon path
						"text": "home page",
					},
					{
						"pagePath": "/pages/monitor/monitor", //Page path
						"text": "monitor", //tab font display
						"iconPath": "static/img/tob_News_icon_normal.png", //The tab icon path is not selected
						"selectedIconPath": "static/img/tob_News_icon_selected.png" //Select the tab icon path
					}, {
						"pagePath": "/pages/fund/fund",
						"text": "conduct financial transactions",
						"iconPath": "static/img/tob_wealth_icon_normal.png",
						"selectedIconPath": "static/img/tob_wealth_icon_selected.png"
					},
					{
						"pagePath": "/pages/strategy/strategy", //Page path
						"text": "extension", //tab font display
						"iconPath": "static/img/tob_News_icon_normal.png", //The tab icon path is not selected
						"selectedIconPath": "static/img/tob_News_icon_selected.png" //Select the tab icon path
					},
					{
						"pagePath": "/pages/my/my",
						"iconPath": "static/img/tob_my_icon_normal.png", //The tab icon path is not selected
						"selectedIconPath": "static/img/tob_my_icon_selected.png", //Select the tab icon path
						"text": "my",
					}
				]
			};
		},
		onLoad() {
			console.log(this.pagePath)
		},
		methods: {
			changeTab(item) {
				let currentPage = item.pagePath;
				uni.showLoading({
					title: 'Loading...'
				})
				uni.redirectTo({
					url: currentPage,
					success: (e) => {
						uni.hideLoading();
					},
					fail: (e) => {
					}
				})
			},
		}
	}
</script>
<style lang="scss" scoped>
	.uni-tabbar {
		position: fixed;
		bottom: 0;
		z-index: 999;
		width: 100%;
		height: 6%;
		display: flex;
		justify-content: space-around;
		
		padding: 7rpx 0;
		box-sizing: border-box;
		background-color: #fff;
		box-shadow: 0px 10px 20px 0px rgba(75, 51, 100, 0.05);

		.uni-tabbar__item {
			display: flex;
			flex-direction: column;
			.uni-tabbar__bd { // tabBar single item
				.uni-tabbar__icon { // tabBar Icon
					width: 54rpx;
					height: 83rpx;
					img {
						width: 100%;
						height: 100%;
					}
				}
			}

			.uni-tabbar__label { // tabBar text
				font-size: 22rpx;
				font-family: $PF-SC-Rfamily;
				font-weight: 400;
				color: #D8DCE7;
				text-align: center;
				&.active {
					background-image: linear-gradient(to right top, #1CFDF1, #B330FF);
					font-size: 22rpx;
					-webkit-background-clip: text;
					-moz-background-clip: text;
					background-clip: text;
					box-decoration-break: clone;
					-webkit-box-decoration-break: clone;
					-moz-box-decoration-break: clone;
					color: transparent;
					position: relative;
				}
			}
		}

		// .uni-tabbar__icon {
		// 	height: 42upx;
		// 	line-height: 42upx;
		// 	text-align: center;
		// }

		.icon {
			display: inline-block;
		}

		// .uni-tabbar__label {
		// 	line-height: 24upx;
		// 	font-size: 24upx;
		// 	color: #999;

		// 	&.active {
		// 		color: #1ca6ec;
		// 	}
		// }
	}
</style>

In main JS inside the global injection

Then introduce it on the page you need

So you can get such a navigation bar

In this way, the gradient navigation bar is realized, but there is a bug, that is, when switching pages, the bottom navigation bar will be reloaded, which doesn't look so smooth, because the writing method is to match the value passed by each page component and jump if it meets the conditions, which is equivalent to jumping to a new page.

3. In order to change the bug in the display, I changed the writing method to imitate the style of the tab

<template>
	<view>
		<view class="content">
			<homePage v-if="currentPage == 0"/>
			<monitorPage v-if="currentPage == 1"/>
			<fundPage v-if="currentPage == 2"/>
			<stragePage v-if="currentPage == 3"/>
			<myPage v-if="currentPage == 4" />
		</view>
		<view class="tabbar" :style="{'padding-bottom': paddingBottomHeight + 'rpx'}">
		<!-- <view class="tabbar"> -->
			<view class="tabbar-item" v-for="(item, index) in list" :key="index" @click="tabbarChange(index)">
				<image class="item-img" :src="item.icon_a" v-if="currentPage == index"></image>
				<image class="item-img" :src="item.icon" v-else></image>
				<view class="item-name" :class="{'tabbarActive': currentPage == index}" v-if="item.text">{{item.text}}
				</view>
			</view>
		</view>
	</view>
</template>
<script>
	import homePage from "../pages/home/home.vue";
	import monitorPage from "../pages/monitor/monitor.vue";
	import fundPage from "../pages/fund/fund.vue";
	import stragePage from "../pages/strategy/strategy.vue";
	import myPage from "../pages/my/my.vue";
	export default {
		components: {
			homePage,
			monitorPage,
			fundPage,
			stragePage,
			myPage
		},
		data() {
			return {
				currentPage: 0,
				paddingBottomHeight: 0, //Bottom fitting height of Apple X and above mobile phones
				list: [{
					text: 'home page',
					icon: "/static/img/tob_front_icon_normal.png", //No icon selected
					icon_a: "/static/img/tob_front_icon_selected.png", //Select Picture
					path: "/pages/home/home", //Page path
				}, {
					text: 'Practical training',
					icon: "/static/img/tob_Facts_icon_normal.png", //No icon selected
					icon_a: "/static/img/tob_Facts_icon_selected.png", //Select Picture
					path: "/pages/monitor/monitor", //Page path
				}, {
					text: 'conduct financial transactions',
					icon: "/static/img/tob_wealth_icon_normal.png",
					icon_a: "/static/img/tob_wealth_icon_selected.png",
					path: "/pages/fund/fund",
				}, {
					text: 'extension',
					icon: "/static/img/tob_Promote_icon_normal.png",
					icon_a: "/static/img/tob_Promote_icon_selected.png",
					path: "/pages/strategy/strategy",
				}, {
					text: 'my',
					icon: "/static/img/tob_my_icon_normal.png",
					icon_a: "/static/img/tob_my_icon_selected.png",
					path: "/pages/my/my",
				}, ]
			};
		},
		created() {
		    let that = this;
		    uni.getSystemInfo({
		        success: function (res) {
		            let model = ['X', 'XR', 'XS', '11', '12', '13', '14', '15'];
		            model.forEach(item => {
		                //Fit the bottom above the iPhone X and give the tabbar a certain height of padding bottom
		                if(res.model.indexOf(item) != -1 && res.model.indexOf('iPhone') != -1) {
		                    that.paddingBottomHeight = 40;
		                }
		            })
		        }
		    });
		},
		watch: {

		},
		methods: {
			tabbarChange(index) {
				this.currentPage = index;
			}
		}
	};
</script>

 

<style lang="scss" scoped>
	.content {
		width: 100%;
		height: 94%;
	}

	.tabbar {
		position: fixed;
		bottom: 0;
		left: 0;
		right: 0;
		display: flex;
		justify-content: space-around;
		align-items: center;
		width: 100%;
		height: 6%;
		background-color: #ffffff;

		.tabbar-item {
			display: flex;
			flex-direction: column;
			align-items: center;
			justify-content: center;
			width: 56rpx;
			height: 85%;

			.item-img {
				width: 54rpx;
				height: 54rpx;
			}

			.item-name {
				text-align: center;
				font-size: 22rpx;
				font-weight: 400;
				font-family: $PF-SC-Rfamily;
				color: #D8DCE7;
			}

			.tabbarActive {
				background-image: linear-gradient(to right top, #1CFDF1, #B330FF);
				font-size: 22rpx;
				-webkit-background-clip: text;
				-moz-background-clip: text;
				background-clip: text;
				box-decoration-break: clone;
				-webkit-box-decoration-break: clone;
				-moz-box-decoration-break: clone;
				color: transparent;
				position: relative;
				animation: mymove 2s infinite;
			}

			@keyframes mymove {
				0% {
					transform: scale(1);
					/*Start at original size*/
				}

				25% {
					transform: scale(1.2);
					/*Zoom in 1.1x*/
				}

				50% {
					transform: scale(1);
				}

				75% {
					transform: scale(1.2);
				}

			}
		}
	}
</style>

In this way, the effect I want can be perfectly realized, and the animation is added to the bottom tab. The above code can be copied and used directly, and the test is effective (app end and h5 end);

However, after this writing, the sub component can't request data because it changes the life cycle of the sub component. Just change onLoad to mounted in vue;

Another problem is that there may be problems in the jump of sub components. Just change the path to an absolute path. If it still doesn't work, add '/' before the absolute path;

The above is the pit I encountered. If there is any problem, please leave a message!!

Keywords: Vue Vue.js css uni-app uniapp

Added by jediman on Fri, 17 Dec 2021 16:10:19 +0200