Ant Design Skeleton 组件 active 动画效果全解析:从一行代码到 CSS 动画底层实现
2026/9/20 1:50:31 网站建设 项目流程

Ant Design Skeleton 组件 active 动画效果全解析:从一行代码到 CSS 动画底层实现

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design

导读

Skeleton(骨架屏)是 Ant Design 中最常用的加载占位组件之一,而active属性正是让骨架屏从"静止的灰色块"变成"有呼吸感的加载提示"的关键开关。本文以components/skeleton/demo/active.md演示为切入点,结合仓库源码深入讲解active动画的用法、生效范围、CSS 动画底层实现原理,以及它与loadinground等属性的配合技巧,帮助你写出既专业又流畅的加载体验。

一、active 动画效果:两行文档背后的核心能力

在仓库的演示文档 components/skeleton/demo/active.md 中,对动画效果的描述极为凝练:

  • zh-CN:显示动画效果。
  • en-US:Display active animation.

与之配套的演示代码位于 components/skeleton/demo/active.tsx,完整代码如下:

import React from 'react'; import { Skeleton } from 'antd'; const App: React.FC = () => <Skeleton active />; export default App;

这就是骨架屏动画的"一行代码"用法:在<Skeleton />上添加active布尔属性,占位图形就会呈现来回扫过的"微光"效果,向用户传达"内容正在加载中"的信号。虽然演示代码只有一行,但其背后涉及active属性的传递链路、CSS Keyframes 动画定义、以及主题 Token 配置等多层实现,下面逐一展开。

二、active 属性在哪些组件上生效

active并非只作用于<Skeleton />主组件。从源码结构看,Skeleton 是一个复合组件(Compound Component),通过静态属性挂载了ButtonAvatarInputImageNode五个子组件,定义于 components/skeleton/Skeleton.tsx#L188-L192:

Skeleton.Button = SkeletonButton; Skeleton.Avatar = SkeletonAvatar; Skeleton.Input = SkeletonInput; Skeleton.Image = SkeletonImage; Skeleton.Node = SkeletonNode;

因此,active动画可以作用于以下全部形态:

使用方式说明
<Skeleton active />主骨架屏(标题 + 段落,可含头像)整体动画
<Skeleton.Button active />按钮形状骨架屏动画
<Skeleton.Avatar active />头像骨架屏动画
<Skeleton.Input active />输入框骨架屏动画
<Skeleton.Image active />图片骨架屏动画
<Skeleton.Node active>...</Skeleton.Node>自定义节点骨架屏动画

主组件:active 类名的拼接

在 components/skeleton/Skeleton.tsx#L163-L176 中,active会被拼接到根节点类名上:

