【uniapp】开发微信小程序自定义底部tabbar

时间:2024-12-18 07:13:30

自定义tabBar的性能体验会低于原生tabBar,小程序端非必要不要自定义。但原生tabBar是相对固定的配置方式,可能无法满足所有场景,这就涉及到自定义tabBar。

一、使用流程
1、配置信息

中的 tabBar 项指定 custom 字段,同时其余 tabBar 相关配置也补充完整

"tabBar": {
    "color": "#ffffff",
	"selectedColor": "#6777FD",
	"custom": true,
	"list": [{},{},{}]
}
2、添加tabBar代码文件

①自定义公共组件

components目录下新建组件页面CustomTabBar,在中开发自定义组件

在这里插入图片描述

<template>
	<view class="tabBar"></view>
</template>

<script>
export default {
	props: {
		selected: Number
	},
	data() {
		return {
			color: "#fff",
			selectedColor: "#6777FD",
			list: [{},{},{}]
		}
	},
	methods: {
		switchTab(url) {
			({
				url
			})
		}
	}
}
</script>

<style lang="scss">

</style>

在需要用到tabBar的页面引入注册并使用组件,通过父子组件通信的方式传参当前是哪个索引页面selected,在子组件通过props接收并使用

<script>
	import CustomTabBar from "@/components/CustomTabBar/"
	export default {
		components: {
			CustomTabBar
		}
	}
</script>

<template>
	<view>
		<custom-tab-bar :selected="1" />
	</view>
</template>

②自定义tabbar

在这里插入图片描述
根目录创建custom-tab-bar文件夹,里面创建、、、文件进行开发,而不是vue文件,uniapp编译器会直接拷贝该目录到微信小程序中。

在这里插入图片描述

中:

Component({
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {
    selected: 0,
    color: "#fff",
    selectedColor: "#6777FD",
    list: [{},{},{}]
  },

  /**
   * 组件的方法列表
   */
  methods: {
    switchTab(e) {
      const data = e.currentTarget.dataset
      const url = data.path
      wx.switchTab({ url })
    },
  }
})

注意:如需实现 tab 选中态,要在当前页面下,通过 getTabBar 接口获取组件实例,并调用 setData 更新选中态

onShow() {
	// 原生微信小程序
	 if (typeof this.getTabBar === 'function' && this.getTabBar()) {
      this.getTabBar().setData({
        selected: 0
      })
    }
	// vue2
	if (typeof this.$mp.page.getTabBar === 'function' && this.$mp.page.getTabBar()) {
		this.$mp.page.getTabBar().setData({
			selected: 0
		})
	}
	// vue3
	if (typeof this.scope.page.getTabBar === 'function' && this.scope.page.getTabBar()) {
		this.scope.page.getTabBar().setData({
			selected: 0
		})
	}
}
二、具体案例

中:

"tabBar": {
	"color": "#ffffff",
	"selectedColor": "#6777FD",
	"custom": true,
	"list": [{
			"pagePath": "pages/aboutFind/use/use",
			"iconPath": "static/image/icon_find2.png",
			"selectedIconPath": "static/image/icon_find1.png",
			"text": "使用"
		},
		{
			"pagePath": "pages/index/index",
			"iconPath": "static/image/icon_go2.png",
			"selectedIconPath": "static/image/icon_go1.png",
			"text": "通行"
		},
		{
			"pagePath": "pages/myInfo/myInfo",
			"iconPath": "static/image/icon_set2.png",
			"selectedIconPath": "static/image/icon_set1.png",
			"text": "我的"
		}
	]
}

①自定义公共组件

中:

<template>
	<view class="tabBar">
		<view class="cont">
			<block v-for="(item,index) in list" :key="index" class="cont-item">
				<view @click="switchTab()"
					:class="{'search':  ? true : false, 'item': !, 'on': selected === index ? true : false, 'off': selected != index ? true : false}">
					<image :src=" selected===index ?  : ">
					</image>
					<view :class="{'txt': true,'selectedColor': selected === index ? true : false}">{{item.text}}</view>
				</view>
			</block>
		</view>
	</view>
</template>

<script>
export default {
	props: {
		selected: Number
	},
	data() {
		return {
			color: "#fff",
			selectedColor: "#6777FD",
			list: [{
					pagePath: "/pages/aboutFind/use/use",
					text: "使用",
					iconPath: "/static/image/icon_find2.png",
					selectedIconPath: "/static/image/icon_find1.png"
				},
				{
					pagePath: "/pages/index/index",
					text: "通行",
					iconPath: "/static/image/icon_go2.png",
					selectedIconPath: "/static/image/icon_go1.png",
					search: true
				},
				{
					pagePath: "/pages/myInfo/myInfo",
					text: "我的",
					iconPath: "/static/image/icon_set2.png",
					selectedIconPath: "/static/image/icon_set1.png"
				}
			]
		}
	},
	methods: {
		switchTab(url) {
			uni.switchTab({
				url
			})
		}
	}
}
</script>

