📅 今天我们继续 50 个小项目挑战!——ImageCarousel组件

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

​​​​

构建一个简洁且功能齐全的图片轮播组件(Carousel)。该组件不仅支持手动切换上一张和下一张图片,还具备自动播放功能。

🌀 组件目标

  • 实现自动播放和手动切换功能
  • 组件结构清晰、易于扩展
  • 使用 TailwindCSS 快速构建 UI 样式

🔧 ImageCarousel.tsx组件实现

import React, { useState, useEffect, useRef } from 'react'

const ImageCarousel: React.FC = () => {
    // 图片数组(可替换为实际数据)
    const images = [
        'https://picsum.photos/id/10/800/400',
        'https://picsum.photos/id/11/800/400',
        'https://picsum.photos/id/12/800/400',
        'https://picsum.photos/id/13/800/400',
    ] as const

    const [currentIndex, setCurrentIndex] = useState<number>(0)
    const autoPlayTimerRef = useRef<NodeJS.Timeout | null>(null)

    // 切换到上一张
    const prevImage = () => {
        setCurrentIndex((prev) => (prev - 1 + images.length) % images.length)
    }

    // 切换到下一张
    const nextImage = () => {
        setCurrentIndex((prev) => (prev + 1) % images.length)
    }

    // 启动自动轮播
    const startAutoPlay = () => {
        if (autoPlayTimerRef.current) return
        autoPlayTimerRef.current = setInterval(() => {
            nextImage()
        }, 3000)
    }

    // 清除定时器
    const clearAutoPlay = () => {
        if (autoPlayTimerRef.current) {
            clearInterval(autoPlayTimerRef.current)
            autoPlayTimerRef.current = null
        }
    }

    // 组件挂载时启动轮播,卸载时清除
    useEffect(() => {
        startAutoPlay()
        return () => {
            clearAutoPlay()
        }
    }, [])

    return (
        <div className="flex min-h-screen items-center justify-center bg-gray-900">
            <div className="absolute top-1/2 left-1/2 h-[400px] w-[800px] -translate-x-1/2 -translate-y-1/2 overflow-hidden">
                {/* 图片容器 */}
                <div
                    className="flex h-full transition-transform duration-500 ease-in-out"
                    style={{ transform: `translateX(-${currentIndex * 100}%)` }}>
                    {images.map((src, index) => (
                        <img
                            key={index}
                            src={src}
                            alt={`Slide ${index + 1}`}
                            className="h-[400px] w-[800px] shrink-0 object-cover"
                        />
                    ))}
                </div>

                {/* 控制按钮 */}
                <div className="absolute bottom-5 left-1/2 flex -translate-x-1/2 gap-5">
                    <button
                        onClick={prevImage}
                        className="cursor-pointer rounded border-none bg-[rgba(0,0,0,0.5)] px-4 py-2 text-white transition-colors hover:bg-[rgba(0,0,0,0.8)]"
                        aria-label="上一张">
                        上一张
                    </button>
                    <button
                        onClick={nextImage}
                        className="cursor-pointer rounded border-none bg-[rgba(0,0,0,0.5)] px-4 py-2 text-white transition-colors hover:bg-[rgba(0,0,0,0.8)]"
                        aria-label="下一张">
                        下一张
                    </button>
                </div>
            </div>
            <div className="fixed right-20 bottom-5 z-100 text-2xl text-red-500">
                CSDN@Hao_Harrision
            </div>
        </div>
    )
}

export default ImageCarousel

🔄 关键差异总结

功能 Vue 3 React + TS
响应式状态 ref() useState()
生命周期 onMounted / onUnmounted useEffect(() => { ... }, [])
定时器存储 模块变量 useRef
列表渲染 v-for .map()
内联样式 :style style={{}}
样式类名 直接写 相同(Tailwind 兼容)

🔁 转换说明

1. 状态管理:ref → useState

Vue React
const currentIndex = ref(0) const [currentIndex, setCurrentIndex] = useState(0)

✅ 功能完全等价。


2. 生命周期:onMounted / onUnmounted → useEffect

Vue:

onMounted(() => startAutoPlay());
onUnmounted(() => clearInterval(autoPlayTimer));

React:

useEffect(() => {
  startAutoPlay();
  return () => clearAutoPlay(); // 清理函数 = onUnmounted
}, []);

useEffect 的返回函数会在组件卸载时执行,等效于 onUnmounted


