Angular 头像组件 | ng-hub-ui-avatar

Angular 头像组件,支持首字母、备用图片、在线状态徽标和 CSS 变量,用于可复用的用户身份界面。

最后更新 2026年9月20日

概览

团队为何搜索这个库

当你需要稳健的用户身份界面,并支持备用方案、首字母、多个图片来源以及对设计系统友好的样式钩子时,请选择这个 Angular 头像组件。

安装

npm install ng-hub-ui-avatar

跳转到

适用于

  • 用户菜单
  • 团队列表
  • 动态信息流
  • 个人资料卡片

关于 avatar

ng-hub-ui-avatar 帮助 Angular 应用在菜单、个人资料卡片和协作界面中一致地展示用户。它覆盖了常见的边界情况,例如图片缺失、首字母生成、状态指示器和共享的尺寸规则。

功能指南

头像来源

可从社交资料、自定义图片、名称首字母或自定义值解析头像,并提供自动回退。

示例:
Facebook 资料

Facebook 资料

Gravatar

Gravatar

GitHub 资料

GitHub 资料

自定义图片来源

自定义图片来源

姓名首字母

姓名首字母

JD
值显示

值显示

75%

回退系统

会优雅地逐一降级遍历每个已配置的来源,直到显示出一个有意义的表示。

示例:
回退系统

回退系统

Fallback Initials

Showing GitHub source (Facebook ID invalid)

尺寸

可控制头像尺寸、文字与尺寸的比例以及渲染多少个首字母。

示例:
自定义尺寸

自定义尺寸

SD
文字大小比例

文字大小比例

RD
首字母数量限制

首字母数量限制

JD

样式

形状、圆角半径、颜色、边框和任意内联样式——全部由输入和 CSS 变量驱动。

示例:
圆形头像

圆形头像

R
圆角半径

圆角半径

R
背景色和前景色

背景色和前景色

C
Custom BG & FG (inputs)
TD
Themed via --hub-avatar-bg-color ([autoColor]="false")
边框颜色

边框颜色

B
自定义样式

自定义样式

S

交互性

通过头像点击事件对用户交互做出响应。

示例:
点击事件处理器

点击事件处理器

CM
Click the avatar, or focus it and press Enter…

分组与徽标

将头像堆叠为分组,并在角落叠加徽标——状态圆点或计数——使用语义色板着色。

示例:
头像组显示

头像组显示

A
B
C
+3
徽标(圆点与计数)

徽标(圆点与计数)

AL
GH
AT
LT
CM
4k
JD
9+

自定义内容

可将任意图标、内联 SVG、图片或表情符号投影到头像内。它会居中、留白并按头像形状裁剪,以不依赖类型的方式调整大小(字体图标缩放,SVG/图片填充),并通过常规的 bgColor / fgColor 输入进行主题化。

示例:
自定义内容(图标、SVG)

自定义内容(图标、SVG)

🚀

核心特性

最近更改

Version 22.12.2 - 9/20/26, 12:00 AM

changed: The npm keywords declare ng-hub-ui, the family name somebody searching for the ecosystem types. Metadata only: no code, types or styles change.

Version 22.12.1 - 9/16/26, 12:00 AM

changed: Repository, issue and README links follow the move to the hub-env organization. Issues for every Hub UI package are now gathered in hub-env/hub-ui, and the repository and bugs fields of the manifest point at the new addresses. No code, types or styles change.

Version 22.12.0 - 9/8/26, 12:00 AM

changed: The two exported classes carry the Hub prefix: HubAvatarComponent and HubAvatarService. A bare AvatarComponent is a name in the consumer's own namespace, not in ours, and an application with users tends to grow an avatar component of its own; the day it does, the two collide in whichever file imports both, and the only way out is an import alias on our side of the line for a name we never had the right to take. Every other class in this family is already prefixed. Nothing about the components changed: same selector, same inputs, same behaviour, same injectable. AvatarModule keeps its name on purpose, because it is already announced for removal in 23.0.0.

deprecated: AvatarComponent and AvatarService are now deprecated aliases, kept so nothing breaks today and removed in 23.0.0. Each is a re-export of the prefixed class, so a codebase importing the old name keeps compiling and keeps getting the very same class, which is pinned by a test rather than promised in prose. See BREAKING_CHANGES.md.

added: A test reads the exported surface back from the compiled module and fails on any class that ships without the Hub prefix, save the two aliases and AvatarModule. A naming rule nothing enforces is one class away from being false: the next export lands unprefixed and nobody notices until a consumer's own AvatarComponent collides with it.

added: ng-hub-ui-ds is declared as an optional peer dependency (>=22.0.0). The avatar colours and radii resolve through the family --hub-sys-* / --hub-ref-* ladder and the manifest said nothing about it, so a consumer reading the package on npm could not tell which package supplies them. It stays optional: every token ends in a literal fallback.

Version 22.11.1 - 9/8/26, 12:00 AM

