首页
/ Ant Design BorderBeam 动画时长:用 duration 属性控制边框流光节奏的完整解析

Ant Design BorderBeam 动画时长:用 duration 属性控制边框流光节奏的完整解析

2026-09-06 15:01:16作者:裴麒琰

本篇指南围绕 Ant Design BorderBeam(边框流光)组件的 duration 属性展开,讲清楚它如何控制流光完成一圈动画所需的时间,并在当前仓库源码层面说明该属性如何经由 CSS 变量落到 animation-duration、如何参与多条流光(count)的错峰延迟计算,以及默认值 6 秒的来源。读完你可以直接在自己的项目中配置合理的流光节奏,并理解其降级与无效值回退行为。

duration 是什么:一句话定义

在官方示例说明文档 duration.md 中,duration 的定义非常明确:

通过 duration 控制流光完成一圈所需时间,单位为秒,默认值为 6 秒。

即流光(beam)沿容器边框环绕一周所耗费的秒数。对应组件 API 表中(见 index.zh-CN.md):

参数 说明 类型 默认值 版本 全局配置
duration 流光完成一圈动画的时间,单位秒 number 6 6.5.0 ×

注意两点事实前提:

  • duration 属于 BorderBeam 组件属性,随 6.5.0 版本引入(组件本身是 6.4.0 新增);
  • 它不支持 ConfigProvider 的组件级全局配置(API 表“全局配置”列为 ×),只能逐实例通过属性传入。

官方示例:三种节奏的对照演示

配套示例 duration.tsx 用三张 Card 并排展示了 3 种典型时长,这也是理解取值语义的最佳入口:

import React from 'react';
import { BorderBeam, Card, Flex, Tag, Typography } from 'antd';

const durations = [
  {
    name: 'Fast',
    seconds: 3,
    description: 'A quick loop for temporary highlights and active modules.',
  },
  {
    name: 'Default',
    seconds: 6,
    description: 'The original pacing for most emphasized containers.',
  },
  {
    name: 'Slow',
    seconds: 12,
    description: 'A calmer loop for persistent panels and ambient surfaces.',
  },
];

const App: React.FC = () => (
  <Flex gap={16} wrap>
    {durations.map(({ name, seconds, description }) => (
      <div key={name} style={{ width: 220 }}>
        <BorderBeam duration={seconds}>
          <Card title={name} extra={<Tag variant="filled">{seconds}s</Tag>}>
            <Typography.Text type="secondary">{description}</Typography.Text>
          </Card>
        </BorderBeam>
      </div>
    ))}
  </Flex>
);

export default App;

示例给出的取值语义可以总结为:

  • 3 秒(Fast):快速循环,适合临时性高亮、正在激活的模块;
  • 6 秒(Default):默认节奏,适合大多数需要强调的容器;
  • 12 秒(Slow):更沉稳的循环,适合常驻面板、氛围型背景表面。

从示例快照(demo-extend.test.ts.snap)可以验证,这三个卡片最终分别生成了 --ant-border-beam-duration: 3s6s12s 的内联 CSS 变量,与传入值一一对应。

源码实现:duration 如何变成 CSS 动画时长

1. 默认值与合法性校验

默认值常量定义在 util.ts 中:

export const DEFAULT_BORDER_BEAM_DURATION = 6;

在组件主文件 BorderBeam.tsx 中,传入值会先经过一次合法性归一化:

const mergedDuration =
  isNumber(duration) && duration > 0 ? duration : DEFAULT_BORDER_BEAM_DURATION;

也就是说,只有“是数字且大于 0”的 duration 才会被采纳,否则一律回退到 6 秒。这意味着:

  • 0、负数、NaN、字符串或 undefined 都不会报错,而是静默使用默认值;
  • mergedDuration 只用于内部计算(如多条流光的错峰),不直接决定最终样式。

2. 通过 CSS 变量注入内联样式

真正把 duration 写到 DOM 上的,是渲染时对内联 style 的条件展开:

...(isNumber(duration) && duration > 0 && { [varName('duration')]: `${duration}s` }),

只有当 duration 是合法正数时,才会设置 --ant-border-beam-duration(变量前缀随主题 hash 变化,varNamegenCssVar 生成),并以秒为单位拼接 s 后缀。若不传该属性,这个 CSS 变量根本不会写入,样式层会取到兜底默认值——这就是“不传 duration 也是 6 秒”的实现方式。

3. 样式层:animation-duration 的落点

在样式文件 style/index.ts 中,流光动画本体由 ::before 伪元素承载:

const antBorderBeamMove = new Keyframes('antBorderBeamMove', {
  from: { offsetDistance: '0%' },
  to: { offsetDistance: '100%' },
});

