Skip to content

Commit 1342aaa

Browse files
committed
Implement internal color animations for avatar avatars
- Replace whole-avatar animations with internal color layer animations - Each work style now affects how colors move, scale, and blend inside the avatar circle - Philosophy determines colors, work style determines internal animation patterns - Iterative: Circular color cycling with rotation and scaling - Detail-oriented: Subtle pulsing and fine position adjustments - Big-picture: Slow, sweeping color movements - Collaborative: Gentle, flowing color transitions - Experimental: Chaotic, unpredictable color movements - Systematic: Precise, rhythmic color adjustments - Background color remains static, two color layers animate independently - Animations use proper TypeScript typing with 'as const' for ease values - Transform origin set to center for proper rotation scaling
1 parent 93ee7f5 commit 1342aaa

File tree

1 file changed

+184
-67
lines changed

1 file changed

+184
-67
lines changed

src/components/Avatar.tsx

Lines changed: 184 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -24,69 +24,172 @@ const PHILOSOPHY_COLORS = {
2424
'grit': ["#FF9A9E", "#FECFEF", "#FECFEF"],
2525
};
2626

27-
// Work style-based animation variants
27+
// Work style-based internal color animation variants
2828
const WORK_STYLE_ANIMATIONS = {
2929
'iterative': {
30-
animate: {
31-
rotate: [0, 360],
32-
scale: [1, 1.1, 1],
30+
// Colors cycle through positions in a circular motion
31+
color1: {
32+
animate: {
33+
translateX: [0, 8, -8, 0],
34+
translateY: [0, -8, 8, 0],
35+
rotate: [0, 90, 180, 270, 360],
36+
scale: [1, 1.1, 0.9, 1],
37+
},
38+
transition: {
39+
duration: 4,
40+
repeat: Infinity,
41+
ease: "linear" as const
42+
}
3343
},
34-
transition: {
35-
duration: 4,
36-
repeat: Infinity,
37-
ease: "linear"
44+
color2: {
45+
animate: {
46+
translateX: [0, -6, 6, 0],
47+
translateY: [0, 6, -6, 0],
48+
rotate: [0, -90, -180, -270, -360],
49+
scale: [1, 0.9, 1.1, 1],
50+
},
51+
transition: {
52+
duration: 4,
53+
repeat: Infinity,
54+
ease: "linear" as const
55+
}
3856
}
3957
},
4058
'detail-oriented': {
41-
animate: {
42-
scale: [1, 1.05, 1],
59+
// Subtle pulsing and fine adjustments
60+
color1: {
61+
animate: {
62+
scale: [1, 1.05, 1],
63+
translateX: [0, 2, -2, 0],
64+
translateY: [0, 1, -1, 0],
65+
},
66+
transition: {
67+
duration: 2,
68+
repeat: Infinity,
69+
ease: "easeInOut" as const
70+
}
4371
},
44-
transition: {
45-
duration: 2,
46-
repeat: Infinity,
47-
ease: "easeInOut"
72+
color2: {
73+
animate: {
74+
scale: [1, 0.98, 1],
75+
translateX: [0, -1, 1, 0],
76+
translateY: [0, -2, 2, 0],
77+
},
78+
transition: {
79+
duration: 2.2,
80+
repeat: Infinity,
81+
ease: "easeInOut" as const
82+
}
4883
}
4984
},
5085
'big-picture': {
51-
animate: {
52-
rotate: [0, 180, 360],
86+
// Slow, sweeping movements
87+
color1: {
88+
animate: {
89+
rotate: [0, 180, 360],
90+
scale: [1, 1.2, 1],
91+
translateX: [0, 10, -10, 0],
92+
},
93+
transition: {
94+
duration: 6,
95+
repeat: Infinity,
96+
ease: "easeInOut" as const
97+
}
5398
},
54-
transition: {
55-
duration: 6,
56-
repeat: Infinity,
57-
ease: "easeInOut"
99+
color2: {
100+
animate: {
101+
rotate: [0, -180, -360],
102+
scale: [1, 0.8, 1],
103+
translateY: [0, -10, 10, 0],
104+
},
105+
transition: {
106+
duration: 6,
107+
repeat: Infinity,
108+
ease: "easeInOut" as const
109+
}
58110
}
59111
},
60112
'collaborative': {
61-
animate: {
62-
x: [0, 5, -5, 0],
63-
y: [0, -5, 5, 0],
113+
// Gentle, flowing movements
114+
color1: {
115+
animate: {
116+
x: [0, 6, -6, 0],
117+
y: [0, -4, 4, 0],
118+
scale: [1, 1.1, 0.9, 1],
119+
},
120+
transition: {
121+
duration: 3,
122+
repeat: Infinity,
123+
ease: "easeInOut" as const
124+
}
64125
},
65-
transition: {
66-
duration: 3,
67-
repeat: Infinity,
68-
ease: "easeInOut"
126+
color2: {
127+
animate: {
128+
x: [0, -4, 4, 0],
129+
y: [0, 6, -6, 0],
130+
scale: [1, 0.9, 1.1, 1],
131+
},
132+
transition: {
133+
duration: 3.5,
134+
repeat: Infinity,
135+
ease: "easeInOut" as const
136+
}
69137
}
70138
},
71139
'experimental': {
72-
animate: {
73-
scale: [1, 1.2, 0.8, 1],
74-
rotate: [0, 90, 180, 270, 360],
140+
// Chaotic, unpredictable movements
141+
color1: {
142+
animate: {
143+
scale: [1, 1.3, 0.7, 1],
144+
rotate: [0, 120, 240, 360],
145+
translateX: [0, 12, -8, 4, 0],
146+
translateY: [0, -8, 12, -4, 0],
147+
},
148+
transition: {
149+
duration: 5,
150+
repeat: Infinity,
151+
ease: "easeInOut" as const
152+
}
75153
},
76-
transition: {
77-
duration: 5,
78-
repeat: Infinity,
79-
ease: "easeInOut"
154+
color2: {
155+
animate: {
156+
scale: [1, 0.8, 1.4, 1],
157+
rotate: [0, -150, -300, -450],
158+
translateX: [0, -10, 6, -12, 0],
159+
translateY: [0, 10, -6, 8, 0],
160+
},
161+
transition: {
162+
duration: 5.5,
163+
repeat: Infinity,
164+
ease: "easeInOut" as const
165+
}
80166
}
81167
},
82168
'systematic': {
83-
animate: {
84-
scale: [1, 1.02, 1],
169+
// Precise, rhythmic movements
170+
color1: {
171+
animate: {
172+
scale: [1, 1.02, 1],
173+
translateX: [0, 3, 0],
174+
translateY: [0, 2, 0],
175+
},
176+
transition: {
177+
duration: 1.5,
178+
repeat: Infinity,
179+
ease: "linear" as const
180+
}
85181
},
86-
transition: {
87-
duration: 1.5,
88-
repeat: Infinity,
89-
ease: "linear"
182+
color2: {
183+
animate: {
184+
scale: [1, 0.98, 1],
185+
translateX: [0, -2, 0],
186+
translateY: [0, -3, 0],
187+
},
188+
transition: {
189+
duration: 1.5,
190+
repeat: Infinity,
191+
ease: "linear" as const
192+
}
90193
}
91194
},
92195
};
@@ -119,15 +222,12 @@ function AvatarFallback({
119222
const filterId = React.useId();
120223

121224
// Get animation props based on working style
122-
const animationProps = workingStyle && WORK_STYLE_ANIMATIONS[workingStyle as keyof typeof WORK_STYLE_ANIMATIONS]
225+
const animationConfig = workingStyle && WORK_STYLE_ANIMATIONS[workingStyle as keyof typeof WORK_STYLE_ANIMATIONS]
123226
? WORK_STYLE_ANIMATIONS[workingStyle as keyof typeof WORK_STYLE_ANIMATIONS]
124-
: {};
227+
: null;
125228

126229
return (
127-
<motion.div
128-
className={`inline-block ${className}`}
129-
{...animationProps}
130-
>
230+
<div className={`inline-block ${className}`}>
131231
<svg
132232
viewBox={`0 0 ${size} ${size}`}
133233
fill="none"
@@ -148,30 +248,47 @@ function AvatarFallback({
148248
<rect width={size} height={size} rx={size * 2} fill="#FFFFFF" />
149249
</mask>
150250
<g mask={`url(#${maskId})`}>
251+
{/* Background color - static */}
151252
<rect width={size} height={size} fill={properties[0].color} />
152-
<path
153-
filter={`url(#${filterId})`}
154-
d="M32.414 59.35L50.376 70.5H72.5v-71H33.728L26.5 13.381l19.057 27.08L32.414 59.35z"
155-
fill={properties[1].color}
156-
transform={`
157-
translate(${properties[1].translateX} ${properties[1].translateY})
158-
rotate(${properties[1].rotate} ${size / 2} ${size / 2})
159-
scale(${properties[1].scale})
160-
`}
161-
/>
162-
<path
163-
filter={`url(#${filterId})`}
253+
254+
{/* First animated color layer */}
255+
<motion.g
256+
{...(animationConfig?.color1 || {})}
164257
style={{
258+
transformOrigin: `${size / 2}px ${size / 2}px`,
259+
}}
260+
>
261+
<path
262+
filter={`url(#${filterId})`}
263+
d="M32.414 59.35L50.376 70.5H72.5v-71H33.728L26.5 13.381l19.057 27.08L32.414 59.35z"
264+
fill={properties[1].color}
265+
transform={`
266+
translate(${properties[1].translateX} ${properties[1].translateY})
267+
rotate(${properties[1].rotate} ${size / 2} ${size / 2})
268+
scale(${properties[1].scale})
269+
`}
270+
/>
271+
</motion.g>
272+
273+
{/* Second animated color layer */}
274+
<motion.g
275+
{...(animationConfig?.color2 || {})}
276+
style={{
277+
transformOrigin: `${size / 2}px ${size / 2}px`,
165278
mixBlendMode: "overlay",
166279
}}
167-
d="M22.216 24L0 46.75l14.108 38.129L78 86l-3.081-59.276-22.378 4.005 12.972 20.186-23.35 27.395L22.215 24z"
168-
fill={properties[2].color}
169-
transform={`
170-
translate(${properties[2].translateX} ${properties[2].translateY})
171-
rotate(${properties[2].rotate} ${size / 2} ${size / 2})
172-
scale(${properties[2].scale})
173-
`}
174-
/>
280+
>
281+
<path
282+
filter={`url(#${filterId})`}
283+
d="M22.216 24L0 46.75l14.108 38.129L78 86l-3.081-59.276-22.378 4.005 12.972 20.186-23.35 27.395L22.215 24z"
284+
fill={properties[2].color}
285+
transform={`
286+
translate(${properties[2].translateX} ${properties[2].translateY})
287+
rotate(${properties[2].rotate} ${size / 2} ${size / 2})
288+
scale(${properties[2].scale})
289+
`}
290+
/>
291+
</motion.g>
175292
</g>
176293
<defs>
177294
<filter
@@ -185,7 +302,7 @@ function AvatarFallback({
185302
</filter>
186303
</defs>
187304
</svg>
188-
</motion.div>
305+
</div>
189306
);
190307
}
191308

0 commit comments

Comments
 (0)