首页
/ ant-design Carousel 轮播组件完全指南:从基础用法到源码级实现原理

ant-design Carousel 轮播组件完全指南:从基础用法到源码级实现原理

2026-09-06 18:15:17作者:幸俭卉

Carousel(轮播) 是 ant-design(Ant Design React 组件库)中用于承载同一层级一组内容的旋转木马式容器组件,典型应用于图片墙、卡片流的横向/纵向轮播展示。本文以 components/carousel/index.en-US.md 为骨架,结合该组件在仓库内的 TypeScript 实现、样式 Token 与测试用例,系统讲解它的使用时机、全部 API 参数、实例方法、设计 Token 自定义方式,以及基于 @ant-design/react-slick 的底层实现机制。读完你既能直接上手搭建可运行的轮播,也能理解源码层的参数归一化与 RTL/垂直布局处理逻辑,为定制与排障打下基础。

何时使用轮播(When To Use)

Carousel 在 ant-design 的组件分类中属于 Data Display(数据展示),官方文档给出三个典型使用场景:

  • 当页面上存在同一层级的一组内容(如一组 Banner、一组推荐卡片)时,适合用轮播做集中展示;
  • 内容空间不足时,可以通过"旋转门"的形式节省版面空间;
  • 常见于一组图片/卡片的沉浸式轮播。

核心取舍在于:只有当内容彼此平级、空间受限且适合"一次只看一条"时才使用 Carousel;反之若内容需要同时对比或阅读,则应改用栅格/列表布局。

快速上手:基础轮播

官方示例 components/carousel/demo/basic.tsx 展示了最朴素的用法——把任意 ReactNode 放进 Carousel 即可构成轮播页,每个子节点代表一张 slide:

import React from 'react';
import { Carousel } from 'antd';

const contentStyle: React.CSSProperties = {
  margin: 0,
  height: '160px',
  color: '#fff',
  lineHeight: '160px',
  textAlign: 'center',
  background: '#364d79',
};

const App: React.FC = () => {
  const onChange = (currentSlide: number) => {
    console.log(currentSlide);
  };

  return (
    <Carousel afterChange={onChange}>
      <div>
        <h3 style={contentStyle}>1</h3>
      </div>
      <div>
        <h3 style={contentStyle}>2</h3>
      </div>
      <div>
        <h3 style={contentStyle}>3</h3>
      </div>
      <div>
        <h3 style={contentStyle}>4</h3>
      </div>
    </Carousel>
  );
};

export default App;

从源码 components/carousel/index.tsx 可以看到组件实现的几个关键细节:

  • Carousel 是 React.forwardRef 包装的组件,对子节点通过 toArray(children) 归一化,并据此计算 slide 数量 countindex.tsx);
  • 默认 arrows = falseautoplay = falsedraggable = falseautoplaySpeed = 3000waitForAnimate = falseindex.tsx);
  • 组件本身并不实现滑动逻辑,而是把归一化后的 props 透传给 @ant-design/react-slickSlickCarouselindex.tsx),antd 在上层补齐了 dot 位置合并、垂直推断、RTL 处理与样式体系。

高度自适应

若各 slide 内容高度不一,可开启 adaptiveHeight(默认 false),让轮播容器高度随当前 slide 内容自动调整。

指示点(Dots)位置控制

指示点默认显示在底部中央。dotPlacement 用于指定指示点相对轮播的位置,可选值为 topbottomstartend,其中 start/end 会随书写方向与布局方向(横向为主时对应左/右,垂直布局时对应上/下)解析。官方示例 components/carousel/demo/placement.tsx 使用 Radio 实时切换四种位置:

import React, { useState } from 'react';
import type { CarouselProps, RadioChangeEvent } from 'antd';
import { Carousel, Radio } from 'antd';

type DotPlacement = CarouselProps['dotPlacement'];

const App: React.FC = () => {
  const [dotPlacement, setDotPlacement] = useState<DotPlacement>('top');

  const handlePositionChange = ({ target: { value } }: RadioChangeEvent) => {
    setDotPlacement(value);
  };

  return (
    <>
      <Radio.Group onChange={handlePositionChange} value={dotPlacement} style={{ marginBottom: 8 }}>
        <Radio.Button value="top">Top</Radio.Button>
        <Radio.Button value="bottom">Bottom</Radio.Button>
        <Radio.Button value="start">Start</Radio.Button>
        <Radio.Button value="end">End</Radio.Button>
      </Radio.Group>
      <Carousel dotPlacement={dotPlacement}>
        {/* 4 个 slide 内容省略,同上 */}
      </Carousel>
    </>
  );
};

从源码看 dotPosition 与 dotPlacement 的关系

