当前位置:   article > 正文

uniapp 、vue2/3自定义底部 tabbar 导航栏凹凸透明显示,高级效果。(兼容h5、微信小程序、app)_uniapp自定义底部tabbar

uniapp自定义底部tabbar

目录

1.项目介绍

2.不同端的效果展示图

2.1无底部安全区域:

2.2有底部安全区域:

3. 代码编写

3.1 html 代码部分:

3.2 html 核心代码:

3.3 css 代码部分:

3.4 css 核心代码:

3.5 vue2 代码编写:

3.6 vue3 代码编写:

3.7 vue2/3 核心代码:

3.8 Tabbar 组件的使用:

4. vue2 完整代码

5. vue3 完整代码


1.项目介绍

项目描述:uniapp 使用 vue2/3,自定义封装 tabbar,底部凹凸透明显示。兼容h5、微信小程序、app等。html 和 css 部分代码一样,只有 js 功能代码,分别使用 vue2 和 vue3 的写法。

兼容性:h5、微信小程序、app端兼容,其他端没有测试过,应该也兼容的。

技术栈:uniapp + vue2 /3。

2.不同端的效果展示图

2.1无底部安全区域:

h5端,底部是没有安全区域的,效果图如下:

h5端,无底部安全区域效果图

2.2有底部安全区域:

微信小程序端app端中,苹果手机底部可能会有安全区域,效果图如下

微信小程序和app中,底部安全区域显示效果图

3. 代码编写

3.1 html 代码部分:

  1. <template>
  2. <view class="tabbar" :style="`--repeat: ${tabbar.length}`">
  3. <view class="tabbar-nav">
  4. <view class="tabbar-nav-a" v-for="(item, index) in tabbar" :key="index"
  5. :class="pageIndex == index ? 'after' : ''" @tap="tapTabbar(item, index)">
  6. <view class="after-icon" v-if="pageIndex == index">
  7. <uni-icons :type="item.selectedIconPath" size="60rpx" color="#fff"></uni-icons>
  8. </view>
  9. <uni-icons :type="item.iconPath" size="48rpx" v-else></uni-icons>
  10. <text class="text">{{ item.text }}</text>
  11. </view>
  12. </view>
  13. <view class="safeZone" :style="{ 'height': safeAreaInsetsBottom }"></view>
  14. </view>
  15. <view :style="{ 'height': tabbarHeight }"></view>
  16. </template>

3.2 html 核心代码:

:style="`--repeat: ${tabbar.length}`" tabbar的数量,根据数组的长度改变。

:style="{ 'height': safeAreaInsetsBottom }" :底部安全区域的高度。

:style="{ 'height': tabbarHeight }":tabbar导航栏的高度,防止内容被遮挡。


3.3 css 代码部分:

  1. <style lang="scss" scoped>
  2. .tabbar {
  3. --width: 100rpx;
  4. --height: 100rpx;
  5. --size: 60rpx;
  6. --repeat: 4;
  7. --color: #FC536E;
  8. --radius: 20rpx;
  9. --rgba: rgba(255, 255, 255, 0.95);
  10. position: fixed;
  11. width: 100%;
  12. bottom: 0;
  13. z-index: 999;
  14. .tabbar-nav {
  15. display: grid;
  16. grid-template-columns: repeat(var(--repeat), 1fr);
  17. .tabbar-nav-a {
  18. display: flex;
  19. flex-direction: column;
  20. align-items: center;
  21. justify-content: flex-end;
  22. padding: 4rpx 0;
  23. position: relative;
  24. height: var(--height);
  25. background-color: var(--rgba);
  26. box-sizing: border-box;
  27. filter: drop-shadow(2rpx -2rpx 2rpx rgba(0, 0, 0, 0.1));
  28. font-size: 24rpx;
  29. &:nth-child(1) {
  30. border-top-left-radius: var(--radius);
  31. }
  32. &:nth-last-child(1) {
  33. border-top-right-radius: var(--radius);
  34. }
  35. }
  36. .after {
  37. background: radial-gradient(circle at center top, transparent var(--size), var(--rgba) 0)center top;
  38. .after-icon {
  39. width: var(--width);
  40. height: var(--height);
  41. background-color: var(--color);
  42. border-radius: 50%;
  43. position: absolute;
  44. left: 50%;
  45. top: 0;
  46. transform: translate(-50%, -50%);
  47. display: flex;
  48. align-items: center;
  49. justify-content: center;
  50. }
  51. text {
  52. color: var(--color);
  53. }
  54. }
  55. }
  56. .safeZone {
  57. background-color: var(--rgba);
  58. }
  59. }
  60. </style>