deprecated: The AvatarModule deprecation now names the release that removes it: 23.0.0. "A future major version" told a reader nothing they could plan against, because in this ecosystem the major tracks Angular's and is not an API decision anyone can anticipate. 23.0.0 is the release that moves this library to Angular 23, and it is the same release the modules of ng-hub-ui-modal, -portal, -calendar, -skeleton, -stepper and -breadcrumbs are removed in. AvatarModule.forRoot() goes with the module. Nothing changes at runtime and nothing is removed yet: both keep working exactly as before, and the migration — AvatarComponent plus provideAvatar() — is written out in BREAKING_CHANGES.md.

Version 22.11.0 - 9/7/26, 12:00 AM

added: placeholder finally paints something. The input had been declared since the component was written and nothing read it, so an avatar with no source and no initials rendered an empty circle while both READMEs listed the input as reserved. It now holds the picture of last resort: shown only while nothing else is — no source resolved, none declared, or one still loading — and never as part of the fallback chain, so it cannot outrank the initials the way handing the same URL to src would. A placeholder that itself fails to load is dropped rather than retried.

Version 22.10.0 - 9/6/26, 12:00 AM

added: Source is part of the public API. The interface that types the clickOnAvatar payload lived only inside the package, so nobody could name the type of their own handler: the choice was any or a hand-copied duplicate that drifts the moment the real one changes. It is a type, so re-exporting it from the entry point costs nothing at runtime.

changed: clickOnAvatar emits Source | null, not Source. The payload was read from a cursor into the fallback list, and that cursor legitimately sits outside it: at -1 until something resolves (an avatar drawn from projected content alone never leaves it) and past the last entry once every source has failed. Clicking in either state handed the consumer undefined through an output that promised a Source, so a handler reading $event.sourceType threw. The absence is now declared instead of leaked and the click still fires; a handler typed (source: Source) has to widen. See BREAKING_CHANGES.md.

changed: The hub-avatar component runs on OnPush. It was the last component in the family still checked on every tick of the application, so a consumer who put an avatar inside an OnPush tree paid for that check and got nothing from it. What the template reads is derived from the inputs, so it marks the view on its own, which is what made the strategy safe to switch.

changed: The avatar is derived from its inputs instead of patched from SimpleChanges. The fallback chain was rebuilt by hand in ngOnChanges, one entry added or removed per changed property, and everything it produced (the resolved picture, the initials, their inline styles) was parked in mutable fields. The chain is a function of the source inputs, so it is computed from them now; the cursor that walks it is linked to it, and what gets painted derives from wherever that cursor sits. The ChangeDetectorRef went with it, because the template reads signals and those mark the view on their own, and dependencies now arrive through inject(). The render state the component used to expose as public fields is now internal. See BREAKING_CHANGES.md.

fixed: Clearing the last source input now clears the avatar. Unsetting name (or src, or any other source) dropped the source from the fallback chain and stopped there: with nothing left to resolve, nothing repainted, and the initials or the picture of the value that had just been removed stayed on screen until some other source arrived. A list rendering avatars for a selection would keep showing the person who had just been deselected.

fixed: The avatar image is named after the person, not after the URL it came from. The alt fell back to the resolved source, so a Gravatar, Facebook or custom picture without an explicit alt made a screen reader read the whole address aloud, and an image resolved asynchronously (GitHub) shipped with no alt at all or with the address a previous source had left behind. The accessible name now comes from alt when given, from name otherwise, and stays empty when there is neither, because an avatar nobody named is decorative and silence beats a URL.

fixed: The manifest now exports the stylesheet paths the documentation teaches. The tarball has always carried styles/index.scss and styles/mixins/_avatar-theme.scss, but the generated exports map listed only the package entry point, so anything under styles/ was formally private. The Angular CLI happens not to notice, since its Sass integration falls back to resolving the package root and joins the rest of the path by hand, yet every resolver that honours the map (webpack's sass-loader, dart-sass's pkg: importer) refuses @use 'ng-hub-ui-avatar/styles' outright. Declaring ./styles and ./styles/mixins/avatar-theme makes the published surface match what the README, the docs page and BREAKING_CHANGES.md tell consumers to write, and aligns the package with ng-hub-ui-ds, which already lists its stylesheet subpaths. Packaging metadata only, with no code, no types and no styles change.

Version 22.9.3 - 9/1/26, 12:00 AM

changed: The homepage in the manifest points at this library's own documentation page rather than at the site root. It is the link a registry shows beside the package and the one a reader clicks from it, and landing on a front page they then have to search is a worse answer than landing on the reference for the package they were already looking at. Metadata only — no code, no types, no styles change, and nothing a consumer imports is affected.

Version 22.9.2 - 8/18/26, 12:00 AM

changed: The README now says what the remote sources cost. Gravatar, GitHub and Facebook were documented as sources resolved over HTTP, which stops one sentence short of the part a consumer has to decide on: those requests leave the visitor's browser, so the third party sees their IP address, and Gravatar also receives a hash of the email address passed to it — enough to correlate a person across every site using it. Nothing changed in the code; the alternative — custom image, initials or text — never leaves your own origin.

Version 22.9.1 - 8/8/26, 12:00 AM

