avatar —— 頭像
This content is not available in your language yet.
頭像元件要處理三種內容形態:圖片、文字、圖示,且需要在圖片載入失敗或位址非法時優雅降級。core/avatar 把「文字提取」和「圖片來源判定」做成純函式——這兩個判定跨端必須完全一致,否則會出現「Vue3 顯示文字、uniapp 顯示破圖」的分歧。
- 各端實作
LhAvatar:圖片可用時渲染img,否則渲染getAvatarText提取的首字,都沒有時渲染圖示佔位。 - 使用者系統裡取「姓名首字」做預設頭像的場景。
- 不該用它:圖片裁剪、上傳。這是
LhUpload+ 業務邏輯的事。
import { getAvatarClassNames, getAvatarText, isImageSource } from '@lh-ui/share/core/avatar'import type { LhAvatarState, LhAvatarShape } from '@lh-ui/share/core/avatar'getAvatarText 用 codePointAt(0) + String.fromCodePoint 而不是 str[0],這是一個容易忽略的坑:'😀'[0] 得到的是半個代理對(\uD83D),渲染出來是亂碼方塊。用碼點提取,emoji 和增補平面字元(很多生僻漢字)都安全。
isImageSource 的正則只做寬鬆的可用性判定,不做安全驗證:匹配 http(s)://、data:image/、絕對路徑 /、相對路徑 ./、../ 開頭的字串視為圖片位址。它的用途是「決定要不要嘗試渲染 img」,真正的載入失敗由 img 的 error 事件兜底(UI 層切到文字狀態)。
| 函式 | 簽名 | 傳回 | 作用 |
|---|---|---|---|
getAvatarClassNames |
(state: LhAvatarState) => string[] |
類別名陣列 | 形狀與尺寸修飾 |
getAvatarText |
(source: string) => string |
首字或 '' |
從文字提取展示字 |
isImageSource |
(src: string | undefined) => boolean |
布林 | 圖片位址是否值得嘗試渲染 |
邊界細節:
getAvatarText('')→'';getAvatarText(' 水墨 ')→'水'(先 trim);getAvatarText('Alice')→'A'。getAvatarText在codePointAt(0)傳回 undefined(空串已 trim 掉,理論上不會觸發)時傳回''。isImageSource(undefined)→false;isImageSource('')→false。
export type LhAvatarShape = 'circle' | 'square'
export interface LhAvatarState { size: LhComponentSize shape: LhAvatarShape}core/avatar 不輸出 aria 屬性包,因為頭像的處理方式取決於內容形態:
- 圖片頭像:用原生
<img>,alt必填(如「張三的頭像」)。不要留空 alt——空 alt 表示「裝飾性圖片」,而頭像通常承載身份資訊。 - 文字頭像:文字本身就是可讀內容,無需額外處理。
- 圖示頭像(匿名使用者佔位):圖示走
getIconAria({ decorative: false, label: '匿名用户' })。
import { getAvatarText, isImageSource } from '@lh-ui/share/core/avatar'
getAvatarText('王小明') // '王'getAvatarText('Alice Smith') // 'A'getAvatarText('😀哈哈') // '😀'isImageSource('https://a.com/1.png') // trueisImageSource('data:image/png;base64,...') // trueisImageSource(undefined) // false<!-- Vue3 --><div :class="getAvatarClassNames({ size, shape })"> <img v-if="isImage && !error" :src="src" :alt="alt" @error="error = true" /> <span v-else-if="text" class="lh-avatar__text">{{ text }}</span> <LhIcon v-else name="user" :decorative="false" label="匿名用户" /></div>// Reactconst text = getAvatarText(name)const showImg = isImageSource(src) && !imgErrorreturn ( <div className={getAvatarClassNames({ size, shape }).join(' ')}> {showImg ? <img src={src} alt={alt} onError={() => setImgError(true)} /> : text ? <span className="lh-avatar__text">{text}</span> : <LhIcon name="user" decorative={false} label="匿名用户" />} </div>)// uniapp:<image> 元件有 @error 事件,邏輯同上;// 文字頭像在非 h5 端注意字型回退(生僻字可能缺字形)| 類別名 | 語義 |
|---|---|
.lh-avatar |
基礎類:固定寬高、溢出隱藏、置中 |
.lh-avatar--{mini|small|medium|large|x-large} |
尺寸檔 |
.lh-avatar--circle / .lh-avatar--square |
形狀(圓角程度) |
.lh-avatar__img |
圖片:鋪滿 + object-fit |
.lh-avatar__text |
文字:置中、白色、按尺寸縮放 |
.lh-avatar__icon |
圖示佔位 |
頭像底色預設是主色令牌;多頭像場景(如頭像組)各端 UI 層可按名字雜湊上色,core 不參與。
- 不要用
str[0]或str.charAt(0)在 UI 層自己取首字——emoji 與生僻字會截斷成亂碼,統一用getAvatarText。 isImageSource傳回 true 不代表圖片存在,只代表「值得一試」。onError兜底是必須的。square不是直角,而是小圓角(視覺調和),直角頭像在設計中顯得過於尖銳。- 圖片頭像的
object-fit預設cover,人臉可能被裁掉邊緣——業務上常需要object-position微調,這在各端 UI 層處理。
@lh-ui/share/core/avatar → getAvatarText( )返回值
(填好参数后点运行)Copyright © 2024-2026 FengMo WenXue