☰
ng-zorro-antd Affix(固钉)组件完全指南:从 API 配置到源码级实现原理
2026/9/25 8:51:36 网站建设 项目流程
  • UI组件
  • 前端

【免费下载链接】ng-zorro-antd

Angular UI Component Library based on Ant Design

项目地址:https://gitcode.com/gh_mirrors/ng/ng-zorro-antd
点击查看免费下载

Affix(固钉)是 ng-zorro-antd 提供的页面固定组件,用于将侧边菜单、操作按钮等元素"钉"在可视范围内,使其在页面滚动时始终保持可见。本文以 components/affix/doc/index.zh-CN.md 官方文档为骨架,结合组件源码、官方 demo 与全局配置实现,系统讲解nz-affix的完整 API、实战用法、全局配置方式以及底层固定位置计算原理。读完本文,你将能够在长页面场景下正确使用 Affix 固定导航与操作区,并理解其"滚动监听 + 占位元素 + 固定定位"的核心工作机制。

何时使用 Affix

当内容区域比较长、需要滚动页面时,这部分内容对应的操作或导航需要在滚动范围内始终展现。Affix 最常见的应用场景是侧边菜单和按钮组合:例如文章详情页侧边的目录导航、长表单页底部的"保存 / 取消"操作按钮、列表页顶部的筛选工具栏等。

需要注意的是:当页面可视范围过小时,慎用此功能,以免固定元素遮挡页面内容。这是官方文档明确给出的使用边界——Affix 不应覆盖页面其他内容,尤其是视口较小时更应谨慎。

快速上手:最简单的用法

在独立组件(standalone)或模块化引入方式下,首先导入NzAffixModule:

import { NzAffixModule } from 'ng-zorro-antd/affix'; import { NzButtonModule } from 'ng-zorro-antd/button';

将需要固定的元素包进<nz-affix>即可。参考官方基础示例 components/affix/demo/basic.ts:

<nz-affix [nzOffsetTop]="offsetTop"> <button nz-button nzType="primary" (click)="setOffsetTop()"> <span>Affix top</span> </button> </nz-affix> <br /> <nz-affix [nzOffsetBottom]="nzOffsetBottom" (click)="setOffsetBottom()"> <button nz-button nzType="primary"> <span>Affix bottom</span> </button> </nz-affix>
export class NzDemoAffixBasicComponent { offsetTop = 10; nzOffsetBottom = 10; setOffsetTop(): void { this.offsetTop += 10; } setOffsetBottom(): void { this.nzOffsetBottom += 10; } }

第一个 Affix 固定于顶部、距顶部10px处触发;第二个固定于底部、距底部10px处触发。点击按钮改变偏移量,可以直观看到固定触发点随偏移值变化。

API 详解:nz-affix 的四个成员

官方文档给出nz-affix的全部 API,整理如下:

成员说明类型默认值全局配置
[nzOffsetBottom]距离窗口底部达到指定偏移量后触发number-✅
[nzOffsetTop]距离窗口顶部达到指定偏移量后触发number0✅
[nzTarget]设置nz-affix需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数string \| HTMLElementwindow
(nzChange)固定状态改变时触发的回调函数EventEmitter<boolean>-

nzOffsetTop:顶部固定偏移

元素滚动到距离窗口(或nzTarget指定容器)顶部nzOffsetTop像素时触发固定,默认值为0,即元素一到达视口顶部边缘即被固定。

在源码 components/affix/affix.component.ts 中,该输入通过numberAttributeWithZeroFallback转换器处理,并带有@WithConfig()装饰器,说明它支持全局配置覆盖:

@Input({ transform: numberAttributeWithZeroFallback }) @WithConfig() nzOffsetTop?: null | number;

nzOffsetBottom:底部固定偏移

当元素滚动到距离窗口底部nzOffsetBottom像素时触发固定,此时元素以position: fixed; bottom: ...的方式贴住视口底部。该参数没有默认值(-),即默认不启用底部固定模式。

注意一个关键的优先级逻辑(见 affix.component.ts):若nzOffsetTop与nzOffsetBottom都未显式设置数值,则默认采用顶部固定模式且offsetTop = 0;只要其中一个被设置为数值,就按显式值进入对应的固定模式。