fixed: Documentation links now point at the canonical localized URLs. The README linked to https://hubui.dev/<path> with no locale prefix and no trailing slash, and both forms are 301-redirected, so every reader arriving from npm or GitHub landed on a redirect instead of the canonical page.

Version 22.9.0 - 7/28/26, 12:00 AM

changed: Accent resolution now imports the canonical resolveHubAccent from ng-hub-ui-utils. The private copy under src/lib/shared/resolve-hub-accent.ts has been deleted in favour of the single, tested implementation shared family-wide. Behaviour is identical (the copy had not diverged): a bareword resolves to var(--hub-sys-color-<name>, <name>), a literal colour passes through unchanged, an empty value yields null.

added: NEW peer dependency: ng-hub-ui-utils >=22.7.0. Consumers must have ng-hub-ui-utils installed alongside this library (it is where resolveHubAccent lives). Users installing via ng add ng-hub-ui get it automatically; manual installs need npm i ng-hub-ui-utils.

Version 22.8.0 - 7/28/26, 12:00 AM

added: interactive input: exposes role=button, focusability and Enter/Space activation for avatars that bind (clickOnAvatar) — the click target used to be mouse-only.

Version 22.7.0 - 7/7/26, 12:00 AM

changed: BREAKING (packaging): the SCSS ships at ng-hub-ui-avatar/styles. The theming mixin now builds to dist/avatar/styles/... instead of dist/avatar/src/lib/styles/..., so @use 'ng-hub-ui-avatar/styles' resolves; any @use that reached into src/lib/styles has to be updated.

changed: badgeColor accepts ANY colour. On top of the built-in semantic accents it now takes a registered custom accent and a literal colour (#ff0000, rgb(), oklch(), a CSS named colour): a bareword becomes var(--hub-sys-color-<name>, <name>), a literal is used as is. The single --hub-avatar-accent slot derives the rest of the family, so the built-in colours are unchanged.

changed: Internal: host bindings moved from the @HostBinding / @HostListener decorators to the host metadata object, as the Angular style guide prescribes. No public API or behaviour change.

Version 22.6.0 - 7/7/26, 12:00 AM

added: autoColor input — set [autoColor]="false" on an initials avatar to suppress the inline hash background so it can be themed via the --hub-avatar-bg-color CSS variable (no !important). Defaults to true; an explicit bgColor still wins.

Version 22.5.0 - 6/30/26, 12:00 AM

added: Standalone AvatarComponent — import it directly (no NgModule) and configure with the new provideAvatar() environment provider.

deprecated: AvatarModule / AvatarModule.forRoot() — kept for backward compatibility (now re-exporting the standalone component); use AvatarComponent + provideAvatar() instead. Removed in 23.0.0.

Version 22.3.0 - 6/26/26, 12:00 AM

changed: BREAKING: the presence-only status input is replaced by a general badge overlay. badge (bare) or [badge]="true" renders a dot, badge="4k" a labelled pill, null or absent nothing; the colour comes from the semantic badgeColor input, so presence is expressed through it (online → success, away → warning, busy → danger, offline → secondary). HubAvatarStatus is renamed HubAvatarBadgeColor and the --hub-avatar-status-* tokens --hub-avatar-badge-*. See BREAKING_CHANGES.md.

added: Semantic colour variants for the avatar and its badge, generated in one loop: a coloured-circle avatar per semantic colour (class="hub-avatar--success") and the matching badgeColor. The hub-avatar-color-variants($colors) mixin emits both in your own CSS, for the eight semantic colours or for your own map.

常见问题

没有头像图片时,怎么显示姓名首字母?

安装 ng-hub-ui-avatar,同时把两个来源都交给 <hub-avatar>:[src] 给图片,[name] 给人名。组件会组成一条回退链,图片加载失败时就把该来源标记为失败并转向下一个,也就是首字母。autoColor 用人名的哈希推导背景色,所以同一个人始终是同一个颜色。

它能从 Gravatar 或 GitHub 拉取头像吗?

能:gravatarId、githubId 和 facebookId 都是输入,provideAvatar() 设定来源的尝试顺序。但要清楚这在运行时意味着什么:浏览器会直接请求 secure.gravatar.com、api.github.com 或 graph.facebook.com,所以这些服务能看到你访客的 IP 地址,而 Gravatar 还会拿到邮箱的哈希值。

怎么显示一组互相叠压的头像?

用一个带 hub-avatar-group 类的元素把它们包起来。叠压完全是 CSS,不是组件:--hub-avatar-group-overlap 决定每个头像压住前一个多少,--hub-avatar-group-ring-width 与 --hub-avatar-group-ring-color 画出分隔它们的圆环。

在选它而不是自己写之前,有哪些要知道的?

三点。size 只接受像素,没有 sm/md/lg 这类尺寸档;字号、徽标和内边距都由 --hub-avatar-size 推导,所以一个数字就能整体缩放。分组是一个 CSS 类,不是 <hub-avatar-group> 组件。还有,即使你只渲染首字母,注入器里也必须能提供 HttpClient,因为头像服务是无条件请求它的。Angular Material 没有可比的头像组件;mat-card-avatar 是一个样式指令。