<style lang="scss">
.tabBar {
	z-index: 100;
	width: 100%;
	position: fixed;
	bottom: 0;
	font-size: 28rpx;
	background-color: #fff;
	color: #636363;
	border-radius: 50rpx 50rpx 0px 0px;
}
.cont {
	z-index: 0;
	height: calc(100rpx + env(safe-area-inset-bottom) / 2);
	padding-bottom: 30rpx;
	display: flex;
	justify-content: space-around;
	.item {
		font-size: 24rpx;
		position: relative;
		width: 15%;
		text-align: center;
		padding: 0;
		display: block;
		height: auto;
		line-height: 1;
		margin: 0;
		background-color: inherit;
		overflow: initial;
		justify-content: center;
		align-items: center;
		padding-top: 20rpx;
	}
	.item:first-child {
		right: 45rpx;
	}
	.item:last-child {
		left: 45rpx;
	}
	.item image:first-child {
		width: 43rpx !important;
		height: 43rpx !important;
		margin: auto
	}
	.item image:last-child {
		width: 41rpx !important;
		height: 43rpx !important;
		margin: auto
	}
	.txt {
		margin-top: 20rpx;
	}
	.on {
		position: relative;
	}
	.on:not(:nth-child(2)):before {
		content: "";
		display: block;
		position: absolute;
		top: 0;
		width: 100%;
		height: 6rpx;
		background-color: #00BCD4;
		border-radius: 120rpx !important;
	}
	.search {
		position: absolute;
		left: 50%;
		transform: translate(-50%, 0);
		top: -50rpx;
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: center;
	}
	.search image {
		width: 100rpx !important;
		height: 100rpx !important;
		z-index: 2;
		border-radius: 100%;
	}
	.search .txt {
		margin-top: 26rpx;
	}
	.selectedColor {
		color: #00BCD4;
	}
}
</style>

在使用到组件的三个页面中,如index首页

<script>
	import CustomTabBar from "@/components/CustomTabBar/"
	export default {
		components: {
			CustomTabBar
		}
	}
</script>

<template>
	<view>
	    <!-- 传对应页面在list数组中位置的索引值 -->
		<custom-tab-bar :selected="1" />
	</view>
</template>

②自定义tabbar

中:

Component({
  /**
   * 组件的属性列表
   */
  properties: {

  },

  /**
   * 组件的初始数据
   */
  data: {
    selected: 0,
    color: "#fff",
    selectedColor: "#6777FD",
    list: [
      {
        pagePath: "/pages/aboutFind/use/use",
        text: "使用",
        iconPath: "/static/image/icon_find2.png",
        selectedIconPath: "/static/image/icon_find1.png"
      },
      {
        pagePath: "/pages/index/index",
        text: "通行",
        iconPath: "/static/image/icon_go2.png",
        selectedIconPath: "/static/image/icon_go1.png",
        search: true
      },
      {
        pagePath: "/pages/myInfo/myInfo",
        text: "我的",
        iconPath: "/static/image/icon_set2.png",
        selectedIconPath: "/static/image/icon_set1.png"
      }
    ]
  },

  /**
   * 组件的方法列表
   */
  methods: {
    switchTab(e) {
      const data = e.currentTarget.dataset
      const url = data.path
      wx.switchTab({ url })
    },
  }
})

中:

{
  "component": true,
  "usingComponents": {}
}

中:

<view class="tabBar">
  <view class="cont">
    <block wx:for="{{list}}" wx:key="index" class="cont-item">
      <view data-path="{{}}" data-index="{{}}" bindtap="switchTab" class="{{?'search':'item'}} {{selected === index ? 'on' : 'off'}}">
        <image src="{{selected === index  ?  : }}"></image>
        <view class="txt {{selected === index ? 'selectedColor' : ''}}">{{}}</view>
      </view>
    </block>
  </view>
</view>

中:

.tabBar {
  z-index: 100;
  width: 100%;
  position: fixed;
  bottom: 0;
  font-size: 28rpx;
  background-color: #fff;
  color: #636363;
  border-radius: 50rpx 50rpx 0px 0px;
}
.cont {
  z-index: 0;
  height: calc(100rpx + env(safe-area-inset-bottom) / 2);
  padding-bottom: 30rpx;
  display: flex;
  justify-content: space-around;
}
.cont .item {
  font-size: 24rpx;
  position: relative;
  /* flex: 1; */
  width: 15%;
  text-align: center;
  padding: 0;
  display: block;
  height: auto;
  line-height: 1;
  margin: 0;
  background-color: inherit;
  overflow: initial;
  justify-content: center;
  align-items: center;
  padding-top: 20rpx;
}
.cont .item:first-child {
  right: 45rpx;
}
.cont .item:last-child {
  left: 45rpx;
}
.cont .item image:first-child {
  width: 43rpx !important;
  height: 43rpx !important;
  margin: auto
}
.cont .item image:last-child {
  width: 41rpx !important;
  height: 43rpx !important;
  margin: auto
}
.cont .txt {
  margin-top: 20rpx;
}
.cont .on {
  position: relative;
}
.cont .on:not(:nth-child(2)):before {
  content: "";
  display: block;
  position: absolute;
  top: 0;
  width: 100%;
  height: 6rpx;
  background-color: #00BCD4;
  border-radius:120rpx !important;
}
.cont .search {
  position: absolute;
  left: 50%;
  transform: translate(-50%,0);
  top: -50rpx;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.cont .search image {
  width: 100rpx !important;
  height: 100rpx !important;
  z-index: 2;
  border-radius: 100%;
}
.cont .search .txt {
  margin-top: 26rpx;
}
.cont .selectedColor {
  color: #00BCD4;
}

在使用到custom-tab-bar的三个页面中:

onShow() {
	if (typeof this.$mp.page.getTabBar === 'function' && this.$mp.page.getTabBar()) {
		this.$mp.page.getTabBar().setData({
			selected: 1
		})
	}
}

在这里插入图片描述

三、疑惑

尽管在自定义页面中已经写了页面的配置项,但在 中的 list 配置页面仍然是必需的,为什么?

这两者的作用不同:

  • 自定义页面配置项:这些配置项是针对你自定义的页面的,包括页面路径、页面标题、页面使用的窗口配置等。这些配置项用于描述单个页面的属性和行为。
  • 中的 list 配置页面:这些配置项用于描述 TabBar 中的各个页面,包括页面路径、页面标题、页面图标路径等。这些配置项告诉微信客户端哪些页面需要在 TabBar 中显示,并且控制它们在 TabBar 中的位置和样式。