More Button Animation - 更多按钮动画

一个基于 CSS Transform 实现的更多按钮动画组件,支持自定义图标和弹出动画效果。

预览

使用示例

import { Text, View } from '@tarojs/components';

export default function Page() {
  return (
    <View className="flex h-screen flex-col items-center justify-center bg-[#F5F5F5]">
      <MoreButton>
        <View className="icon-[lucide--alarm-clock-check] size-5"></View>
        <View className="icon-[lucide--aperture] size-5"></View>
        <View className="icon-[lucide--apple] size-5"></View>
        <View className="icon-[lucide--angry] size-5"></View>
      </MoreButton>
    </View>
  );
}

下载

1

添加依赖

npm i clsx tailwind-merge
2

添加工具函数

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);
};

源代码

import { cn } from '@/utils';
import { Text, View } from '@tarojs/components';
import { CSSProperties, PropsWithChildren, useState } from 'react';

const MoreButton = ({ children, size }: PropsWithChildren<{ size?: number }>) => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <View className="relative" style={{ '--size': `${size ?? 96}rpx` } as CSSProperties}>
      <View
        id="more-button-animation"
        className={cn(
          'relative z-10 box-border flex size-[var(--size)] items-center justify-center rounded-full transition-transform duration-300 ease-spring-bounce',

          isOpen ? '-rotate-45 bg-gray-300' : 'bg-yellow-300'
        )}
        onClick={() => setIsOpen(state => !state)}
      >
        <Text className={cn('icon-[lucide--plus]')}></Text>
      </View>
      <View
        className={cn(
          'absolute bottom-[calc(var(--size)/2)] left-1/2 z-0 box-border flex w-4/5 -translate-x-1/2 flex-col items-center space-y-1.5 rounded-full bg-white pb-[calc(var(--size)/2+var(--size)/4)] pt-[calc(var(--size)/4)] transition-all duration-500 ease-spring-bounce',
          isOpen ? 'translate-y-0 opacity-100' : 'translate-y-[calc(var(--size)/2)] opacity-0'
        )}
      >
        {children}
      </View>
    </View>
  );
};