赞
踩
目录
项目描述:uniapp 使用 vue2/3,自定义封装 tabbar,底部凹凸透明显示。兼容h5、微信小程序、app等。html 和 css 部分代码一样,只有 js 功能代码,分别使用 vue2 和 vue3 的写法。
兼容性:h5、微信小程序、app端兼容,其他端没有测试过,应该也兼容的。
技术栈:uniapp + vue2 /3。
在h5端,底部是没有安全区域的,效果图如下:
在微信小程序端和 app端中,苹果手机底部可能会有安全区域,效果图如下
- <template>
- <view class="tabbar" :style="`--repeat: ${tabbar.length}`">
- <view class="tabbar-nav">
- <view class="tabbar-nav-a" v-for="(item, index) in tabbar" :key="index"
- :class="pageIndex == index ? 'after' : ''" @tap="tapTabbar(item, index)">
- <view class="after-icon" v-if="pageIndex == index">
- <uni-icons :type="item.selectedIconPath" size="60rpx" color="#fff"></uni-icons>
- </view>
- <uni-icons :type="item.iconPath" size="48rpx" v-else></uni-icons>
- <text class="text">{{ item.text }}</text>
- </view>
- </view>
- <view class="safeZone" :style="{ 'height': safeAreaInsetsBottom }"></view>
- </view>
- <view :style="{ 'height': tabbarHeight }"></view>
- </template>
:style="`--repeat: ${tabbar.length}`" :tabbar的数量,根据数组的长度改变。
:style="{ 'height': safeAreaInsetsBottom }" :底部安全区域的高度。
:style="{ 'height': tabbarHeight }":tabbar导航栏的高度,防止内容被遮挡。
- <style lang="scss" scoped>
- .tabbar {
- --width: 100rpx;
- --height: 100rpx;
- --size: 60rpx;
- --repeat: 4;
- --color: #FC536E;
- --radius: 20rpx;
- --rgba: rgba(255, 255, 255, 0.95);
- position: fixed;
- width: 100%;
- bottom: 0;
- z-index: 999;
-
- .tabbar-nav {
- display: grid;
- grid-template-columns: repeat(var(--repeat), 1fr);
-
- .tabbar-nav-a {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: flex-end;
- padding: 4rpx 0;
- position: relative;
- height: var(--height);
- background-color: var(--rgba);
- box-sizing: border-box;
- filter: drop-shadow(2rpx -2rpx 2rpx rgba(0, 0, 0, 0.1));
- font-size: 24rpx;
-
- &:nth-child(1) {
- border-top-left-radius: var(--radius);
- }
-
- &:nth-last-child(1) {
- border-top-right-radius: var(--radius);
- }
- }
-
- .after {
- background: radial-gradient(circle at center top, transparent var(--size), var(--rgba) 0)center top;
-
- .after-icon {
- width: var(--width);
- height: var(--height);
- background-color: var(--color);
- border-radius: 50%;
- position: absolute;
- left: 50%;
- top: 0;
- transform: translate(-50%, -50%);
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- text {
- color: var(--color);
- }
- }
- }
-
- .safeZone {
- background-color: var(--rgba);
- }
- }
- </style>
background: radial-gradient(circle at center top, transparent var(--size), var(--rgba) 0)center top;凹凸透明显示,最主要就是靠这行样式代码。
--repeat: 4;:控制 tabbar 的数量
- <script>
- export default {
- name: "Tabbar",
- props: {
- pageIndex: {
- type: Number,
- default: 0,
- }
- },
- data() {
- return {
- tabbarHeight: '',
- safeAreaInsetsBottom: '',
- tabbar: [{
- iconPath: "heart",
- selectedIconPath: "heart-filled",
- text: "爱吧",
- pagePath: "/pages/index/index"
- },
- {
- iconPath: "wallet",
- selectedIconPath: "wallet-filled",
- text: "记账",
- pagePath: "/pages/bookkeeping/bookkeeping"
-
- },
- {
- iconPath: "chat",
- selectedIconPath: "chat-filled",
- text: "社区",
- pagePath: "/pages/community/community"
-
- },
- {
- iconPath: "person",
- selectedIconPath: "person-filled",
- text: "我",
- pagePath: "/pages/my/my"
- }]
- }
- },
- created() {
- uni.hideTabBar({ animation: true })
- let res = uni.getWindowInfo().safeAreaInsets.bottom + 50
- this.safeAreaInsetsBottom = res + "rpx";
- this.tabbarHeight = res * 2 + "rpx"
- },
- methods: {
- tapTabbar(val, index) {
- uni.switchTab({
- url: val.pagePath
- })
- }
- }
-
- }
- </script>
- <script setup>
- defineOptions({
- name: "Tabbar"
- });
- const tabbarHeight = ref('')
- const safeAreaInsetsBottom = ref(0)
-
- const props = defineProps({
- pageIndex: {
- type: Number,
- default: 0
- }
- });
- const tabbar = reactive([{
- iconPath: "heart",
- selectedIconPath: "heart-filled",
- text: "爱吧",
- pagePath: "/pages/index/index"
- },
- {
- iconPath: "wallet",
- selectedIconPath: "wallet-filled",
- text: "记账",
- pagePath: "/pages/bookkeeping/bookkeeping"
-
- },
- {
- iconPath: "chat",
- selectedIconPath: "chat-filled",
- text: "社区",
- pagePath: "/pages/community/community"
-
- },
- {
- iconPath: "person",
- selectedIconPath: "person-filled",
- text: "我",
- pagePath: "/pages/my/my"
- }
-
- ])
- onMounted(() => {
- uni.hideTabBar({ animation: true })
- let res = uni.getWindowInfo().safeAreaInsets.bottom
- safeAreaInsetsBottom.value = res * 2 + "rpx";
- tabbarHeight.value = res + 50 * 2 + "rpx";
- });
- const tapTabbar = (val, index) => {
- uni.switchTab({
- url: val.pagePath
- })
- }
- </script>
uni.hideTabBar({ animation: true }) :隐藏默认的 tabBar 。
let res = uni.getWindowInfo().safeAreaInsets.bottom :获取底部安全区域的高度,返回px单位。
this.safeAreaInsetsBottom = res * 2 + "rpx":将高度 px单位 转为 rpx单位。
this.tabbarHeight = res + 50 * 2 + "rpx" :(底部安全区域高度 + 导航栏自身高度)转 rpx单位。
<Tabbar :pageIndex="0"></Tabbar>
:pageIndex="0":用于控制激活状态,高亮显示。
- <template>
- <view class="tabbar" :style="`--repeat: ${tabbar.length}`">
- <view class="tabbar-nav">
- <view class="tabbar-nav-a" v-for="(item, index) in tabbar" :key="index"
- :class="pageIndex == index ? 'after' : ''"
- @tap="tapTabbar(item, index)">
- <view class="after-icon" v-if="pageIndex == index">
- <uni-icons size="60rpx" color="#fff"
- :type="item.selectedIconPath" ></uni-icons>
- </view>
- <uni-icons :type="item.iconPath" size="48rpx" v-else></uni-icons>
- <text class="text">{{ item.text }}</text>
- </view>
- </view>
- <view class="safeZone" :style="{ 'height': safeAreaInsetsBottom }"></view>
- </view>
- <view :style="{ 'height': tabbarHeight }"></view>
- </template>
- <script>
- export default {
- name: "Tabbar",
- props: {
- pageIndex: {
- type: Number,
- default: 0,
- }
- },
- data() {
- return {
- tabbarHeight: '',
- safeAreaInsetsBottom: '',
- tabbar: [{
- iconPath: "heart",
- selectedIconPath: "heart-filled",
- text: "爱吧",
- pagePath: "/pages/index/index"
- },
- {
- iconPath: "wallet",
- selectedIconPath: "wallet-filled",
- text: "记账",
- pagePath: "/pages/bookkeeping/bookkeeping"
-
- },
- {
- iconPath: "chat",
- selectedIconPath: "chat-filled",
- text: "社区",
- pagePath: "/pages/community/community"
-
- },
- {
- iconPath: "person",
- selectedIconPath: "person-filled",
- text: "我",
- pagePath: "/pages/my/my"
- }]
- }
- },
- created() {
- uni.hideTabBar({ animation: true })
- let res = uni.getWindowInfo().safeAreaInsets.bottom
- this.safeAreaInsetsBottom = res * 2 + "rpx";
- this.tabbarHeight = res + 50 * 2 + "rpx"
- },
- methods: {
- tapTabbar(val, index) {
- uni.switchTab({
- url: val.pagePath
- })
- }
- }
-
- }
- </script>
- <style lang="scss" scoped>
- .tabbar {
- --width: 100rpx;
- --height: 100rpx;
- --size: 60rpx;
- --repeat: 4;
- --color: #FC536E;
- --radius: 20rpx;
- --rgba: rgba(255, 255, 255, 0.95);
- position: fixed;
- width: 100%;
- bottom: 0;
- z-index: 999;
-
- .tabbar-nav {
- display: grid;
- grid-template-columns: repeat(var(--repeat), 1fr);
-
- .tabbar-nav-a {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: flex-end;
- padding: 4rpx 0;
- position: relative;
- height: var(--height);
- background-color: var(--rgba);
- box-sizing: border-box;
- filter: drop-shadow(2rpx -2rpx 2rpx rgba(0, 0, 0, 0.1));
- font-size: 24rpx;
-
- &:nth-child(1) {
- border-top-left-radius: var(--radius);
- }
-
- &:nth-last-child(1) {
- border-top-right-radius: var(--radius);
- }
- }
-
- .after {
- background: radial-gradient(circle at center top,
- transparent var(--size), var(--rgba) 0)center top;
-
- .after-icon {
- width: var(--width);
- height: var(--height);
- background-color: var(--color);
- border-radius: 50%;
- position: absolute;
- left: 50%;
- top: 0;
- transform: translate(-50%, -50%);
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- text {
- color: var(--color);
- }
- }
- }
-
- .safeZone {
- background-color: var(--rgba);
- }
- }
- </style>
- <template>
- <view class="tabbar" :style="`--repeat: ${tabbar.length}`">
- <view class="tabbar-nav">
- <view class="tabbar-nav-a" v-for="(item, index) in tabbar" :key="index"
- :class="pageIndex == index ? 'after' : ''"
- @tap="tapTabbar(item, index)">
- <view class="after-icon" v-if="pageIndex == index">
- <uni-icons size="60rpx" color="#fff"
- :type="item.selectedIconPath">
- </uni-icons>
- </view>
- <uni-icons :type="item.iconPath" size="48rpx" v-else></uni-icons>
- <text class="text">{{ item.text }}</text>
- </view>
- </view>
- <view class="safeZone" :style="{ 'height': safeAreaInsetsBottom }"></view>
- </view>
- <view :style="{ 'height': tabbarHeight }"></view>
- </template>
- <script setup>
- defineOptions({
- name: "Tabbar"
- });
- const tabbarHeight = ref('')
- const safeAreaInsetsBottom = ref(0)
-
- const props = defineProps({
- pageIndex: {
- type: Number,
- default: 0
- }
- });
- const tabbar = reactive([{
- iconPath: "heart",
- selectedIconPath: "heart-filled",
- text: "爱吧",
- pagePath: "/pages/index/index"
- },
- {
- iconPath: "wallet",
- selectedIconPath: "wallet-filled",
- text: "记账",
- pagePath: "/pages/bookkeeping/bookkeeping"
-
- },
- {
- iconPath: "chat",
- selectedIconPath: "chat-filled",
- text: "社区",
- pagePath: "/pages/community/community"
-
- },
- {
- iconPath: "person",
- selectedIconPath: "person-filled",
- text: "我",
- pagePath: "/pages/my/my"
- }
-
- ])
- onMounted(() => {
- uni.hideTabBar({ animation: true })
- let res = uni.getWindowInfo().safeAreaInsets.bottom
- safeAreaInsetsBottom.value = res * 2 + "rpx";
- tabbarHeight.value = res + 50 * 2 + "rpx";
- });
- const tapTabbar = (val, index) => {
- uni.switchTab({
- url: val.pagePath
- })
- }
- </script>
- <style lang="scss" scoped>
- .tabbar {
- --width: 100rpx;
- --height: 100rpx;
- --size: 60rpx;
- --repeat: 4;
- --color: #FC536E;
- --radius: 20rpx;
- --rgba: rgba(255, 255, 255, 0.95);
- position: fixed;
- width: 100%;
- bottom: 0;
- z-index: 999;
-
- .tabbar-nav {
- display: grid;
- grid-template-columns: repeat(var(--repeat), 1fr);
-
- .tabbar-nav-a {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: flex-end;
- padding: 4rpx 0;
- position: relative;
- height: var(--height);
- background-color: var(--rgba);
- box-sizing: border-box;
- filter: drop-shadow(2rpx -2rpx 2rpx rgba(0, 0, 0, 0.1));
- font-size: 24rpx;
-
- &:nth-child(1) {
- border-top-left-radius: var(--radius);
- }
-
- &:nth-last-child(1) {
- border-top-right-radius: var(--radius);
- }
- }
-
- .after {
- background: radial-gradient(circle at center top,
- transparent var(--size),
- var(--rgba) 0)center top;
-
- .after-icon {
- width: var(--width);
- height: var(--height);
- background-color: var(--color);
- border-radius: 50%;
- position: absolute;
- left: 50%;
- top: 0;
- transform: translate(-50%, -50%);
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- text {
- color: var(--color);
- }
- }
- }
-
- .safeZone {
- background-color: var(--rgba);
- }
- }
- </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。