首页
/ Ant Design Drawer 遮罩(mask)详解:从 blur / dimmed / none 三种效果到 mask 属性的底层实现

Ant Design Drawer 遮罩(mask)详解:从 blur / dimmed / none 三种效果到 mask 属性的底层实现

2026-09-07 23:12:05作者:田桥桑Industrious

在 Ant Design 的 Drawer 抽屉组件中,遮罩(mask)是覆盖在页面其余内容上的半透明层,用来隔离背景交互并引导用户聚焦于抽屉内容。本篇文章以官方 demo 文档 components/drawer/demo/mask.md(中文标题为"遮罩效果",英文标题为 "mask effect")为核心,完整讲解其配套示例 mask.tsx 所演示的 blur(模糊)、dimmed(变暗)、none(无遮罩)三种遮罩形态,并结合源码剖析 mask 属性的对象化配置、点击关闭语义、ConfigProvider 级联配置与底层样式实现。阅读完本文后,你将能精准控制 Drawer 遮罩的开关、模糊与点击行为,并理解这些配置背后在 Drawer.tsxuseMergedMask.ts 中的真实生效链路。

mask 属性:从布尔值到对象配置的能力跃迁

要理解"遮罩效果"这个 demo,首先需要认识 Drawer 的 mask 属性。根据 index.en-US.md 中的 API 表格,其类型定义为:

属性 说明 类型 默认值 版本
mask 遮罩效果 boolean | { enabled?: boolean, blur?: boolean, closable?: boolean } true mask.closable: 6.3.0

也就是说,mask 在传统布尔值基础上,支持传入对象来细分控制三个维度:

  • enabled:是否渲染遮罩,等效于原来的布尔开关;
  • blur:是否在遮罩之上叠加背景模糊效果;
  • closable:点击遮罩区域(抽屉外部区域)是否关闭抽屉。

从源码看,这一对象化配置由 useMergedMask.ts 中的 MaskConfig 类型承载,并被 Drawer.tsx 中重定义的 DrawerProps['mask'] 引用为 MaskType

export interface MaskConfig {
  enabled?: boolean;
  blur?: boolean;
  closable?: boolean;
}
export type MaskType = MaskConfig | boolean;

示例 demo 正是用对象/布尔两种写法演示了三种遮罩档位:{ blur: true }(blur 模糊遮罩)、true(dimmed 变暗遮罩)、false(none 无遮罩)。

精读官方 demo:一个页面演示三种遮罩形态

mask.md 的中英文描述虽然只有"遮罩效果 / mask effect"寥寥几字,但真正的技术内容都沉淀在配套的 mask.tsx 中。它定义了一个联合类型与配置表,将三种遮罩模式映射到对应的 mask 取值:

import React, { useState } from 'react';
import { Button, Drawer, Space } from 'antd';

type MaskType = 'blur' | 'dimmed' | 'none';
type DrawerConfig = {
  type: MaskType;
  mask: boolean | { blur: boolean };
  title: string;
};

const drawerList: DrawerConfig[] = [
  { type: 'blur', mask: { blur: true }, title: 'blur' },
  { type: 'dimmed', mask: true, title: 'Dimmed mask' },
  { type: 'none', mask: false, title: 'No mask' },
];

在渲染层,demo 用 useState 记录当前打开的遮罩类型,open 仅在 open === item.type 时为 true,因此三个按钮各自对应一个 Drawer 实例,点击按钮时只会唤起匹配类型的那一个:

const App: React.FC = () => {
  const [open, setOpen] = useState<false | MaskType>(false);

  const showDrawer = (type: MaskType) => {
    setOpen(type);
  };

  const onClose = () => {
    setOpen(false);
  };

  return (
    <Space wrap>
      {drawerList.map((item) => (
        <React.Fragment key={item.type}>
          <Button
            onClick={() => {
              showDrawer(item.type);
            }}
          >
            {item.title}
          </Button>
          <Drawer
            title={item.title}
            placement="right"
            mask={item.mask}
            onClose={onClose}
            open={open === item.type}
          >
            <p>Some contents...</p>
            <p>Some contents...</p>
            <p>Some contents...</p>
          </Drawer>
        </React.Fragment>
      ))}
    </Space>
  );
};

export default App;

三种取值的直观差异可以概括为:

  • mask={{ blur: true }}:遮罩仍然渲染并变暗,同时页面背景会再叠加一层 4px 的模糊(详见下文样式解析),适合需要彻底弱化背景的专注类场景;
  • mask: true(默认值):经典半透明遮罩,背景被 colorBgMask 色值压暗,但仍可看清轮廓,是最常用的"dimmed 变暗"形态;
  • mask: false:完全不渲染遮罩层,抽屉悬浮于页面上方,背景可继续交互。

注意 open 同一时刻只可能等于一种类型,showDrawer 再次点击同一个按钮不会导致状态抖动,而点击遮罩或关闭按钮时统一走 onCloseopen 置回 false