dotPosition 是旧 API(取值 top/bottom/left/right/start/end),自 dotPlacement 引入后已被标记 @deprecated。源码 components/carousel/index.tsxuseMemo 完成归一化合并:

  • 优先取 dotPlacement,其次取 dotPosition,默认 bottom
  • left 会被映射为 startright 被映射为 end,从而用一套 start/end 语义同时兼容 RTL 与 LTR;
  • 开发环境下若仍传入 dotPosition,会通过 devUseWarning('Carousel') 打出 dotPositiondotPlacement 的 deprecation 警告(index.tsx)。

垂直模式的自动推断

源码中当 dotPlacementstartend 时,Carousel 会自动把 vertical 置为 true(除非显式传入 vertical,见 index.tsx),这意味指示点在左右两侧时轮播本身转为纵向滑动;同时向 react-slick 传入 verticalSwiping={mergedVertical} 以启用纵向拖拽。

自动播放与指示点进度条

autoplay 控制是否自动滚动,默认 false。它既支持布尔值,也支持对象形式 autoplay={{ dotDuration: true }}dotDuration 选项自 5.24.0 起支持),后者会让激活态指示点显示一条随时间增长的"加载进度",直观提示距下一次自动切换还剩多长时间。autoplaySpeed 用于设定相邻两次自动滚动的时间间隔(毫秒),默认 3000

官方示例 components/carousel/demo/autoplay.tsx 是最简自动播放形式:

<Carousel autoplay>
  {/* 4 个 slide */}
</Carousel>

components/carousel/demo/dot-duration.tsx 演示了"自动播放 + 指示点进度条 + 更慢的间隔"组合(版本要求 5.24.0+):

<Carousel autoplay={{ dotDuration: true }} autoplaySpeed={5000}>
  {/* 4 个 slide */}
</Carousel>

进度条的样式实现原理

进度条并非由 JS 计时器驱动,而是纯 CSS 动画:组件把 autoplaySpeed(毫秒)写入 CSS 变量 --dot-duration(源码定义为 export const DotDuration = '--dot-duration',见 style/index.ts),作为激活态指示点的 animation-duration

const mergedShowDuration = autoplay && (isPlainObject(autoplay) ? autoplay.dotDuration : false);
const dotDurationStyle: React.CSSProperties = mergedShowDuration
  ? { [DotDuration]: `${autoplaySpeed}ms` }
  : {};

随后外层容器接收该 style(index.tsx),style/index.ts 中定义的 @ant-...-dot-animation 关键帧让激活点宽度从 0 生长到 dotActiveWidth,从而与 autoplay 节奏精确同步(水平点从宽度 0 起增长,垂直点则由专门的关键帧从高度 0 增长)。

autoplay 与 rtl 的联动

autoplay 布尔值会被强制为 !!autoplay 后传入底层(index.tsx),即"对象形式开启、布尔 true 开启"。测试 components/carousel/tests/index.test.tsx 还覆盖了窗口 resize 后自动播放触发的场景,并在组件卸载时移除 resize 监听以避免泄漏(同文件 L99-L110)。

切换效果:fade 淡入淡出

默认切换效果是横向滑动(effect="scrollx"),若希望相邻画面间平滑淡入淡出,可设置 effect="fade"。官方示例 components/carousel/demo/fade.tsx

<Carousel effect="fade">
  {/* 4 个 slide */}
</Carousel>

effect 的取值在源码中以联合类型定义:export type CarouselEffect = 'scrollx' | 'fade';index.tsx)。渲染前会做一次归一化——当传入 effect === 'fade' 时,除保留 effect 外还会把 react-slick 的 fade 标记置为 trueindex.tsx),确保底层走 fade 渲染管线。

切换箭头(arrows)

arrows 决定是否显示上一张/下一张切换箭头,默认 false,自 5.17.0 起支持。官方示例 components/carousel/demo/arrows.tsx 同时展示了横向与纵向(dotPlacement="start" 触发)两种箭头布局,并配合 infinite={false} 关闭无限循环:

<>
  <Carousel arrows infinite={false}>
    {/* slides */}
  </Carousel>
  <br />
  <Carousel arrows dotPlacement="start" infinite={false}>
    {/* slides */}
  </Carousel>
</>

箭头按钮的源码实现

源码中没有额外依赖图标库,而是用原生 <button> 构造箭头:内部组件 ArrowButton 接收 react-slick 传入的 currentSlide/slideCount 等状态并以 button 渲染(index.tsx)。默认箭头提供了无障碍标签 aria-label,文案取自 locale 中的 prevSlide/nextSlide,且在 RTL 下交换方向(index.tsx):

