📅 我们继续 50 个小项目挑战!—— Expanding Cards 组件

仓库地址:https://gitee.com/hhm-hhm/50days50projects.git


✨ 组件目标

  • 一组横向排列的背景卡片
  • 点击卡片后,其展开至主要区域,其他卡片收缩
  • 显示当前卡片的标题,其他隐藏

🧱 技术实现点

  • React状态 useState 
  • map方法 渲染卡片列表
  • TailwindCSS flex, transition, bg-cover, rounded, bg-[url] 等核心工具类
  • 条件样式绑定 className

🔧 ExpandingCards.tsx 组件实现

import { useState } from 'react';

interface ImageItem {
  id: number;
  imageUrl: string; // 改为纯字符串 URL
  title: string;
}

const imageList: ImageItem[] = [
  {
    id: 1,
    imageUrl:
      'https://images.unsplash.com/photo-1558979158-65a1eaa08691?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
    title: 'Explore The World',
  },
  {
    id: 2,
    imageUrl:
      'https://images.unsplash.com/photo-1572276596237-5db2c3e16c5d?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
    title: 'Wild Forest',
  },
  {
    id: 3,
    imageUrl:
      'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-1.2.1&auto=format&fit=crop&w=1353&q=80',
    title: 'Sunny Beach',
  },
  {
    id: 4,
    imageUrl:
      'https://images.unsplash.com/photo-1551009175-8a68da93d5f9?ixlib=rb-1.2.1&auto=format&fit=crop&w=1351&q=80',
    title: 'City on Winter',
  },
  {
    id: 5,
    imageUrl:
      'https://images.unsplash.com/photo-1549880338-65ddcdfd017b?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80',
    title: 'Mountains - Clouds',
  },
];

const ExpandingCards = () => {
  const [current, setCurrent] = useState<number>(1);

  return (
    <div className="flex min-h-screen justify-center p-20">
      {imageList.map((item) => (
        <div
          key={item.id}
          onClick={() => setCurrent(item.id)}
          className={`
            relative m-2 flex-1/12 cursor-pointer rounded-[4vw]
            font-mono text-2xl text-white transition-all duration-500 ease-in-out
            ${current === item.id ? 'flex-2/3' : 'flex-1/12'}
            aspect-video
          `}
          style={{
            backgroundImage: `url(${item.imageUrl})`,
            backgroundSize: 'cover',
            backgroundPosition: 'center',
            backgroundRepeat: 'no-repeat',
          }}
        >
          <h3
            className={`
              absolute bottom-5 left-5 transition-all duration-500 ease-in-out
              ${current === item.id ? 'opacity-100' : 'opacity-0'}
            `}
          >
            {item.title}
          </h3>
        </div>
      ))}
    </div>
  );
}
export default ExpandingCards;

💡 TailwindCSS 样式重点讲解

TailwindCSS 样式重点讲解
类名功能描述
flex-1/12flex-2/3自定义比例控制卡片展开宽度
bg-cover bg-center背景图片尺寸与居中展示
rounded-[4vw]圆角自定义为视口宽度
transition-all duration-500平滑过渡效果
opacity-0 / opacity-100控制标题显隐


🦌 常量定义 + 组件路由

constants/index.tsx添加组件预览常量

export interface NavItem {
  id: number;
  title: string;
  link: string; // 如 '/', '#projects', '/about' 等
}
export const navList: NavItem[] = [
    { id: 1, title: 'Home', link: '#Home' },
    { id: 2, title: 'Peoject', link: '#Projects' },
    { id: 3, title: 'About', link: '#About' },
]

interface ProjectItem {
  id: number | string;
  title: string;
  image: string; 
  link: string; 
}
export const projectList: ProjectItem[] = [
   {
        id: 1,
        title: 'Expanding Cards',
        image: 'https://50projects50days.com/img/projects-img/1-expanding-cards.png',
        link: 'ExpandingCards',
    },
    
]

router/index.tsxchildren数组中添加子路由

// src/router/index.tsx
import { createBrowserRouter } from 'react-router-dom';
import type { RouteObject } from 'react-router-dom';
import App from '@/App';
const routes: RouteObject[] = [
  {
    path: '/',
    element: <App />,
    children: [
       {
        path: '/ExpandingCards',
        lazy: () => import( '@/projects/ExpandingCards.tsx').then(mod => ({ Component: mod.default })),
      },
    ],
  },
  {
    path: '/about',
     lazy: () => import('@/pages/AboutPage').then(mod => ({ Component: mod.default })),
  },
  {
    path: '/test',
    lazy: () => import('@/pages/TestPage').then(mod => ({ Component: mod.default })),
  },
  {
    path: '/projects',
    lazy: () => import('@/sections/Projects').then(mod => ({ Component: mod.default })),
  },
  {
    path: '/nav',
    lazy: () => import('@/sections/Nav').then(mod => ({ Component: mod.default })),
  },
];

const router = createBrowserRouter(routes);

export default router;

🚀 小结

这个组件展示了如何用非常少的代码和工具类实现炫酷 UI 效果。我们学会了:

  • 使用 map 构建动态组件结构
  • 用 useState 管理当前状态
  • 动态绑定 className 实现动画和交互反馈
  • 熟练使用 TailwindCSS v4 的实用工具类,提升开发效率

📅 明日预告:Progress Steps!实现步骤引导组件。


原文链接:https://blog.csdn.net/qq_44808710/article/details/148045065

更多推荐