3.4 css 核心代码:

background: radial-gradient(circle at center top, transparent var(--size), var(--rgba) 0)center top;凹凸透明显示,最主要就是靠这行样式代码。

--repeat: 4;:控制 tabbar 的数量


3.5 vue2 代码编写:

  1. <script>
  2. export default {
  3. name: "Tabbar",
  4. props: {
  5. pageIndex: {
  6. type: Number,
  7. default: 0,
  8. }
  9. },
  10. data() {
  11. return {
  12. tabbarHeight: '',
  13. safeAreaInsetsBottom: '',
  14. tabbar: [{
  15. iconPath: "heart",
  16. selectedIconPath: "heart-filled",
  17. text: "爱吧",
  18. pagePath: "/pages/index/index"
  19. },
  20. {
  21. iconPath: "wallet",
  22. selectedIconPath: "wallet-filled",
  23. text: "记账",
  24. pagePath: "/pages/bookkeeping/bookkeeping"
  25. },
  26. {
  27. iconPath: "chat",
  28. selectedIconPath: "chat-filled",
  29. text: "社区",
  30. pagePath: "/pages/community/community"
  31. },
  32. {
  33. iconPath: "person",
  34. selectedIconPath: "person-filled",
  35. text: "我",
  36. pagePath: "/pages/my/my"
  37. }]
  38. }
  39. },
  40. created() {
  41. uni.hideTabBar({ animation: true })
  42. let res = uni.getWindowInfo().safeAreaInsets.bottom + 50
  43. this.safeAreaInsetsBottom = res + "rpx";
  44. this.tabbarHeight = res * 2 + "rpx"
  45. },
  46. methods: {
  47. tapTabbar(val, index) {
  48. uni.switchTab({
  49. url: val.pagePath
  50. })
  51. }
  52. }
  53. }
  54. </script>

3.6 vue3 代码编写:

  1. <script setup>
  2. defineOptions({
  3. name: "Tabbar"
  4. });
  5. const tabbarHeight = ref('')
  6. const safeAreaInsetsBottom = ref(0)
  7. const props = defineProps({
  8. pageIndex: {
  9. type: Number,
  10. default: 0
  11. }
  12. });
  13. const tabbar = reactive([{
  14. iconPath: "heart",
  15. selectedIconPath: "heart-filled",
  16. text: "爱吧",
  17. pagePath: "/pages/index/index"
  18. },
  19. {
  20. iconPath: "wallet",
  21. selectedIconPath: "wallet-filled",
  22. text: "记账",
  23. pagePath: "/pages/bookkeeping/bookkeeping"
  24. },
  25. {
  26. iconPath: "chat",
  27. selectedIconPath: "chat-filled",
  28. text: "社区",
  29. pagePath: "/pages/community/community"
  30. },
  31. {
  32. iconPath: "person",
  33. selectedIconPath: "person-filled",
  34. text: "我",
  35. pagePath: "/pages/my/my"
  36. }
  37. ])
  38. onMounted(() => {
  39. uni.hideTabBar({ animation: true })
  40. let res = uni.getWindowInfo().safeAreaInsets.bottom
  41. safeAreaInsetsBottom.value = res * 2 + "rpx";
  42. tabbarHeight.value = res + 50 * 2 + "rpx";
  43. });
  44. const tapTabbar = (val, index) => {
  45. uni.switchTab({
  46. url: val.pagePath
  47. })
  48. }
  49. </script>

3.7 vue2/3 核心代码:

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单位。


3.8 Tabbar 组件的使用:

<Tabbar :pageIndex="0"></Tabbar>

:pageIndex="0":用于控制激活状态,高亮显示。