mask={false}:无遮罩场景与其调试示例

无遮罩也是官方正式支持的使用形态。文档 index.en-US.md 中以 debug 形式收录了另一个示例 No mask,配套 demo 见 no-mask.tsx

<Drawer
  title="Drawer without mask"
  placement="right"
  mask={false}
  onClose={onClose}
  open={open}
>
  <p>Some contents...</p>
  <p>Some contents...</p>
  <p>Some contents...</p>
</Drawer>

mask={false} 会被 useMergedMask.ts 中的 normalizeMaskConfig 归一化为 { enabled: false },进而让合并结果中 enabled !== false 的判断失败,Drawer 底层不再挂载遮罩 DOM。该 demo 中还顺带演示了 styles.mask 的语义化样式写法(如 widthbackgroundborderRadiusboxShadowoverflow)。这里有一个实现层面的注意点:遮罩 DOM 是否存在取决于 enabled,只有当遮罩实际渲染时 styles.mask / classNames.mask 才会真正作用于遮罩元素(相关语义结构说明见文档中的 Semantic DOM 一节)。因此,如果希望页面背景下压暗的同时保留一个特殊形状的遮罩,应开启遮罩并对 styles.mask 做定制,而不是与 mask={false} 组合使用。

需要补充的是,把 mask 设为 false 并不等于抽屉变得无法关闭:用户仍可通过右上角关闭按钮、ESC 键(keyboard 属性控制)等途径关闭,只是"点击遮罩关闭"这一交互天然失效,因为遮罩根本不存在。

点击遮罩关闭:closable 与已废弃的 maskClosable

传统上 Drawer 通过 maskClosable 控制"点击遮罩关闭",该属性在 Drawer.tsx 中被标注为 @deprecated,官方建议迁移到 mask.closable。二者在 useMergedMask 中完成了兼容性合并:

export const useMergedMask = (
  mask?: MaskType,
  contextMask?: MaskType,
  prefixCls?: string,
  maskClosable?: boolean,
) => {
  return useMemo(() => {
    const maskConfig = normalizeMaskConfig(mask, maskClosable);
    const contextMaskConfig = normalizeMaskConfig(contextMask);

    const mergedConfig: MaskConfig = {
      blur: false,
      ...contextMaskConfig,
      ...maskConfig,
      closable: maskConfig.closable ?? maskClosable ?? contextMaskConfig.closable ?? true,
    };

    const className = mergedConfig.blur ? `${prefixCls}-mask-blur` : undefined;

    return [mergedConfig.enabled !== false, { mask: className }, !!mergedConfig.closable];
  }, [mask, contextMask, prefixCls, maskClosable]);
};

从这段源码可以读出四条关键信息:

  1. 默认值closable 的最终取值按 mask.closablemaskClosable → 上下文 → true 的优先级链取第一个非空值,因此默认情况下点击遮罩即可关闭抽屉;
  2. 归一化逻辑:布尔 mask 会被自动转换成 { enabled },保证后续统一以对象处理;
  3. blur 类名派生:当 blur: true 时,返回给上层的类名是 ${prefixCls}-mask-blur,也就是实际渲染出的 ant-drawer-mask-blur
  4. 返回值三元组:最终返回 [是否启用遮罩, 遮罩类名对象, 是否可点击遮罩关闭],由 Drawer.tsx 解构后分别透传给 rc-drawer 的 maskmaskClosable

相关行为在测试中有直接覆盖,例如 DrawerEvent.test.tsxmaskClosable 相关用例验证了 "点击遮罩不触发 onClose" 与 "对象化配置与 ConfigProvider 全局配置的优先级关系",可作为 mask.closable 替换 maskClosable 的回归保障。

遮罩的模糊效果如何实现:backdrop-filter 与 motion 动画

blur: true 时,遮罩层会额外获得模糊能力。这一效果并非通过改变遮罩本身的透明度实现,而是基于 CSS backdrop-filter。在 Drawer 的样式文件 components/drawer/style/index.ts 中可以找到遮罩的基础样式:

[`${componentCls}-mask`]: {
  position: 'absolute',
  inset: 0,
  zIndex: zIndexPopup,
  background: colorBgMask,
  pointerEvents: 'auto',

  [`&${componentCls}-mask-blur`]: {
    backdropFilter: 'blur(4px)',
  },
},

细节解读如下:

  • 遮罩背景色来自设计令牌 colorBgMaskzIndex 取自 Drawer 的组件令牌 zIndexPopup,因此模糊/变暗效果与整体弹层层级体系一致;
  • 叠加的 .ant-drawer-mask-blur 类把 backdrop-filter 设为 blur(4px),实现"毛玻璃"式的背景模糊。backdrop-filter 需要浏览器支持,且对父级层叠上下文有一定要求,这在低版本浏览器中可能表现为无模糊但遮罩仍在,属正常的渐进增强行为;
  • 遮罩的 pointerEvents: 'auto' 保证了点击遮罩可以被捕获并触发 closable 关闭逻辑。

