Scroll Transition Tabbar - 动画标签栏
一个基于滚动位置的动画标签栏组件。
预览
使用示例
import { AnimationView } from '@/components/animation';
import { cn } from '@/utils';
import { useSpring } from '@react-spring/web';
import { View } from '@tarojs/components';
import { getWindowInfo, usePageScroll } from '@tarojs/taro';
import { useState } from 'react';
definePageConfig({
navigationBarBackgroundColor: '#000000',
navigationBarTextStyle: 'white',
navigationBarTitleText: 'Animation tabbar',
});
const DATA = Array.from({ length: 100 }).map((_, index) => ({
id: index,
color: `#${Math.floor(Math.random() * 16777215).toString(16)}`,
}));
const optionsData: AnimationTabbarProps['options'] = [
{
icon: 'icon-[lucide--house]',
value: 'home',
},
{
icon: 'icon-[lucide--search]',
value: 'search',
},
{
icon: 'icon-[lucide--heart]',
value: 'favorite',
},
{
icon: 'icon-[lucide--user-round]',
value: 'user',
},
{
icon: 'icon-[lucide--settings]',
value: 'settings',
},
];
export default function Page() {
return (
<View className="grid grid-cols-4 gap-2.5 bg-black p-4">
{DATA.map(({ id, color }) => {
return (
<View
key={id}
className="col-span-1 flex h-16 items-center justify-center rounded"
style={{
backgroundColor: color,
}}
></View>
);
})}
<AnimationTabbar options={optionsData} activeValue="home" />
</View>
);
}
下载
1
添加依赖
npm i @react-spring/web clsx tailwind-merge
2
添加工具函数
// @/utils
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
// 合并 Tailwind CSS 类名和 clsx 类名
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// 根据屏幕宽度计算响应式值
export const pt = (num: number) => {
const { screenWidth } = getWindowInfo();
return num * (screenWidth / 375);
};
3
添加动画组件
// @/components/animation
import { animated } from '@react-spring/web';
import { Label, Text, View } from '@tarojs/components';
export const AnimationView = animated(View);
export const AnimationText = animated(Text);
export const AnimationLabel = animated(Label);
源代码
import { AnimationView } from '@/components/animation';
import { cn } from '@/utils';
import { useSpring } from '@react-spring/web';
import { View } from '@tarojs/components';
import { getWindowInfo, usePageScroll } from '@tarojs/taro';
import { useState } from 'react';
interface AnimationTabbarProps {
options: {
icon: string;
value: string;
}[];
activeValue?: string;
onChange?: (value: string) => void;
}
const AnimationTabbar = ({ options, activeValue, onChange }: AnimationTabbarProps) => {
const { safeArea, screenHeight, windowHeight, statusBarHeight } = getWindowInfo();
const safeButtom = screenHeight - (safeArea?.bottom ?? 0);
const [isScroll, setIsScroll] = useState(false);
usePageScroll(e => {
if (e.scrollTop === 0) {
setIsScroll(false);
}
if (!isScroll && e.scrollTop > 100) {
setIsScroll(true);
}
});
const fromStyle = { paddingBottom: safeButtom, bottom: 0, insetX: 0, borderRadius: '16px 16px 0px 0px' };
const toStyle = { paddingBottom: 0, bottom: safeButtom + 10, insetX: 16, borderRadius: '25px 25px 25px 25px' };
const { insetX, ...style } = useSpring({
from: fromStyle,
to: isScroll ? toStyle : fromStyle,
});
return (
<AnimationView
style={{ ...style, left: insetX, right: insetX }}
className="fixed overflow-hidden border border-gray-400 bg-black/40 backdrop-blur-md"
>
{/* 可以替换以下内容,传递子组件,完全自定义 */}
<View className="flex items-center p-4">
{options.map(({ icon, value }) => {
return (
<View key={value} className="flex flex-1 items-center justify-center">
<View
className={cn('size-6 text-gray-400', icon, activeValue === value && 'text-white')}
onClick={() => onChange?.(value)}
></View>
</View>
);
})}
</View>
</AnimationView>
);
};