nzTarget:自定义滚动容器

nzTarget用于设置nz-affix需要监听滚动事件的元素,默认是window。传入值可以是:

  • 返回 DOM 元素的函数(文档表述);
  • 直接传HTMLElement或选择器字符串string(源码类型为string | Element | Window,见 affix.component.ts)。

源码中target的解析逻辑(affix.component.ts):

private get target(): Element | Window { const el = this.nzTarget; return (typeof el === 'string' ? this.document.querySelector(el) : el) || window; }

即字符串会被当作 CSS 选择器查询,查询失败或未设置时回退到window。当固定发生在非窗口容器中时,底部偏移会额外叠加"窗口高度与容器底部之间"的间距(affix.component.ts):

const targetBottomOffset = targetNode === window ? 0 : window.innerHeight - targetRect.bottom!;

nzChange:固定状态改变回调

(nzChange)在固定状态发生切换时触发(从不固定变为固定、或从固定变为不固定),回调参数为布尔值:true表示当前已固定,false表示已解除固定。

源码中该输出通过 Angular 新式output()函数声明(affix.component.ts),并仅在状态真正发生翻转时才 emit(affix.component.ts):

if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) { this.nzChange.emit(fixed); }

监听固定状态变化

参考官方示例 components/affix/demo/on-change.ts:设置nzOffsetTop为120,即元素滚动到距顶部 120px 时固定,并通过nzChange回调得知当前是否处于固定状态:

<nz-affix [nzOffsetTop]="120" (nzChange)="onChange($event)"> <button nz-button> <span>120px to affix top</span> </button> </nz-affix>
export class NzDemoAffixOnChangeComponent { onChange(status: boolean): void { console.log(status); } }

该能力非常实用:例如固定后可以给元素追加阴影样式、改变按钮文案,或联动调整页面布局的占位。

在自定义滚动容器内固定(nzTarget 实战)

官方示例 components/affix/demo/target.ts 演示了如何将 Affix 限定在某个可滚动容器内,而不是整个窗口:

<div class="scrollable-container" #target> <div class="background"> <nz-affix [nzTarget]="target" id="affix-container-target"> <button nz-button nzType="primary"> <span>Fixed at the top of container</span> </button> </nz-affix> </div> </div>
styles: ` .scrollable-container { height: 100px; overflow-y: scroll; } .background { padding-top: 60px; height: 300px; background-image: url(//zos.alipayobjects.com/rmsportal/RmjwQiJorKyobvI.jpg); } `

要点:

  • 容器需要设置固定高度与overflow-y: scroll(或auto),使其成为真正可滚动的区域;
  • 通过模板引用变量#target将容器元素传给nzTarget;
  • 此时 Affix 只监听该容器的滚动事件,容器内部的滚动才会触发固定,页面本身的滚动不会影响它。

全局配置:通过 NzConfig 统一设置偏移

表格中nzOffsetTop与nzOffsetBottom标注了"全局配置 ✅",意味着可以通过 ng-zorro-antd 的全局配置服务统一设置,无需在每个组件实例上重复传入。

全局配置类型定义于 components/core/config/config.ts:

export interface AffixConfig { nzOffsetBottom?: number; nzOffsetTop?: number; }

配置方式(例如在app.config.ts中):