关键帧基于 CSS Motion Path 的 offset-path/offset-distance:一个边长为 size 的方形渐变层(offsetPath: rect(0 auto auto 0 round ...))沿容器边框路径从 0% 移动到 100%,配合 @supports (offset-path: rect(...)) 特性探测,在不支持该特性的浏览器中整个流光层保持 display: none 静默降级。动画属性如下:

'&::before': {
  ...
  animationName: antBorderBeamMove,
  animationDuration: varRef('duration', `${DEFAULT_BORDER_BEAM_DURATION}s`),
  animationDelay: varRef('delay', '0s'),
  animationTimingFunction: 'linear',
  animationIterationCount: 'infinite',
  willChange: 'offset-distance',
}

注意 varRef('duration', '6s')duration 属性最终就是这里 animation-duration 的 CSS 变量来源,默认兜底同样来自 DEFAULT_BORDER_BEAM_DURATION。由于 animationTimingFunctionlinear 且循环 infiniteduration 越大整体观感越平缓,越小越急促——与示例中 Fast/Default/Slow 的语义完全一致。

duration 与 count 的联动:多条流光的错峰延迟

duration 并非孤立存在,它与 count(流光数量,6.6.0 引入)共同决定多条流光的相位分布。仍在 BorderBeam.tsx 中:

...(index > 0 && {
  [varName('delay')]: `${(-mergedDuration * index) / mergedCount}s`,
}),

index 条流光(从 1 开始)的动画延迟为 -duration × index / count。负延迟使所有流光在页面加载瞬间即处于循环的不同相位,均匀分布在一圈内。

这一点被单测直接固化(见 index.test.tsx):

rerender(
  <BorderBeam count={3} duration={12}>
    <div>content</div>
  </BorderBeam>,
);

expect(getBeamElements(container)).toHaveLength(3);
// 三条流光各自的 animation-delay
expect(...).toEqual(['', '-4s', '-8s']);

count=3duration=12 时,三条流光的延迟依次为 0s-4s-8s(12 ÷ 3 = 4 秒一档)。由此可以得到一个实用的调参结论:调大 duration 时若想保持多条流光的均匀间隔感,无需额外处理——错峰延迟会随 duration 等比放大,视觉上依然是均匀分布。

同一测试文件还验证了 duration=12 时 beam 元素的 --ant-border-beam-duration12s,而移除该属性后变量被清空、回落到样式层默认的 6 秒。

失效边界与降级行为

理解 duration 的完整行为,还需要知道两条“不出效果”的边界,均能从样式源码确认(style/index.ts):

  1. CSS 特性不支持:流光依赖 mask-compositeoffset-path: rect(...),任一不支持时组件通过 @supports 门控保持 display: noneduration 自然无从生效,但不会破坏页面布局(流光层本身 position: absolutepointer-events: none,不干扰内容交互)。
  2. 减少动态效果偏好:命中 prefers-reduced-motion: reduce 时,::before 被显式 display: none,同时 genNoMotionRawStyletransition/animation 置为 none。官方文档 FAQ 也明确:BorderBeam 将流光视为装饰效果,用户系统开启减少动态效果时 beam 会被隐藏。此时 duration 的取值没有意义,属于预期行为而非缺陷。

另外,流光层通过 createPortal 插入到 children 对应的真实 DOM 节点中(见 BorderBeamEffect.tsx),因此被包裹的内容必须是原生 DOM 元素或正确透传 ref 的组件,否则组件拿不到宿主节点,直接不渲染流光层——这一点在 index.zh-CN.md 的 FAQ “为什么 BorderBeam 没有效果?” 中也有说明,排查 duration 不生效时应优先确认流光本身是否已渲染。

使用建议与小结

  • duration 的单位是秒,类型必须是数字;0、负数等非正数值会被静默回退为默认 6 秒,建议在业务代码中只传有意义的正数(如 3 / 6 / 12);
  • 临时强调、激活态高亮建议取较短值(3 秒上下),常驻氛围效果建议取较长值(10 秒以上),避免视觉疲劳;
  • count 搭配时不必单独考虑相位:组件会按 -duration × index / count 自动均分,duration 只决定整体快慢;
  • 该属性自 6.5.0 起可用,且不支持 ConfigProvider 全局配置,需要按实例传递;
  • 完整属性(colorsizelineWidthoutset 等)以 index.zh-CN.md 的 API 表为准,更多示例可参考 demo 目录 下的 basichovercount 等演示。

一句话总结:durationBorderBeam 流光动画的节奏旋钮——它经由内联 CSS 变量 --ant-border-beam-duration 映射到 ::before 伪元素的 animation-duration,默认 6 秒,非法值自动回退,并会按比例参与多条流光的错峰延迟计算。

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