refactor(web): 使用 DiceBear 生成默认头像
This commit is contained in:
parent
948a5fda9e
commit
63285b8068
@ -1,7 +1,7 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { generatePixelAvatar } from '../pixelAvatar.js'
|
||||
|
||||
const decodeAvatarSvg = (avatar) => decodeURIComponent(avatar.replace('data:image/svg+xml,', ''))
|
||||
const DICEBEAR_GLYPHS_AVATAR_BASE_URL = 'https://api.dicebear.com/10.x/glyphs/svg'
|
||||
|
||||
const run = () => {
|
||||
{
|
||||
@ -20,15 +20,22 @@ const run = () => {
|
||||
|
||||
{
|
||||
const avatar = generatePixelAvatar('user-003')
|
||||
assert.ok(avatar.startsWith('data:image/svg+xml,'), 'Should return an SVG data URL')
|
||||
console.log('T3 Data URL prefix: PASS')
|
||||
assert.equal(
|
||||
avatar,
|
||||
`${DICEBEAR_GLYPHS_AVATAR_BASE_URL}?seed=user-003`,
|
||||
'Should return a DiceBear glyphs avatar URL'
|
||||
)
|
||||
console.log('T3 DiceBear URL: PASS')
|
||||
}
|
||||
|
||||
{
|
||||
const svg = decodeAvatarSvg(generatePixelAvatar('user-004'))
|
||||
assert.ok(svg.includes('<svg'), 'Decoded output should contain an SVG tag')
|
||||
assert.ok(svg.includes('<rect'), 'Decoded output should contain pixel rects')
|
||||
console.log('T4 Decodable SVG: PASS')
|
||||
const avatar = generatePixelAvatar(' user/中文 ')
|
||||
assert.equal(
|
||||
avatar,
|
||||
`${DICEBEAR_GLYPHS_AVATAR_BASE_URL}?seed=user%2F%E4%B8%AD%E6%96%87`,
|
||||
'Seed should be trimmed and URL encoded'
|
||||
)
|
||||
console.log('T4 Encoded seed: PASS')
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@ -1,20 +1,4 @@
|
||||
const DEFAULT_SIZE = 64
|
||||
const GRID_SIZE = 5
|
||||
const MIRROR_COLUMNS = 3
|
||||
const DEFAULT_BACKGROUND = '#f3f4f6'
|
||||
|
||||
const COLOR_PALETTE = [
|
||||
'#265c96',
|
||||
'#3996ae',
|
||||
'#13c2c2',
|
||||
'#52c41a',
|
||||
'#faad14',
|
||||
'#ff7a45',
|
||||
'#ff4d4f',
|
||||
'#9254de',
|
||||
'#597ef7',
|
||||
'#4f4f4f'
|
||||
]
|
||||
const DICEBEAR_GLYPHS_AVATAR_BASE_URL = 'https://api.dicebear.com/10.x/glyphs/svg'
|
||||
|
||||
const normalizeSeed = (id) => {
|
||||
if (id === null || id === undefined || String(id).trim() === '') {
|
||||
@ -23,52 +7,7 @@ const normalizeSeed = (id) => {
|
||||
return String(id).trim()
|
||||
}
|
||||
|
||||
const hashSeed = (seed) => {
|
||||
let hash = 2166136261
|
||||
|
||||
for (let i = 0; i < seed.length; i += 1) {
|
||||
hash ^= seed.charCodeAt(i)
|
||||
hash = Math.imul(hash, 16777619)
|
||||
}
|
||||
|
||||
return hash >>> 0
|
||||
}
|
||||
|
||||
const buildCells = (hash) => {
|
||||
const cells = []
|
||||
|
||||
for (let row = 0; row < GRID_SIZE; row += 1) {
|
||||
for (let col = 0; col < MIRROR_COLUMNS; col += 1) {
|
||||
const bitIndex = row * MIRROR_COLUMNS + col
|
||||
const filled = ((hash >>> bitIndex) & 1) === 1
|
||||
|
||||
if (!filled) continue
|
||||
|
||||
cells.push([col, row])
|
||||
|
||||
const mirroredCol = GRID_SIZE - col - 1
|
||||
if (mirroredCol !== col) {
|
||||
cells.push([mirroredCol, row])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cells.length === 0) {
|
||||
cells.push([2, 2])
|
||||
}
|
||||
|
||||
return cells
|
||||
}
|
||||
|
||||
export const generatePixelAvatar = (id) => {
|
||||
const seed = normalizeSeed(id)
|
||||
const hash = hashSeed(seed)
|
||||
const color = COLOR_PALETTE[(hash >>> 16) % COLOR_PALETTE.length]
|
||||
const cells = buildCells(hash)
|
||||
.map(([x, y]) => `<rect x="${x}" y="${y}" width="1" height="1"/>`)
|
||||
.join('')
|
||||
|
||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${DEFAULT_SIZE}" height="${DEFAULT_SIZE}" viewBox="0 0 ${GRID_SIZE} ${GRID_SIZE}" shape-rendering="crispEdges"><rect width="${GRID_SIZE}" height="${GRID_SIZE}" fill="${DEFAULT_BACKGROUND}"/><g fill="${color}">${cells}</g></svg>`
|
||||
|
||||
return `data:image/svg+xml,${encodeURIComponent(svg)}`
|
||||
return `${DICEBEAR_GLYPHS_AVATAR_BASE_URL}?seed=${encodeURIComponent(seed)}`
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user