前情
不知道该说是公司对产品要求稿,还是公司喜欢作,最近接手公司的一个全新跨端(快抖微支+app)的项目,项目还没有上线就已经改了4版了,改改UI,换换皮也就算了,关键是流程也是在一直修改,最近就接到上层说这小程序UI太丑了,要重新出UI,说现在的UI只能打60分,希望UI能多玩出点花样来,于是就接到现在的需求了,UI希望实现展形Tabbar,根据不同页面主题色需要切换Tabbar的背景,还希望默认高亮选中的是Tabbar项的中间项,而且还希望实现一些icon切换动效,里面任何一条想实现都只能靠自定义 Tabbar来做了
心里虽有排斥,排斥的是修改太频率了,每次都是刚刚调通又来大调整,心态上多少有点浮动,但这就是工作吧,互相配合才能打造老板喜欢的好的产品,用户喜欢不喜欢只能靠市场验证了,我也就偷偷骂了二句娘就接着开发这一个需求了
自定义Tabbar
异常Tabbar,对于我来说不是什么大问题,因为我已经在插件市场分享了一个自定义Tabbar的组件,就是为了能快速应对这种需求,我发布在插件市场的组件地址是:https://ext.dcloud.net.cn/plugin?id=21094
我实现异形组件的关键代码如下
这是Tabbar的配置数据:
jsx
...
tabbar: {
color: '#8D8E91',
selectedColor: '#000000',
borderStyle: 'white',
backgroundColor: 'transparent',
tabbarHeight: 198,
holderHeight: 198,
iconStyle: { width: '44rpx', height: '44rpx' },
activeIconStyle: { width: '44rpx', height: '44rpx' },
textStyle: { fontSize: '24rpx' },
activeTextStyle: { fontSize: '24rpx' },
list: [
{
pagePath: '/pages/discover/discover',
iconPath: '/static/tabbarnew/fx.png',
selectedIconPath: '/static/tabbarnew/fxactive.png',
text: '发现',
key: 'discover',
},
{
pagePath: '/pages/games/games',
iconPath: '/static/tabbarnew/yx.png',
selectedIconPath: '/static/tabbarnew/yxactive.png',
text: '游戏',
key: 'games',
},
{
pagePath: '/pages/index/index',
iconPath: 'https://cdn.dianbayun.com/static/tabs/xwz.gif',
selectedIconPath: 'https://cdn.dianbayun.com/static/tabs/xwzactive.gif',
text: '新物种',
key: 'index',
},
{
pagePath: '/pages/product/product',
iconPath: '/static/tabbarnew/sc.png',
selectedIconPath: '/static/tabbarnew/scactive.png',
text: '商城',
key: 'product',
},
{
pagePath: '/pages/my/my',
iconPath: '/static/tabbarnew/wd.png',
selectedIconPath: '/static/tabbarnew/wdactive.png',
text: '我的',
key: 'my',
},
],
}
...
下面是导航栏组件的关键结构和一些为了实现icon切换动效的css:
jsx
<!-- CustomTabBar 组件关键代码 -->
<hbxw-tabbar
:config="globalInstance.tabbar"
:active-key="activeKey"
:tabbar-style="{ backgroundImage: bgType === 'black' ? 'url(\'黑色背景\')' : 'url(\'白色背景\')', backgroundSize: '100% auto' }"
>
<template #default="{ item, isActive, color, selectColor, iconStyleIn, activeIconStyleIn, textStyleIn, activeTextStyleIn }">
<view
class="w-full flex flex-col items-center justify-center h-[134rpx] relative"
v-if="item.key !== 'index'"
>
<view class="w-[44rpx] h-[44rpx] relative" :class="{'active': isActive}">
<image
class="w-[44rpx] h-[44rpx] absolute top-0 left-0 normal-img"
:src="item.iconPath"
:style="iconStyleIn"
/>
<image
class="w-[44rpx] h-[44rpx] absolute top-0 left-0 active-img"
:src="item.selectedIconPath"
:style="activeIconStyleIn"
/>
</view>
<text
class="text-[24rpx]"
:style="{ color: !isActive ? color : selectColor, ...(isActive ? activeTextStyleIn : textStyleIn) }"
>
{{ item.text }}
</text>
</view>
<view
class="w-full flex flex-col items-center justify-center h-[134rpx] relative"
v-else
>
<view class="w-[103rpx] h-[103rpx] relative" :class="{'active': isActive}">
<image
class="w-[103rpx] h-[103rpx] absolute top-0 left-0 normal-img"
:src="item.iconPath"
/>
<image
class="w-[103rpx] h-[103rpx] absolute top-0 left-0 active-img"
:src="item.selectedIconPath"
/>
</view>
</view>
</template>
</hbxw-tabbar>
</template>
// 这个是为了实现icon动添加的css
<style lang="scss" scoped>
@keyframes normalimg {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(.3);
}
}
@keyframes activeimg {
0% {
opacity: 0;
transform: scale(.3);
}
100% {
opacity: 1;
transform: scale(1);
}
}
.active-img{
opacity: 0;
transform: scale(.3);
}
.normal-img{
opacity: 1;
transform: scale(1);
}
.active {
.normal-img{
animation: normalimg 0.4s ease-in-out 0s forwards;
}
.active-img{
animation: activeimg 0.4s ease-in-out 0.4s forwards;
}
}
</style>
注:当前项目我使用了Tainwind CSS原子化CSS框架来书写样式
在页面上使用的代码如下:
jsx
<!-- 这是首页的页面Tabbar 高亮index项,同时背景用黑色 -->
<CustomTabBar activeKey="index" bgType="black" />
其实原理很简单,因为我的发布在应用市场的组件有提供 slot,你可以自由定义Tabbar的每一项的结构样式,我这里的做法就是中间项单独一块结构来实现异形效果,实现后的效果如下:

坑位?
展开tabbar效果是好实现的,但是在实现Tabbar切换icon动效的时候,我遇到了麻烦,小程序虽然有提供专门用于做动画的API,但是我个人不太喜欢用,我比较喜欢使用css3实现动画,使用上更简单的同时,动画流畅度也优于JS来做
因为是切换动效,首先想到的就是通过transition来实现,通过给父组添加一个active的类名控制下面icon的来实现切换动效,这是实现状态变化动效的首选,但是发现完全没有用,一度怀疑是不是小程序不支持transition,于是想到换方案,我通过aniamtion来实现动效,确实是有效果的,但是只有首次切换tabbar的时候有效果
Why?
我一开始是怀疑是不是小程序对于css3动画有兼容性问题,或者是支付宝不支持动效,因为我此时正在开发的就是支付宝端,也去小程序论坛逛了逛 ,确实有一些帖子说到transition在小程序上兼容问题,也问了AI,AI也说是有,而且不现标签组件可能支持的transition还不一样,此时我陷入了怀疑,难道真的要靠JS来实现么,但是以我的个人开发经验,我不止在一个小程序项目中使用过css3来实现动效,都是没有问题的,在经过一段时间的思考我,我突然意识到一个问题,动画没出现真的不是兼容性的问题,而是没有动效或者只有首次有这根本就是正常现象
transition没有是因为当你切换tabbar的时候整个组件是重新渲染的,对于初次渲染的元素你是没法使用transition的,至于为什么后面点也都没有,是我在尝试 animation的时候发现它只有首次点击切换的时候才有我才突然意识到,因为这是tabbar啊,小程序是有会缓存的,你显示一次后,小程序页面会运行在后台,你再次切换的时候只是激活而已,根本不会有样式的变化
解决方案
既然Tabbar切换页在不会重新从0渲染,只是显示与隐藏而已,那我们就手动的让它来实现Tabbar的高亮样式切换即可,虽然Tabbar切换页面不会重新渲染,但是它会触发二个小程序的生命钩子onShow/onHide,那我们就从这二处着手,因为是多个页面要复用,我此处抽了hooks,关键代码如下:
jsx
import { onShow, onLoad, onHide } from '@dcloudio/uni-app'
import { ref } from 'vue'
export const usePageTabbar = (type) => {
const activeType = ref('')
onLoad(() => {
uni.hideTabBar()
activeType.value = type
})
onShow(() => {
activeType.value = type
uni.hideTabBar()
})
onHide(() => {
activeType.value = ''
uni.showTabBar()
})
return {
activeType
}
}
页面上使用也做了调整,关键代码如下:
jsx
<script setup>
...
import { usePageTabbar } from '@/hooks/pagesTabbar'
const { activeType } = usePageTabbar('index')
...
</script>
<template>
...
<!-- 页面tabbar -->
<CustomTabBar :activeKey="activeType" />
...
</template>
至此完成了这一次的 tabbar大改造,实现的效果如下:

其实此时再切换回用transition去做动画,这也是可以的,只是我后面已经用 animaltion实现了就懒得改它了
思考
对于做开么的我们,平时抽取一些可以复用的组件并分享真的是值得做的一件事,它可以在很多时候帮你提高开发速度,同时也减少了你反复的写一些重复代码
对于需求调整这是很多开发都不喜欢的事,因为当项目需求调整的过多,原来已经快接近屎山的代码更加加还变成屎山,但是这个对于一些小公司开发流程不是特别规范的需求调整是不可避免的,我们无需过多烦恼,只要项目进度允许,他们要调就让他调吧,相信大家都是为了打造一款精品应用在使劲而已,何乐而不为了
个人的能力和认识都有限,对于一个问题的解决方案有很多种,我上面的实现方案并不一定最好的,如果你有更好的解决方案,欢迎不吝分享,一起学习一起进步