4. vue2 完整代码

  1. <template>
  2. <view class="tabbar" :style="`--repeat: ${tabbar.length}`">
  3. <view class="tabbar-nav">
  4. <view class="tabbar-nav-a" v-for="(item, index) in tabbar" :key="index"
  5. :class="pageIndex == index ? 'after' : ''"
  6. @tap="tapTabbar(item, index)">
  7. <view class="after-icon" v-if="pageIndex == index">
  8. <uni-icons size="60rpx" color="#fff"
  9. :type="item.selectedIconPath" ></uni-icons>
  10. </view>
  11. <uni-icons :type="item.iconPath" size="48rpx" v-else></uni-icons>
  12. <text class="text">{{ item.text }}</text>
  13. </view>
  14. </view>
  15. <view class="safeZone" :style="{ 'height': safeAreaInsetsBottom }"></view>
  16. </view>
  17. <view :style="{ 'height': tabbarHeight }"></view>
  18. </template>
  19. <script>
  20. export default {
  21. name: "Tabbar",
  22. props: {
  23. pageIndex: {
  24. type: Number,
  25. default: 0,
  26. }
  27. },
  28. data() {
  29. return {
  30. tabbarHeight: '',
  31. safeAreaInsetsBottom: '',
  32. tabbar: [{
  33. iconPath: "heart",
  34. selectedIconPath: "heart-filled",
  35. text: "爱吧",
  36. pagePath: "/pages/index/index"
  37. },
  38. {
  39. iconPath: "wallet",
  40. selectedIconPath: "wallet-filled",
  41. text: "记账",
  42. pagePath: "/pages/bookkeeping/bookkeeping"
  43. },
  44. {
  45. iconPath: "chat",
  46. selectedIconPath: "chat-filled",
  47. text: "社区",
  48. pagePath: "/pages/community/community"
  49. },
  50. {
  51. iconPath: "person",
  52. selectedIconPath: "person-filled",
  53. text: "我",
  54. pagePath: "/pages/my/my"
  55. }]
  56. }
  57. },
  58. created() {
  59. uni.hideTabBar({ animation: true })
  60. let res = uni.getWindowInfo().safeAreaInsets.bottom
  61. this.safeAreaInsetsBottom = res * 2 + "rpx";
  62. this.tabbarHeight = res + 50 * 2 + "rpx"
  63. },
  64. methods: {
  65. tapTabbar(val, index) {
  66. uni.switchTab({
  67. url: val.pagePath
  68. })
  69. }
  70. }
  71. }
  72. </script>
  73. <style lang="scss" scoped>
  74. .tabbar {
  75. --width: 100rpx;
  76. --height: 100rpx;
  77. --size: 60rpx;
  78. --repeat: 4;
  79. --color: #FC536E;
  80. --radius: 20rpx;
  81. --rgba: rgba(255, 255, 255, 0.95);
  82. position: fixed;
  83. width: 100%;
  84. bottom: 0;
  85. z-index: 999;
  86. .tabbar-nav {
  87. display: grid;
  88. grid-template-columns: repeat(var(--repeat), 1fr);
  89. .tabbar-nav-a {
  90. display: flex;
  91. flex-direction: column;
  92. align-items: center;
  93. justify-content: flex-end;
  94. padding: 4rpx 0;
  95. position: relative;
  96. height: var(--height);
  97. background-color: var(--rgba);
  98. box-sizing: border-box;
  99. filter: drop-shadow(2rpx -2rpx 2rpx rgba(0, 0, 0, 0.1));
  100. font-size: 24rpx;
  101. &:nth-child(1) {
  102. border-top-left-radius: var(--radius);
  103. }
  104. &:nth-last-child(1) {
  105. border-top-right-radius: var(--radius);
  106. }
  107. }
  108. .after {
  109. background: radial-gradient(circle at center top,
  110. transparent var(--size), var(--rgba) 0)center top;
  111. .after-icon {
  112. width: var(--width);
  113. height: var(--height);
  114. background-color: var(--color);
  115. border-radius: 50%;
  116. position: absolute;
  117. left: 50%;
  118. top: 0;
  119. transform: translate(-50%, -50%);
  120. display: flex;
  121. align-items: center;
  122. justify-content: center;
  123. }
  124. text {
  125. color: var(--color);
  126. }
  127. }
  128. }
  129. .safeZone {
  130. background-color: var(--rgba);
  131. }
  132. }
  133. </style>

