图标使用阿里矢量图标
<template>
<view class="content-box">
<view class="tab-card">
<view :class="['item', current == index ? 'active' : '']" v-for="(item, index) in tabbar.list" @click="tabbarChange(item.pagePath, index)">
<view :style="{ '--icon-color': tabbar.iconColor, '--selected-icon-color': tabbar.selectedIconColor }" class="icon iconfont" :class="index == current ? item.selectedIconPath : item.iconPath"></view>
<view class="tab-text">{{ item.text }}</view>
</view>
<view class="slidingBlock" :style="slidingBlockStyle"> </view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, reactive, inject, onMounted, getCurrentInstance, defineProps, defineEmits } from 'vue';
import { getdeviceInfo } from '@/utils/deviceInfo';
const imgUrl = inject('imgUrl');
const { globalData } = reactive(getdeviceInfo());
const emit = defineEmits(['upDataTab']);
const current: any = ref(0);
const slidingBlockStyle: any = ref({});
const instance: any = getCurrentInstance(); // 获取组件实例
const tabbar: any = reactive({
color: '#ccc', // 未选中字体颜色
selectedColor: '#4468ff', // 选择字体颜色
iconColor: '#fff', // 图标默认颜色
selectedIconColor: '#1c1c1c', // 选中图标颜色
backgroundColor: '#fff', // 背景颜色
selectedBackground: '#fff',
list: [
{
text: '首页', // 底部文字
iconPath: 'icon-home', // 未选择icon图标
selectedIconPath: 'icon-home', // 选中icon图标
pagePath: '/pages/tabBar/index', //
},
{
text: '客户',
iconPath: 'icon-client',
selectedIconPath: 'icon-client',
pagePath: '/pages/tabBar/classify',
},
{
text: '我的',
iconPath: 'icon-my',
selectedIconPath: 'icon-my',
pagePath: '/pages/tabBar/my',
},
],
});
onMounted(() => {
getClientRect(0);
});
function tabbarChange(path: any, ind: any) {
console.log(path, ind);
if (ind == current.value) return;
current.value = ind;
// this.$emit('change', path);
emit('upDataTab', path);
getClientRect(ind);
}
function getClientRect(ind: any) {
uni.createSelectorQuery()
.in(instance.proxy)
// 获取所有标签的位置信息
.selectAll('.item')
.boundingClientRect((rects: any) => {
if (!rects || rects.length === 0) return; // 安全判断
// 获取当前选中的标签位置
const targetRect = rects[ind];
if (!targetRect) return;
// 获取父容器(tabs-container)的位置信息
const containerRect = rects[0].top !== undefined ? rects[0] : { left: 0 }; // 如果无法获取到父容器的具体位置,则默认为0
// 计算目标标签相对于父容器左侧的距离占整个视口宽度的比例
const viewportWidth = getdeviceInfo().windowWidth;
const relativeLeft = ((targetRect.left - containerRect.left) / viewportWidth) * 100;
slidingBlockStyle.value = {
width: `${targetRect.width}px`,
height: `${targetRect.height}px`,
transform: `translateX(calc(${relativeLeft}vw + 13rpx))`, // 使用vw单位表示相对视口宽度的百分比
};
})
// 确保执行查询
.exec();
}
defineExpose({
tabbarChange,
getClientRect,
});
</script>
<style lang="scss" scoped>
page {
background-color: #f1f1f1;
font-size: 32rpx;
}
.tab-card {
position: fixed;
bottom: 0;
left: 0;
right: 0;
width: 400rpx;
// background-color: #1c1c1c;
border-radius: 100rpx;
display: flex;
justify-content: space-between;
align-items: center;
// padding: 16rpx;
padding: 8rpx 13rpx;
margin: auto;
// box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
.item {
// transform: all 1s;
display: flex;
align-items: center;
border-radius: 100rpx;
padding: 10rpx 32rpx;
height: 100%;
align-items: center;
// background: rebeccapurple;
> view {
color: #000;
}
.icon {
// color: var(--icon-color);
color: #000;
font-size: 13rpx;
}
}
.active .icon {
// color: var(--selected-icon-color);
color: #fff;
// transition: color 0s ease 0.5s;
transition: all 0.5s;
}
.tab-text {
margin-left: 5rpx;
font-size: 10rpx;
}
.active .tab-text {
color: #fff;
transition: all 0.5s;
}
.slidingBlock {
// background-color: #fff;
background: linear-gradient(90deg, #ff6d3d 25%, #ff413f 75%);
border-radius: 100rpx;
position: absolute;
left: 0;
transition: all 0.5s;
z-index: -1;
}
}
</style>
使用方式
<template>
<view>
<view class="container">
<home v-if="current == '/pages/tabBar/index'" />
<classify v-if="current == '/pages/tabBar/classify'" />
<user v-if="current == '/pages/tabBar/my'" />
</view>
<!-- 组件 -->
<my-tab-bar ref="mytabbar" @upDataTab="upDataTab"></my-tab-bar>
</view>
</template>
<script setup lang="ts">
import { onShow } from '@dcloudio/uni-app';
import { ref, inject, reactive } from 'vue';
import home from '@/pages/tabBar/index.vue';
import classify from '@/pages/tabBar/classify.vue';
import user from '@/pages/tabBar/my.vue';
import myTabBar from '@/components/myTabBar.vue';
const imgUrl = inject('imgUrl');
const mytabbar: any = ref();
const current: any = ref('/pages/tabBar/index');
function upDataTab(params: any) {
current.value = params;
}
</script>
<style lang="scss" scoped>
.container {
width: 100%;
height: 100vh;
background: linear-gradient(90deg, #f9cdbd, #fde7d9);
}
</style>