3. 定时器管理:使用 useRef 存储 timer ID

  • Vue 中直接用 let autoPlayTimer = null(模块作用域变量);
  • React 中必须用 useRef 保存 timer,避免闭包问题且不触发重渲染:
const autoPlayTimerRef = useRef<NodeJS.Timeout | null>(null);

✅ 这是 React 中管理定时器、订阅等副作用的标准做法


4. 自动轮播安全启动

const startAutoPlay = () => {
  if (autoPlayTimerRef.current) return; // 防止重复启动
  autoPlayTimerRef.current = setInterval(...);
};

✅ 避免多次点击或快速切换导致多个定时器并发。


5. JSX vs Template

Vue React
<img v-for="..." :src="image" /> {images.map(...)}
:style="{ transform: ... }" style={{ transform: ... }}

✅ 所有逻辑一一对应。


6. 样式与布局优化

  • 添加 flex-shrink-0 到 <img>:确保每张图保持 800px 宽度,不被 flex 压缩;
  • 使用 -translate-x-1/2 -translate-y-1/2 实现绝对居中(与 Vue 一致);
  • 按钮添加 transition-colors 提升交互体验;
  • 添加 alt 属性符合可访问性规范。

7. 类型安全增强

  • 使用 as const 固定 images 数组,防止意外修改;
  • 明确 NodeJS.Timeout 类型(Node 环境)或浏览器环境可用 number,但在 Vite + TS 中通常兼容。

如果你在纯浏览器环境开发,也可写成:

const autoPlayTimerRef = useRef<number | null>(null);

因为 setInterval 在浏览器返回 number

Vite 默认支持 Node 类型,所以 NodeJS.Timeout 更通用。

✅ 功能验证

  • 页面加载后自动轮播(3秒/张);
  • 点击“上一张”/“下一张”可手动切换;
  • 组件卸载时自动清除定时器(无内存泄漏);
  • 图片水平滑动动画流畅;
  • 响应式居中布局。

💡 扩展建议(可选)

  • 添加指示器(dots)显示当前页;
  • 支持传入 images 作为 props;
  • 暂停自动播放(hover 时暂停);
  • 键盘导航(← → 方向键)。

🎨 TailwindCSS 样式重点讲解

🎯 TailwindCSS 样式说明
类名 作用
absolute 将元素定位为绝对定位
top-1/2 left-1/2 设置元素相对于最近的定位祖先顶部和左侧对齐
-translate-x-1/2 -translate-y-1/2 将元素沿 X 轴和 Y 轴移动自身宽度和高度的一半,实现居中对齐
h-[400px] w-[800px] 固定元素的高度和宽度
overflow-hidden 隐藏超出容器的内容
transition-transform duration-500 ease-in-out 添加变换属性的过渡动画,持续时间为 500ms,缓动效果为 ease-in-out
object-cover 保持图片比例并填充整个容器
hover:bg-[rgba(0,0,0,0.8)] 设置鼠标悬停时的背景颜色为更深的半透明黑色

🦌 路由组件 + 常量定义

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

{
    path: '/',
    element: <App />,
    children: [
       ...
      {
                path: '/ImageCarousel',
                lazy: () =>
                    import('@/projects/ImageCarousel').then((mod) => ({
                        Component: mod.default,
                    })),
            },
    ],
 },
constants/index.tsx 添加组件预览常量
import demo35Img from '@/assets/pic-demo/demo-35.png'
省略部分....
export const projectList: ProjectItem[] = [
    省略部分....
     {
        id: 35,
        title: 'Image Carousel',
        image: demo35Img,
        link: 'ImageCarousel',
    },
]

🚀 小结

通过这篇文章,我们使用 React19 和 TailwindCSS 创建一个功能完善的图片轮播组件。

你可以扩展这个基础的图片轮播组件:

✅ 添加指示器(Indicators):显示当前是第几张图片,支持点击切换。
✅ 触控滑动(Touch Support):支持在移动端通过滑动手势切换图片。
✅ 暂停自动播放:当鼠标悬停在轮播图上时暂停自动播放。
✅ 动态加载图片:从后端接口获取图片数据。
✅ 响应式设计:根据屏幕尺寸自适应轮播图宽度和高度。

📅 明日预告: 我们将完成Hoverboard组件,实现一个非常炫酷的鼠标悬浮彩虹轨迹。🚀

感谢阅读,欢迎点赞、收藏和分享 😊


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

每天造一个轮子,码力暴涨不是梦!🚀

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