import { provideNzConfig } from 'ng-zorro-antd/core/config'; const nzConfig = { affix: { nzOffsetTop: 80, // 全局默认顶部偏移 nzOffsetBottom: 40 // 全局默认底部偏移 } }; export const appConfig: ApplicationConfig = { providers: [provideNzConfig(nzConfig)] };

实例上显式传入的nzOffsetTop/nzOffsetBottom会覆盖全局配置(源码中@WithConfig()装饰器即实现"实例值优先、否则回退全局值"的合并逻辑)。

源码级实现原理

要真正驾驭 Affix,理解其底层机制很有帮助。核心逻辑集中在 components/affix/affix.component.ts:

1. 事件监听与节流

组件在构造时通过effect注册监听(affix.component.ts),并借助ngZone.runOutsideAngular将事件处理移出 Angular Zone 以减少不必要的变更检测开销。监听的事件定义于 components/affix/respond-events.ts:

export enum AffixRespondEvents { resize = 'resize', scroll = 'scroll', touchstart = 'touchstart', touchmove = 'touchmove', touchend = 'touchend', pageshow = 'pageshow', load = 'LOAD' }

所有事件流与NzResizeObserver观察到的尺寸变化合并后,统一经过throttleTime(20ms)节流(常量NZ_AFFIX_DEFAULT_SCROLL_TIME = 20,见 affix.component.ts),最终驱动updatePosition重算位置。

2. 占位元素与双样式机制

这是 Affix 最精妙的设计:组件模板(affix.component.ts)在内部包了一层<div #fixedEl>来装载投影内容(ng-content),而组件宿主本身充当占位元素:

<div #fixedEl> <ng-content /> </div>
  • 占位样式(placeholderStyle):当内容被固定(脱离文档流)时,宿主元素会被赋予与固定内容相同的width与height,从而撑住原布局位置,避免下方内容上跳;
  • 固定样式(affixStyle):内部fixedEl被设置为position: fixed并定位到视口(或容器)相应位置,同时添加ant-affix类(常量NZ_AFFIX_CLS_PREFIX,见 affix.component.ts)。

固定时元素的实际定位计算(顶部模式)为(affix.component.ts):

if (scrollTop >= elemOffset.top - (offsetTop as number) && offsetMode.top) { const width = elemOffset.width; const top = targetRect.top + (offsetTop as number); this.setAffixStyle(e, { position: 'fixed', top, left: targetRect.left + elemOffset.left, width }); ... }

其中元素相对容器的偏移由工具函数getOffset计算(affix.component.ts),getTargetRect则区分窗口(返回全零矩形)与普通元素(返回getBoundingClientRect()),实现见 components/affix/utils.ts。

3. 与文档一致的行为细节

  • 固定/解除固定只改变内部fixedEl的样式与ant-affix类,不销毁、不重建投影内容;
  • 方向(RTL/LTR)变化时组件会重新计算位置(构造函数中effect读取dir()信号);
  • 滚动事件经过 20ms 节流且带trailing选项,保证连续滚动时高频位置更新被合并,性能可控。

注意事项与避坑指南

nz-affix内的元素不要使用绝对定位(position: absolute),因为固定定位(fixed)与绝对定位叠加会破坏相对定位基准。如确实需要绝对定位效果,官方文档给出的做法是:直接对nz-affix本身设置绝对定位:

<nz-affix style="position: absolute; top: 10px, left: 10px"> ... </nz-affix>

另外两点实践建议:

  • 固定元素会脱离文档流,Affix 已通过占位元素保持布局稳定,但请避免在视口过小的场景下使用,防止固定内容遮挡正文;
  • 页面中存在横向滚动条时,注意固定元素以left定位(相对滚动容器左侧),若容器宽度小于元素宽度可能造成截断,必要时结合全局配置调整偏移。

小结

Affix 是长页面场景下提升可用性的基础组件,其核心 API 只有四个:顶部偏移nzOffsetTop(默认 0)、底部偏移nzOffsetBottom、自定义滚动容器nzTarget(默认window)以及固定状态回调nzChange。通过全局配置NzConfig的affix节点可以统一管理前两个偏移参数。从源码看,它依靠"滚动/触摸/尺寸变化事件 + 20ms 节流"驱动位置重算,并用"宿主占位 + 内部 fixed 样式"的双层结构在固定时保持布局稳定——理解这一机制,有助于在复杂布局中正确地使用并排查 Affix 相关的定位问题。

  • UI组件
  • 前端

【免费下载链接】ng-zorro-antd

Angular UI Component Library based on Ant Design

项目地址:https://gitcode.com/gh_mirrors/ng/ng-zorro-antd
点击查看免费下载
上一篇:终极指南:3步让老旧Mac免费升级到最新macOS系统
下一篇:Anki间隔重复记忆系统完整指南:免费开源的高效学习工具

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

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

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

立即咨询