此外,遮罩与面板的入场/离场动画在 components/drawer/style/motion.ts 中定义,而动画名则由 Drawer.tsx 通过 getTransitionName(prefixCls, 'mask-motion') 动态生成,使"淡入淡出遮罩 + 滑入滑出面板"共用同一套 MOTION_CONFIGmotionAppear/motionEnter/motionLeave 均开启,motionDeadline: 500)。测试快照中可见实际生成的类名,例如 ant-drawer-mask ant-drawer-mask-blur,参见 demo-extend.test.tsx.snap 中的渲染结果。

ConfigProvider 全局配置:遮罩的上下文级联

mask 不仅能写在单个 Drawer 上,还可以通过 ConfigProvider 的组件级配置(components={{ drawer: { mask: ... } } })对整棵组件树生效。该能力在 index.en-US.md 的 API 表中对应 "Global Config" 一列:mask 全局配置自 6.0.0 起支持,mask.closable 自 6.3.0 起支持。

Drawer.tsx 中,组件通过 useComponentConfig('drawer') 取出上下文中的 mask: contextMask,随后在 useMergedMask.ts 中与组件自身的 mask 做浅合并:

const mergedConfig: MaskConfig = {
  blur: false,
  ...contextMaskConfig,
  ...maskConfig,
  closable: maskConfig.closable ?? maskClosable ?? contextMaskConfig.closable ?? true,
};

展开顺序 ...contextMaskConfig 在前、...maskConfig 在后,意味着单个 Drawer 上显式传入的 mask 字段会覆盖 ConfigProvider 的全局配置,而全局配置又能兜底所有未显式声明该字段的 Drawer。DrawerEvent.test.tsxmask.closable 与 ConfigProvider 配置互相覆盖的用例,正是对这一优先级关系的回归验证。这一机制非常适合"整站统一关闭遮罩"或"全局默认开启背景模糊"这类批量治理诉求。

mask 与其他能力的联动:焦点管理、嵌套与样式令牌

Drawer.tsx 的实现可以观察到一个容易被忽略的联动:焦点陷阱是否启用取决于遮罩状态:

const mergedFocusable = useFocusable(
  { ...contextFocusable, ...focusable },
  getContainer !== false && mergedMask,
);

即当 Drawer 通过 getContainer 渲染在 body 下且遮罩启用时,默认会开启焦点管理(focusTrap),把 Tab 焦点约束在抽屉内部;而当 mask={false} 时,焦点陷阱的默认前提被破坏,需要显式通过 focusable 配置({ trap?: boolean, focusTriggerAfterClose?: boolean },自 6.2.0 起支持)来接管。这意味着"去掉遮罩"不只是视觉层面的取舍,还牵动键盘可达性与无障碍体验,在需要背景内容保留可交互性的无遮罩场景下要格外留意焦点是否合理流转。

此外,Drawer 的嵌套场景(push 属性,默认 { distance: 180 })会让被压入的抽屉整体(含其遮罩区域)随面板一起平移,多个抽屉的遮罩按 zIndexPopup 令牌与渲染顺序正确堆叠;这些能力与 mask 效果组合后仍能正常工作。涉及遮罩的样式令牌主要是全局设计令牌 colorBgMask(遮罩底色)与组件令牌 zIndexPopup(弹层层级),开发者可通过主题 token 覆盖实现自定义的遮罩观感,例如更深的压暗色或更高的层级。

小结与验证路径

围绕 mask.md 的"遮罩效果"主题,本文要点可归纳为:

  • mask 支持 boolean{ enabled, blur, closable } 对象两种形态,对应 demo 中的 none / dimmed / blur 三种遮罩形态;
  • mask={false} 移除遮罩层,需配合键盘与焦点方案保障可访问性;
  • 点击遮罩关闭由 mask.closable 控制,maskClosable 已废弃;
  • 模糊遮罩通过 ant-drawer-mask-blur + backdrop-filter: blur(4px) 实现,遮罩动画与颜色分别由 motion.tscolorBgMask 令牌决定;
  • 全局配置可通过 ConfigProvider 注入,组件级显式配置优先。

若想进一步核对以上结论,可以按图索骥:阅读 demo 源码 mask.tsxno-mask.tsx 观察使用姿势;查看 useMergedMask.ts 理解归一化与优先级合并;在 style/index.ts 中验证 blur 的 CSS 实现;最后借助 Drawer.test.tsx 中针对 ant-drawer-mask-blur 类名的断言(校验开启 blur 后类名出现、关闭后消失)来确认行为与预期一致。掌握这些细节后,无论是要做沉浸式专注的模糊遮罩、轻量悬浮的无遮罩抽屉,还是全局统一遮罩策略,都能在 Ant Design Drawer 上从容落地。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
915
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388