Skip to content

Commit 49f8828

Browse files
committed
feat: support seed generating
1 parent c1db8e8 commit 49f8828

File tree

4 files changed

+94
-39
lines changed

4 files changed

+94
-39
lines changed

README.md

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ or
3636
yarn add react-nice-avatar
3737
```
3838

39+
or
40+
41+
```sh
42+
pnpm i react-nice-avatar
43+
```
44+
3945
## Usage
4046

4147
1. Import the component.
@@ -126,15 +132,3 @@ The options can be passed into genConfig or as React props
126132
## License
127133

128134
Released under [MIT](/LICENSE) by [@dapi-labs](https://github.com/dapi-labs).
129-
130-
---
131-
132-
<br />
133-
134-
<div align="center">
135-
<a href="https://dapiok.com">
136-
<img src="https://user-images.githubusercontent.com/5305874/131276202-ee5f6941-531c-4c01-bbc4-3ff8aca0e629.png" width="260" alt="dapi">
137-
</a>
138-
</div>
139-
140-
<br />

demo/tsconfig.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
"target": "es5", // specify ECMAScript target version
99
"allowJs": true, // allow a partial TypeScript and JavaScript codebase
1010
"allowSyntheticDefaultImports": true, // import default
11-
"baseUrl": "."
11+
"baseUrl": ".",
12+
"path": {
13+
"react-nice-avatar/*": "../src/*"
14+
}
1215
},
1316
"include": [
1417
"./src",

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export interface NiceAvatarProps extends AvatarConfig {
4747
shape?: "circle" | "rounded" | "square"
4848
}
4949

50-
export type GenConfigFunc = (config?: AvatarFullConfig) => Required<AvatarFullConfig>
50+
export type GenConfigFunc = (config?: AvatarFullConfig | string) => Required<AvatarFullConfig>
5151

5252
export declare const genConfig: GenConfigFunc
5353

src/utils.ts

Lines changed: 83 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ interface PickRandomOpt<T> {
2121
avoidList?: T[],
2222
usually?: T[]
2323
}
24+
2425
type PickRandomFromList = <T>(data: T[], opt?: PickRandomOpt<T | undefined>) => T;
26+
2527
export const pickRandomFromList: PickRandomFromList = (data, { avoidList = [], usually = [] } = {}) => {
2628
// Filter out avoid options
2729
const avoidSet = new Set(
@@ -66,6 +68,7 @@ interface DefaultOptions {
6668
bgColor: string[],
6769
gradientBgColor: string[]
6870
}
71+
6972
export const defaultOptions: DefaultOptions = {
7073
sex: ["man", "woman"],
7174
faceColor: ["#F9C9B6", "#AC6651"],
@@ -93,21 +96,61 @@ export const defaultOptions: DefaultOptions = {
9396
"linear-gradient(45deg, #56b5f0 0%, #45ccb5 100%)"
9497
]
9598
};
99+
100+
const stringToHashCode = (str: string) : number => {
101+
if (str.length === 0) return 0
102+
let hash = 0
103+
let char
104+
for (let i = 0; i < str.length; i++) {
105+
char = str.charCodeAt(i);
106+
hash = ((hash << 5) - hash) + char;
107+
hash |= 0; // Convert to 32bit integer
108+
}
109+
return Math.abs(hash);
110+
}
111+
112+
type PickByHashCodeOpts = {
113+
avoidList?: string[],
114+
usually?: string[]
115+
}
116+
const pickByHashCode = (code: number, type: keyof DefaultOptions, opts?: PickByHashCodeOpts): string => {
117+
const avoidList = opts && opts.avoidList || []
118+
const usually = opts && opts.usually || []
119+
120+
// Filter out avoid options
121+
const avoidSet = new Set<string>(avoidList)
122+
let myDefaultOptions = defaultOptions[type].filter(item => !avoidSet.has(item))
123+
124+
// Increase selecting possibility of usually options
125+
myDefaultOptions = usually
126+
.filter(Boolean)
127+
.reduce(
128+
(acc, cur) => acc.concat(new Array(15).fill(cur)),
129+
[] as string[]
130+
)
131+
.concat(myDefaultOptions)
132+
133+
const index = code % myDefaultOptions.length
134+
return myDefaultOptions[index]
135+
}
136+
96137
export const genConfig: GenConfigFunc = (userConfig = {}) => {
138+
const isSeedConfig = typeof userConfig === 'string';
139+
const hashCode = isSeedConfig && stringToHashCode(userConfig) || 0
97140
const response = {} as Required<AvatarFullConfig>;
98-
response.sex = userConfig.sex || pickRandomFromList(defaultOptions.sex);
99-
response.faceColor = userConfig.faceColor || pickRandomFromList(defaultOptions.faceColor);
100-
response.earSize = userConfig.earSize || pickRandomFromList(defaultOptions.earSize);
101-
response.eyeStyle = userConfig.eyeStyle || pickRandomFromList(defaultOptions.eyeStyle);
102-
response.noseStyle = userConfig.noseStyle || pickRandomFromList(defaultOptions.noseStyle);
103-
response.mouthStyle = userConfig.mouthStyle || pickRandomFromList(defaultOptions.mouthStyle);
104-
response.shirtStyle = userConfig.shirtStyle || pickRandomFromList(defaultOptions.shirtStyle);
105-
response.glassesStyle = userConfig.glassesStyle || pickRandomFromList(defaultOptions.glassesStyle, { usually: ["none"] });
141+
response.sex = isSeedConfig ? pickByHashCode(hashCode, 'sex') as Sex : (userConfig.sex || pickRandomFromList(defaultOptions.sex));
142+
response.faceColor = isSeedConfig ? pickByHashCode(hashCode, 'faceColor') : (userConfig.faceColor || pickRandomFromList(defaultOptions.faceColor));
143+
response.earSize = isSeedConfig ? pickByHashCode(hashCode, 'earSize') as EarSize : (userConfig.earSize || pickRandomFromList(defaultOptions.earSize));
144+
response.eyeStyle = isSeedConfig ? pickByHashCode(hashCode, 'eyeStyle') as EyeStyle : (userConfig.eyeStyle || pickRandomFromList(defaultOptions.eyeStyle));
145+
response.noseStyle = isSeedConfig ? pickByHashCode(hashCode, 'noseStyle') as NoseStyle : (userConfig.noseStyle || pickRandomFromList(defaultOptions.noseStyle));
146+
response.mouthStyle = isSeedConfig ? pickByHashCode(hashCode, 'mouthStyle') as MouthStyle : (userConfig.mouthStyle || pickRandomFromList(defaultOptions.mouthStyle));
147+
response.shirtStyle = isSeedConfig ? pickByHashCode(hashCode, 'shirtStyle') as ShirtStyle : (userConfig.shirtStyle || pickRandomFromList(defaultOptions.shirtStyle));
148+
response.glassesStyle = isSeedConfig ? pickByHashCode(hashCode, 'glassesStyle', { usually: ["none"] }) as GlassesStyle : (userConfig.glassesStyle || pickRandomFromList(defaultOptions.glassesStyle, { usually: ["none"] }));
106149

107150
// Hair
108151
let hairColorAvoidList: string[] = [];
109152
let hairColorUsually: string[] = [];
110-
if (!userConfig.hairColor) {
153+
if (isSeedConfig || !userConfig.hairColor) {
111154
switch (response.sex) {
112155
case "woman": {
113156
hairColorAvoidList = response.faceColor === defaultOptions.faceColor[1] && ["#77311D"] || [];
@@ -118,48 +161,63 @@ export const genConfig: GenConfigFunc = (userConfig = {}) => {
118161
}
119162
}
120163
}
121-
response.hairColor = userConfig.hairColor || pickRandomFromList(defaultOptions.hairColor, {
122-
avoidList: hairColorAvoidList,
123-
usually: hairColorUsually
124-
});
164+
response.hairColor = isSeedConfig
165+
? pickByHashCode(hashCode, 'hairColor', {
166+
avoidList: hairColorAvoidList,
167+
usually: hairColorUsually
168+
})
169+
: (userConfig.hairColor || pickRandomFromList(defaultOptions.hairColor, {
170+
avoidList: hairColorAvoidList,
171+
usually: hairColorUsually
172+
}));
125173

126-
let myHairStyle = userConfig.hairStyle;
127-
if (!myHairStyle) {
174+
if (isSeedConfig || !userConfig.hairStyle) {
128175
switch (response.sex) {
129176
case "man": {
130-
myHairStyle = pickRandomFromList(defaultOptions.hairStyleMan, { usually: ["normal", "thick"] });
177+
response.hairStyle = isSeedConfig
178+
? pickByHashCode(hashCode, 'hairStyleMan', { usually: ["normal", "thick"] }) as HairStyleMan
179+
: pickRandomFromList(defaultOptions.hairStyleMan, { usually: ["normal", "thick"] });
131180
break;
132181
}
133182
case "woman": {
134-
myHairStyle = pickRandomFromList(defaultOptions.hairStyleWoman);
183+
response.hairStyle = isSeedConfig
184+
? pickByHashCode(hashCode, 'hairStyleWoman') as HairStyleWoman
185+
: pickRandomFromList(defaultOptions.hairStyleWoman);
135186
break;
136187
}
137188
}
138189
}
139-
response.hairStyle = myHairStyle;
140190

141191
// Hat
142-
response.hatStyle = userConfig.hatStyle || pickRandomFromList(defaultOptions.hatStyle, { usually: ["none"] });
143-
response.hatColor = userConfig.hatColor || pickRandomFromList(defaultOptions.hatColor);
192+
response.hatStyle = isSeedConfig
193+
? pickByHashCode(hashCode, 'hatStyle', { usually: ["none"] }) as HatStyle
194+
: (userConfig.hatStyle || pickRandomFromList(defaultOptions.hatStyle, { usually: ["none"] }));
195+
response.hatColor = isSeedConfig ? pickByHashCode(hashCode, 'hatColor') : (userConfig.hatColor || pickRandomFromList(defaultOptions.hatColor));
144196
const _hairOrHatColor = response.hatStyle === "none" && response.hairColor || response.hatColor;
145197

146198
// Eyebrow
147-
if (userConfig.eyeBrowStyle) {
199+
if (!isSeedConfig && userConfig.eyeBrowStyle) {
148200
response.eyeBrowStyle = userConfig.eyeBrowStyle
149201
} else {
150202
response.eyeBrowStyle = response.sex === "woman"
151-
? pickRandomFromList(defaultOptions.eyeBrowWoman)
203+
? isSeedConfig
204+
? pickByHashCode(hashCode, 'eyeBrowWoman') as EyeBrowStyle
205+
: pickRandomFromList(defaultOptions.eyeBrowWoman)
152206
: "up"
153207
}
154208

155209
// Shirt color
156-
response.shirtColor = userConfig.shirtColor || pickRandomFromList(defaultOptions.shirtColor, { avoidList: [_hairOrHatColor] });
210+
response.shirtColor = isSeedConfig
211+
? pickByHashCode(hashCode, 'shirtColor', { avoidList: [_hairOrHatColor] })
212+
: userConfig.shirtColor || pickRandomFromList(defaultOptions.shirtColor, { avoidList: [_hairOrHatColor] });
157213

158214
// Background color
159-
if (userConfig.isGradient) {
215+
if (!isSeedConfig && userConfig.isGradient) {
160216
response.bgColor = userConfig.bgColor || pickRandomFromList(defaultOptions.gradientBgColor);
161217
} else {
162-
response.bgColor = userConfig.bgColor || pickRandomFromList(defaultOptions.bgColor, { avoidList: [_hairOrHatColor, response.shirtColor] });
218+
response.bgColor = isSeedConfig
219+
? pickByHashCode(hashCode, 'bgColor', { avoidList: [_hairOrHatColor, response.shirtColor] })
220+
: userConfig.bgColor || pickRandomFromList(defaultOptions.bgColor, { avoidList: [_hairOrHatColor, response.shirtColor] });
163221
}
164222

165223
return response;

0 commit comments

Comments
 (0)