pixel-avatar-lib
Version:
A React component library for generating customizable pixel avatars with DNA encoding system
1,404 lines (1,398 loc) • 37.7 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
PixelAvatar: () => PixelAvatar,
formatDNA: () => formatDNA,
generateRandomDNA: () => generateRandomDNA,
generateRandomDNAString: () => generateRandomDNAString,
isValidDNA: () => isValidDNA,
parseDNA: () => parseDNA
});
module.exports = __toCommonJS(index_exports);
// src/components/pixel-avatar.tsx
var import_react = require("react");
// src/data/character-parts.ts
var characterParts = {
// Hair styles
hair: {
variants: [
// Style 1: Short hair
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 2: Long hair
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 3: Spiky hair
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 4: Bald with headband
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 5: Mohawk
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 6: Curly hair
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 7: Ponytail
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 8: Afro
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 9: Dreadlocks
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 10: Buzz cut
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
]
]
},
// Face styles
face: {
variants: [
// Style 1: Simple face
[
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 2: Smiling face
[
[],
[],
[],
[],
[],
[]
],
// Style 3: Surprised face
[
[],
[],
[],
[],
[],
[]
],
// Style 4: Angry face
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 5: Cool face with sunglasses
[
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 6: Winking face
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 7: Sleepy face
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 8: Crying face
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 9: Laughing face
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 10: Masked face
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
]
]
},
// Neck styles
neck: {
variants: [
// Style 1: Simple neck
[
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 2: Neck with collar
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 3: Neck with bowtie
[
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 4: Neck with necklace
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 5: Neck with scarf
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 6: Neck with chain
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 7: Neck with tattoo
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 8: Neck with bandana
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 9: Neck with choker
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 10: Neck with high collar
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
]
]
},
// Clothing styles
clothing: {
variants: [
// Style 1: T-shirt
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 2: Hoodie
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 3: Suit
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 4: Tank top
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 5: Armor
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 6: Vest
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 7: Jacket
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 8: Dress shirt
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 9: Sweater
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 10: Polo shirt
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
]
]
},
// Hands styles
hands: {
variants: [
// Style 1: Simple hands
[
[],
[],
[],
[]
],
// Style 2: Gloved hands
[
[],
[],
[],
[]
],
// Style 3: Robot hands
[
[],
[],
[],
[]
],
// Style 4: Claws
[
[],
[],
[],
[],
[],
[]
],
// Style 5: Paws
[
[],
[],
[],
[],
[],
[]
],
// Style 6: Fingerless gloves
[
[],
[],
[],
[]
],
// Style 7: Mittens
[
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 8: Bandaged hands
[
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 9: Cybernetic hands
[
[],
[],
[],
[],
[],
[]
],
// Style 10: Gauntlets
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
]
]
},
// Item styles (held in hands)
item: {
variants: [
// Style 1: Sword
[
[],
[],
[],
[],
[],
[],
[]
],
// Style 2: Shield
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 3: Magic staff
[
[],
[],
[],
[],
[],
[],
[]
],
// Style 4: Potion
[
[],
[],
[],
[],
[],
[]
],
// Style 5: Bow
[
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 6: Axe
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 7: Hammer
[
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 8: Spear
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 9: Crystal orb
[
[],
[],
[],
[],
[],
[],
[],
[],
[]
],
// Style 10: Book
[
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]
]
]
}
};
// src/utils/dna.ts
function generateRandomDNA() {
return {
hair: Math.floor(Math.random() * 10),
face: Math.floor(Math.random() * 10),
neck: Math.floor(Math.random() * 10),
clothing: Math.floor(Math.random() * 10),
hands: Math.floor(Math.random() * 10),
item: Math.floor(Math.random() * 10)
};
}
function parseDNA(dnaString) {
if (!dnaString || typeof dnaString !== "string") {
throw new Error("DNA string is required and must be a string");
}
const parts = dnaString.split("-");
if (parts.length !== 6) {
throw new Error("DNA string must contain exactly 6 parts separated by hyphens");
}
const [hair, face, neck, clothing, hands, item] = parts.map((part) => {
const num = parseInt(part, 10);
if (isNaN(num) || num < 0 || num > 9) {
throw new Error(`Invalid DNA part: ${part}. Each part must be a number between 0-9`);
}
return num;
});
return { hair, face, neck, clothing, hands, item };
}
function formatDNA(dna) {
return `${dna.hair}-${dna.face}-${dna.neck}-${dna.clothing}-${dna.hands}-${dna.item}`;
}
function isValidDNA(dnaString) {
try {
parseDNA(dnaString);
return true;
} catch {
return false;
}
}
function generateRandomDNAString() {
return formatDNA(generateRandomDNA());
}
// src/components/pixel-avatar.tsx
var import_jsx_runtime = require("react/jsx-runtime");
var PixelAvatar = (0, import_react.forwardRef)(
({
dna,
size = 256,
className,
pixelSize = 6,
// 增加默认像素尺寸,让头像更大
style,
backgroundColor = "#ffffff",
offsetY = 0,
offsetX = -3.5
// 向左移动头像以在24x24画布中水平居中
}, ref) => {
const canvasRef = (0, import_react.useRef)(null);
const selectedParts = (0, import_react.useMemo)(() => {
try {
return parseDNA(dna);
} catch (error) {
console.warn("Invalid DNA string, using default avatar:", error);
return { hair: 0, face: 0, neck: 0, clothing: 0, hands: 0, item: 0 };
}
}, [dna]);
const canvasSize = (0, import_react.useMemo)(() => 24 * pixelSize, [pixelSize]);
(0, import_react.useEffect)(() => {
const canvas = ref ? ref.current : canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, canvasSize, canvasSize);
const drawOrder = ["neck", "clothing", "hands", "hair", "face", "item"];
drawOrder.forEach((partKey) => {
const partIndex = selectedParts[partKey];
const partData = characterParts[partKey];
if (partIndex >= 0 && partIndex < partData.variants.length) {
const pixelData = partData.variants[partIndex];
pixelData.forEach((pixel) => {
const [x, y, color] = pixel;
const adjustedX = x + offsetX;
const adjustedY = y + offsetY;
if (adjustedX >= 0 && adjustedX < 24 && adjustedY >= 0 && adjustedY < 24) {
ctx.fillStyle = color;
ctx.fillRect(
adjustedX * pixelSize,
adjustedY * pixelSize,
pixelSize,
pixelSize
);
}
});
}
});
}, [selectedParts, pixelSize, canvasSize, backgroundColor, offsetY, offsetX, ref]);
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"canvas",
{
ref: ref || canvasRef,
width: canvasSize,
height: canvasSize,
className,
style: {
width: `${size}px`,
height: `${size}px`,
imageRendering: "pixelated",
backgroundColor,
// 设置CSS背景颜色作为后备
...style
}
}
);
}
);
PixelAvatar.displayName = "PixelAvatar";
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
PixelAvatar,
formatDNA,
generateRandomDNA,
generateRandomDNAString,
isValidDNA,
parseDNA
});