5. vue3 完整代码

  1. <template>
  2. <view class="tabbar" :style="`--repeat: ${tabbar.length}`">
  3. <view class="tabbar-nav">
  4. <view class="tabbar-nav-a" v-for="(item, index) in tabbar" :key="index"
  5. :class="pageIndex == index ? 'after' : ''"
  6. @tap="tapTabbar(item, index)">
  7. <view class="after-icon" v-if="pageIndex == index">
  8. <uni-icons size="60rpx" color="#fff"
  9. :type="item.selectedIconPath">
  10. </uni-icons>
  11. </view>
  12. <uni-icons :type="item.iconPath" size="48rpx" v-else></uni-icons>
  13. <text class="text">{{ item.text }}</text>
  14. </view>
  15. </view>
  16. <view class="safeZone" :style="{ 'height': safeAreaInsetsBottom }"></view>
  17. </view>
  18. <view :style="{ 'height': tabbarHeight }"></view>
  19. </template>
  20. <script setup>
  21. defineOptions({
  22. name: "Tabbar"
  23. });
  24. const tabbarHeight = ref('')
  25. const safeAreaInsetsBottom = ref(0)
  26. const props = defineProps({
  27. pageIndex: {
  28. type: Number,
  29. default: 0
  30. }
  31. });
  32. const tabbar = reactive([{
  33. iconPath: "heart",
  34. selectedIconPath: "heart-filled",
  35. text: "爱吧",
  36. pagePath: "/pages/index/index"
  37. },
  38. {
  39. iconPath: "wallet",
  40. selectedIconPath: "wallet-filled",
  41. text: "记账",
  42. pagePath: "/pages/bookkeeping/bookkeeping"
  43. },
  44. {
  45. iconPath: "chat",
  46. selectedIconPath: "chat-filled",
  47. text: "社区",
  48. pagePath: "/pages/community/community"
  49. },
  50. {
  51. iconPath: "person",
  52. selectedIconPath: "person-filled",
  53. text: "我",
  54. pagePath: "/pages/my/my"
  55. }
  56. ])
  57. onMounted(() => {
  58. uni.hideTabBar({ animation: true })
  59. let res = uni.getWindowInfo().safeAreaInsets.bottom
  60. safeAreaInsetsBottom.value = res * 2 + "rpx";
  61. tabbarHeight.value = res + 50 * 2 + "rpx";
  62. });
  63. const tapTabbar = (val, index) => {
  64. uni.switchTab({
  65. url: val.pagePath
  66. })
  67. }
  68. </script>
  69. <style lang="scss" scoped>
  70. .tabbar {
  71. --width: 100rpx;
  72. --height: 100rpx;
  73. --size: 60rpx;
  74. --repeat: 4;
  75. --color: #FC536E;
  76. --radius: 20rpx;
  77. --rgba: rgba(255, 255, 255, 0.95);
  78. position: fixed;
  79. width: 100%;
  80. bottom: 0;
  81. z-index: 999;
  82. .tabbar-nav {
  83. display: grid;
  84. grid-template-columns: repeat(var(--repeat), 1fr);
  85. .tabbar-nav-a {
  86. display: flex;
  87. flex-direction: column;
  88. align-items: center;
  89. justify-content: flex-end;
  90. padding: 4rpx 0;
  91. position: relative;
  92. height: var(--height);
  93. background-color: var(--rgba);
  94. box-sizing: border-box;
  95. filter: drop-shadow(2rpx -2rpx 2rpx rgba(0, 0, 0, 0.1));
  96. font-size: 24rpx;
  97. &:nth-child(1) {
  98. border-top-left-radius: var(--radius);
  99. }
  100. &:nth-last-child(1) {
  101. border-top-right-radius: var(--radius);
  102. }
  103. }
  104. .after {
  105. background: radial-gradient(circle at center top,
  106. transparent var(--size),
  107. var(--rgba) 0)center top;
  108. .after-icon {
  109. width: var(--width);
  110. height: var(--height);
  111. background-color: var(--color);
  112. border-radius: 50%;
  113. position: absolute;
  114. left: 50%;
  115. top: 0;
  116. transform: translate(-50%, -50%);
  117. display: flex;
  118. align-items: center;
  119. justify-content: center;
  120. }
  121. text {
  122. color: var(--color);
  123. }
  124. }
  125. }
  126. .safeZone {
  127. background-color: var(--rgba);
  128. }
  129. }
  130. </style>

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/970693
推荐阅读
相关标签
  

闽ICP备14008679号