const cls = classNames( prefixCls, { [`${prefixCls}-with-avatar`]: hasAvatar, [`${prefixCls}-active`]: active, [`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-round`]: round, }, // ... );

activetrue时,渲染出的 DOM 根节点会带有ant-skeleton-active类(默认 prefix 为ant),CSS 层据此触发动画。

子组件:active 直接透传

以 components/skeleton/Button.tsx#L29-L40 为例,子组件同样通过类名拼接响应active

const cls = classNames( prefixCls, `${prefixCls}-element`, { [`${prefixCls}-active`]: active, [`${prefixCls}-block`]: block, }, className, rootClassName, hashId, cssVarCls, );

components/skeleton/Avatar.tsx#L28-L38 中的实现与之对称。这些子组件最终都会把尺寸、形状等参数交给统一的底层元素 components/skeleton/Element.tsx 渲染,Element的 props 类型中同样声明了active?: boolean(见 components/skeleton/Element.tsx#L4-L12),并且支持数字类型的size直接生成像素级宽高。

三、动画的底层实现:CSS-in-JS 中的 Keyframes

active动画并非由 JavaScript 逐帧驱动,而是通过@ant-design/cssinjs生成的标准 CSS 动画实现。核心代码位于 components/skeleton/style/index.ts#L44-L51:

const skeletonClsLoading = new Keyframes(`ant-skeleton-loading`, { '0%': { backgroundPosition: '100% 50%', }, '100%': { backgroundPosition: '0 50%', }, });

这个名为ant-skeleton-loading的关键帧动画,让背景的backgroundPosition100% 50%(最右侧)平移到0 50%(最左侧),从而产生"光带从左向右扫过"的视觉效果。

动画应用规则

动画样式通过genSkeletonColor函数统一生成(components/skeleton/style/index.ts#L76-L83):

const genSkeletonColor = (token: SkeletonToken): CSSObject => ({ background: token.skeletonLoadingBackground, backgroundSize: '400% 100%', animationName: skeletonClsLoading, animationDuration: token.skeletonLoadingMotionDuration, animationTimingFunction: 'ease', animationIterationCount: 'infinite', });

关键点说明:

  • backgroundSize: '400% 100%':背景被放大到容器宽度的 4 倍,为渐变光带留出足够的平移空间;
  • animationTimingFunction: 'ease':缓动函数让动画在首尾减速,观感更自然;
  • animationIterationCount: 'infinite':无限循环,直到组件卸载或active被关闭。

动画的触发范围

genBaseStyle中,active动画被限定在带-active类名的容器内,并一次性作用于所有子占位元素(components/skeleton/style/index.ts#L357-L369):

[`${componentCls}${componentCls}-active`]: { [` ${skeletonTitleCls}, ${skeletonParagraphCls} > li, ${skeletonAvatarCls}, ${skeletonButtonCls}, ${skeletonInputCls}, ${skeletonImageCls} `]: { ...genSkeletonColor(token), }, },

也就是说,只要在<Skeleton active />上开启动画,内部的标题(-title)、段落每一行(-paragraph > li)、头像(-avatar)、按钮(-button)、输入框(-input)、图片(-image)占位块会同步且统一地播放同一条光带动画,整体视觉上非常整齐。

光带渐变与动画时长来自 Design Token

动画背景的渐变色和时长并不是写死的魔法值,而是由主题 Token 提供(components/skeleton/style/index.ts#L404-L405):

skeletonLoadingBackground: `linear-gradient(90deg, ${token.gradientFromColor} 25%, ${token.gradientToColor} 37%, ${token.gradientFromColor} 63%)`, skeletonLoadingMotionDuration: '1.4s',
  • 渐变色:90 度线性渐变,从起点色gradientFromColor(默认取colorFillContent)过渡到终点色gradientToColor(默认取colorFill),再回到起点色,构成"亮-暗-亮"的光带;
  • 动画时长1.4s完成一次完整扫动,节奏舒缓,不会造成视觉压迫。

开发者可以通过主题覆盖gradientFromColorgradientToColor等 Token 自定义光带配色。需要注意的是,旧的colorcolorGradientEnd两个 Token 已被标记为废弃,源码中通过deprecatedTokens声明了从旧到新的映射关系(components/skeleton/style/index.ts#L410-L415)。

四、与 loading 配合:从占位到内容的无缝切换

active动画只有在"占位状态"下才有意义,而控制占位状态的是loading属性。在 components/skeleton/Skeleton.tsx#L108 中:

if (loading || !('loading' in props)) { // 渲染骨架屏 } return children ?? null; // 否则渲染真实内容

规则解析:

  • 未传loading属性时,始终渲染骨架屏(这也是<Skeleton active />单独使用即可看到动画的原因);
  • loadingtrue时渲染骨架屏;
  • loadingfalse时直接渲染children传入的真实内容,没有子节点则返回null

仓库测试 components/skeleton/tests/index.test.tsx#L45-L58 对该行为有明确覆盖:loading={false}且子节点为0时,渲染结果文本是0;子节点为[1, 2, 3]时渲染出123。因此,在实际业务中典型的加载模式是:

import React, { useEffect, useState } from 'react'; import { Skeleton, Card } from 'antd'; const App: React.FC = () => { const [loading, setLoading] = useState(true); const [data, setData] = useState<string>(''); useEffect(() => { // 模拟异步请求 setTimeout(() => { setData('加载完成的内容'); setLoading(false); }, 2000); }, []); return ( <Card> <Skeleton active loading={loading} avatar paragraph={{ rows: 4 }}> {data} </Skeleton> </Card> ); }; export default App;

数据到达后loading置为false,骨架屏连同动画一并消失,内容直接呈现,全程无需手动控制动画的开关。

五、active 动画相关的完整属性速查

为了更准确地使用动画效果,这里汇总主组件与各子组件中与active相关(以及直接影响动画观感)的完整属性清单(依据 components/skeleton/index.zh-CN.md 的 API 章节整理):

Skeleton(主组件)

属性说明类型默认值
active是否展示动画效果booleanfalse
avatar是否显示头像占位图boolean | SkeletonAvatarPropsfalse
loading为 true 时显示占位图,反之直接展示子组件boolean-
paragraph是否显示段落占位图boolean | SkeletonParagraphPropstrue
round为 true 时,段落和标题显示圆角booleanfalse
title是否显示标题占位图boolean | SkeletonTitlePropstrue

SkeletonAvatarProps

属性说明类型默认值
active是否展示动画效果,仅在单独使用头像骨架时生效booleanfalse
shape指定头像的形状circle|square-
size设置头像占位图的大小number |large|small|default-

SkeletonTitleProps

属性说明类型默认值
width设置标题占位图的宽度number | string-

SkeletonParagraphProps

属性说明类型默认值
rows设置段落占位图的行数number-
width设置段落占位图的宽度,若为数组则是对应的每行宽度,反之是最后一行的宽度number | string | Array<number | string>-

SkeletonButtonProps

属性说明类型默认值版本
active是否展示动画效果booleanfalse
block将按钮宽度调整为其父宽度的选项booleanfalse4.17.0
shape指定按钮的形状circle|round|square|default-
size设置按钮的大小large|small|default-

SkeletonInputProps

属性说明类型默认值
active是否展示动画效果booleanfalse
size设置输入框的大小large|small|default-

段落width的"逐行宽度"逻辑可以在 components/skeleton/Paragraph.tsx#L14-L24 中看到具体实现:当width为数组时按索引取对应值;为单个值时仅作用于最后一行(rows - 1 === index)。例如paragraph={{ rows: 3, width: ['80%', '90%', '60%'] }}可构造出前长后短的典型段落效果,配合active动画视觉层次更丰富。

六、组合形态:动画效果在不同骨架元素上的应用

active动画与各类骨架元素组合时,动画始终生效,但占位图形状由各自的shapesize决定。参考 components/skeleton/demo/element.tsx 的交互式演示,可以组合出如下形态:

import React from 'react'; import { Divider, Skeleton, Space, Switch } from 'antd'; import { useState } from 'react'; const App: React.FC = () => { const [active, setActive] = useState(true); return ( <> <Space> <Skeleton.Button active={active} size="large" shape="round" /> <Skeleton.Avatar active={active} size="large" shape="circle" /> <Skeleton.Input active={active} size="large" /> </Space> <Divider /> <Skeleton.Button active={active} block /> <Divider /> <Skeleton active avatar paragraph={{ rows: 4 }} /> <Switch checked={active} onChange={setActive} /> </> ); }; export default App;

要点:

  • Skeleton.Avatarshape仅支持circlesquareSkeleton.Button额外支持round(胶囊形);
  • 数字size(如Skeleton.Avatar size={40})会被 components/skeleton/Element.tsx#L28-L38 直接转为像素宽高与行高;
  • 动画开启与否与形状、尺寸完全正交,可以自由组合,不会互相干扰。

七、动画相关的主题定制(Design Token)

如果需要让动画光带更贴合品牌视觉,可以通过 ConfigProvider 覆盖 Skeleton 的主题 Token。与动画直接相关的 Token 汇总如下(定义于 components/skeleton/style/index.ts#L7-L42):

Token说明默认推导
gradientFromColor渐变色起点颜色colorFillContent
gradientToColor渐变色终点颜色colorFill
titleHeight标题骨架屏高度controlHeight / 2
blockRadius骨架屏圆角borderRadiusSM
paragraphMarginTop段落骨架屏上间距marginLG + marginXXS
paragraphLiHeight段落骨架屏单行高度controlHeight / 2

光带扫描的时长(skeletonLoadingMotionDuration,默认1.4s)属于内部 Token,如需调整动画节奏,可从全局层面覆盖基础 Token 或直接定制渐变两端的颜色:

import React from 'react'; import { ConfigProvider, Skeleton } from 'antd'; const App: React.FC = () => ( <ConfigProvider theme={{ components: { Skeleton: { gradientFromColor: '#f0f5ff', gradientToColor: '#d6e4ff', }, }, }} > <Skeleton active avatar paragraph={{ rows: 3 }} /> </ConfigProvider> );

结语

从 components/skeleton/demo/active.md 中那句"显示动画效果",到 components/skeleton/demo/active.tsx 的一行<Skeleton active />,再到 components/skeleton/style/index.ts 中基于 CSS-in-JS 的ant-skeleton-loading关键帧动画,Ant Design 把"加载中"的视觉反馈封装成了一个几乎零成本的属性开关。理解这条从active属性 →-active类名 → Keyframes 动画 → Design Token 的完整链路,你就能在列表加载、卡片占位、详情页骨架等场景中,用最少的代码交出专业且有质感的加载体验。

【免费下载链接】ant-designAn enterprise-class UI design language and React UI library项目地址: https://gitcode.com/gh_mirrors/ant/ant-design

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询