prevArrow={prevArrow ?? <ArrowButton aria-label={isRTL ? contextLocale.nextSlide : contextLocale.prevSlide} />}
nextArrow={nextArrow ?? <ArrowButton aria-label={isRTL ? contextLocale.prevSlide : contextLocale.nextSlide} />}

箭头样式由 style/index.tsgenArrowsStyle 负责:绝对定位于垂直居中,通过 ::after 伪元素绘制两条边框旋转得到指向左右的箭头,hover/focus 时透明度从 0.4 提升至 1,slick-disabled 态(首/末且 infinite={false})则隐藏并禁止点击。垂直布局下箭头会改到顶部/底部居中。

自定义箭头(FAQ 官方口径)

官方文档 FAQ 明确指出:若要添加自定义箭头,参考 issue #12479 的思路——基于 react-slick 的 API,通过 prevArrow / nextArrow 传入自定义按钮组件即可覆盖默认箭头。

完整 API 一览

除了上面涉及的属性,ant-design Carousel 还支持下述常用配置。通用组件属性(class 名、style、id、无障碍等)见 Common props(仓库对应 docs/react 目录)。以下所有属性能否被全局 ConfigProvider 的组件级配置覆盖,以最后一列为准(Carousel 当前均为 ×,即不支持通过 components/config-providercomponentConfig 批量注入):

属性 说明 类型 默认值 版本 全局配置
arrows 是否显示切换箭头 boolean false 5.17.0 ×
autoplay 是否自动滚动,可传 autoplay={{ dotDuration: true }} 显示进度条 boolean | { dotDuration?: boolean } false dotDuration: 5.24.0 ×
autoplaySpeed 每次自动滚动的间隔(毫秒) number 3000 ×
adaptiveHeight 自动调整 slide 高度 boolean false ×
dotPlacement 指示点位置,取 top/bottom/start/end 之一 string bottom ×
dotPosition 指示点位置(旧 API),取值 top/bottom/left/right/start/end,请改用 dotPlacement string bottom ×
dots 是否在底部显示指示点;传对象可配置 dotsClass 之外的自定义 className(即 { className?: string } boolean | { className?: string } true ×
draggable 桌面端是否可通过拖拽滚动 boolean false ×
fade 是否使用淡入淡出过渡(也可用 effect 控制) boolean false ×
infinite 是否无限循环包裹内容 boolean true ×
speed 动画速度(毫秒) number 500 ×
easing 过渡插值函数名 string linear ×
effect 过渡效果 scrollx | fade scrollx ×
afterChange 当前索引变化后回调 (current: number) => void - ×
beforeChange 当前索引变化前回调 (current: number, next: number) => void - ×
waitForAnimate 切换时是否等待动画完成 boolean false ×

说明:Carousel 的类型定义继承自 @ant-design/react-slickSettings(通过 Omit<Settings, 'dots' | 'dotsClass' | 'autoplay'> 排除被重定义项,见 index.tsx),因此 react-slick 的其余底层配置项同样可用。完整底层 API 请查阅 react-slick 官方文档(Settings 全部字段在 index.tsx 的类型引用中亦有体现)。

几个值得留意的行为细节

  • 滑动容器默认开启 waitForAnimate = false,即快速连点时动画未结束也会响应下一次切换;
  • slide 内的交互元素:样式层对非激活 slide 做了 pointer-events: none 并隐藏其中的 radio/checkbox 输入,仅在 .slick-active 时恢复可见与可点(style/index.ts),避免轮播未激活页内的表单控件被误操作;
  • RTL:若外层 ConfigProvider 设置 direction="rtl" 且非垂直布局,isRTL 为 true((rtl ?? direction === 'rtl') && !vertical),组件会额外带上 -rtl class,并对首屏初始 slide 做镜像换算 count - initialSlide - 1index.tsx);
  • children 归一化:多根或 Fragment 内容会被 toArray 拍平后逐个当作 slide。

实例方法(Methods)

Carousel 暴露了 CarouselRef 类型的 ref 能力。类型定义(index.tsx)除了文档列出的三个方法,还透出了 autoPlay(playType?)innerSlidernativeElement(DOM 根节点,自 5.4.0 起支持用于获取真实节点引用)。

名称 说明
goTo(slideNumber, dontAnimate) 跳到指定 slide 索引;dontAnimate=true 时无动画直达
next() 切换到下一张
prev() 切换到上一张

源码中的方法接线

goTo 内部最终调用 react-slick 实例的 slickGoTo(slide, dontAnimate)index.tsx),其余方法通过 React.useImperativeHandle 直接暴露底层 slick 能力:

React.useImperativeHandle(
  ref,
  () => ({
    goTo,
    autoPlay: slickRef.current.innerSlider.autoPlay,
    innerSlider: slickRef.current.innerSlider,
    prev: slickRef.current.slickPrev,
    next: slickRef.current.slickNext,
    nativeElement: nativeElementRef.current!,
  }),
  [slickRef.current],
);

测试 components/carousel/tests/index.test.tsx 验证了完整调用链路:ref.current.goTo(2)innerSlider.state.currentSlide 变为 2,等待动画完成后 prev()/next() 依次把 currentSlide 从 1 切回 2;nativeElement 指向根节点 .ant-carousel 的测试见同文件 L34-L44。典型用法如下:

import React, { useRef } from 'react';
import type { CarouselRef } from 'antd';
import { Carousel, Button } from 'antd';

const App: React.FC = () => {
  const ref = useRef<CarouselRef>(null);

  return (
    <>
      <Button onClick={() => ref.current?.goTo(2)}>跳到第 3 张</Button>
      <Button onClick={() => ref.current?.prev()}>上一张</Button>
      <Button onClick={() => ref.current?.next()}>下一张</Button>
      <Carousel ref={ref}>...</Carousel>
    </>
  );
};

Design Token:主题化定制指示点与箭头

Carousel 支持通过 Design Token 进行主题化。组件样式采用 antd 的 cssinjs 方案,Token 定义于 style/index.tsprepareComponentToken 给出默认值(style/index.ts):

Token 含义 默认值
dotWidth 指示点宽度 16
dotHeight 指示点高度 3
dotGap 相邻指示点间距 等于全局 marginXXS
dotOffset 指示点距轮播边缘的距离 12
dotActiveWidth 激活态指示点宽度 24
dotWidthActive 激活态指示点宽度(已废弃,请改用 dotActiveWidth 24
arrowSize 切换箭头尺寸 16
arrowOffset 切换箭头距轮播边缘的距离 等于全局 marginXS

源码中 deprecatedTokens: [['dotWidthActive', 'dotActiveWidth']]style/index.ts)注册了旧 Token 的自动迁移映射。

在 ConfigProvider 中覆盖 Token

官方调试示例 components/carousel/demo/component-token.tsx 展示了如何把指示点改成宽 50、高 50、激活宽 80 的"块状"样式(注:该 demo 标记为 debug,仅供验证组件能力,生产项目请按设计需要取值):

import { Carousel, ConfigProvider } from 'antd';

export default () => (
  <ConfigProvider
    theme={{
      components: {
        Carousel: {
          dotWidth: 50,
          dotHeight: 50,
          dotActiveWidth: 80,
        },
      },
    }}
  >
    <Carousel>{/* slides */}</Carousel>
  </ConfigProvider>
);

Token 生效的关键在样式生成端:

  • 指示点核心样式由 genDotsStyle 生成(style/index.ts):指示点宽度过渡到 dotActiveWidth,激活点 ::after 通过 animation-duration: var(--dot-duration) 与 autoplay 进度联动;
  • 垂直场景由 genCarouselVerticalStyle 生成(style/index.ts):dots 转为纵向 flexDirection: column 并整体 translateY(-50%) 居中,且把"宽/高"互换(reverseSizeOfDot),激活动画改用高度增长的关键帧;
  • RTL 场景由 genCarouselRtlStyle 补充(style/index.ts)。

可访问性与国际化

常见问题 FAQ

如何添加自定义箭头?

参见官方文档 FAQ 指向的 issue #12479。核心思路是:arrow 元素本质上只是渲染在轮播两侧、可点击的按钮,antd 将 prevArrow/nextArrow 原样透传给 react-slick(index.tsx),因此你可以用任意自定义组件替换默认箭头:

<Carousel arrows prevArrow={<YourArrow direction="prev" />} nextArrow={<YourArrow direction="next" />}>
  {/* slides */}
</Carousel>

小结

ant-design 的 Carousel 是一层"薄壳":它在成熟的 @ant-design/react-slick 之上补齐了指示点位置归一化(dotPositiondotPlacement)、垂直模式推断、RTL 镜像、基于 CSS 变量 --dot-duration 的 autoplay 进度动画以及一套完整的 cssinjs Token 体系。理解这层设计后,无论是通过 dotPlacement/effect/autoplay 快速完成常见交互,还是借助 Design Token 深度定制指示点与箭头外观,或是通过 ref 的方法(goTo/prev/next)接入业务导航,都能得心应手。仓库内每个特性都配有可运行的 demo 与自动化测试(tests),可作为你集成与验证行为的直接参考。

登录后查看全文
热门项目推荐
相关项目推荐