Перейти к содержимому

avatar —— Аватар

Компонент аватара работает с тремя формами контента: изображение, текст и иконка — и должен корректно деградировать, если изображение не загрузилось или адрес некорректен. 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», а реальные ошибки загрузки перехватываются событием error элемента img (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') // true
isImageSource('data:image/png;base64,...') // true
isImageSource(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>
// React
const text = getAvatarText(name)
const showImg = isImageSource(src) && !imgError
return (
<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