Skip to content

Commit a2dab1b

Browse files
committed
Enhance AnimationSystem with origin-based particle positioning and improved animation effects. Update confetti colors and adjust launch parameters for stars, confetti, hearts, sparkles, and balloons. Refactor CSS for better visual effects and transitions.
1 parent a816e8a commit a2dab1b

File tree

2 files changed

+187
-103
lines changed

2 files changed

+187
-103
lines changed

animations.js

Lines changed: 86 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class AnimationSystem {
99
this.launchBalloons.bind(this),
1010
this.launchFireworks.bind(this),
1111
];
12-
this.confettiColors = ['#ef4444', '#f59e0b', '#22c55e', '#3b82f6', '#a855f7', '#ec4899'];
12+
this.confettiColors = ['#22c55e', '#14b8a6', '#3b82f6', '#f59e0b', '#f97316', '#ec4899'];
1313
}
1414

1515
getStarsContainer() {
@@ -41,46 +41,82 @@ class AnimationSystem {
4141
return particle;
4242
}
4343

44-
createStar() {
44+
getOriginMetrics(element) {
45+
if (!element?.getBoundingClientRect) {
46+
return {
47+
x: this.viewportWidth() / 2,
48+
y: this.viewportHeight() * 0.45,
49+
width: 120,
50+
height: 56,
51+
};
52+
}
53+
54+
const rect = element.getBoundingClientRect();
55+
return {
56+
x: rect.left + rect.width / 2,
57+
y: rect.top + rect.height / 2,
58+
width: rect.width,
59+
height: rect.height,
60+
};
61+
}
62+
63+
setParticleOrigin(particle, origin, spreadX = 0.28, spreadY = 0.2) {
64+
particle.style.left = `${origin.x + this.randomBetween(-origin.width * spreadX, origin.width * spreadX)}px`;
65+
particle.style.top = `${origin.y + this.randomBetween(-origin.height * spreadY, origin.height * spreadY)}px`;
66+
}
67+
68+
createStar(origin) {
4569
const star = this.createParticle('star', '⭐');
46-
star.style.left = `${Math.random() * this.viewportWidth()}px`;
47-
star.style.fontSize = `${this.randomBetween(20, 40)}px`;
48-
star.style.animationDuration = `${this.randomBetween(0.7, 1.8)}s`;
70+
this.setParticleOrigin(star, origin);
71+
star.style.fontSize = `${this.randomBetween(18, 28)}px`;
72+
star.style.setProperty('--dx', `${this.randomBetween(-90, 90)}px`);
73+
star.style.setProperty('--dy', `${this.randomBetween(-120, -48)}px`);
74+
star.style.setProperty('--twirl', `${this.randomBetween(-180, 180)}deg`);
75+
star.style.animationDuration = `${this.randomBetween(0.65, 0.95)}s`;
4976
return star;
5077
}
5178

52-
createConfetti() {
79+
createConfetti(origin) {
5380
const confetti = this.createParticle('confetti');
54-
confetti.style.left = `${Math.random() * this.viewportWidth()}px`;
55-
confetti.style.top = '-20px';
56-
confetti.style.backgroundColor = this.pickRandom(this.confettiColors);
57-
confetti.style.width = `${this.randomBetween(8, 14)}px`;
58-
confetti.style.height = `${this.randomBetween(14, 24)}px`;
59-
confetti.style.animationDuration = `${this.randomBetween(2.2, 3.5)}s`;
81+
const color = this.pickRandom(this.confettiColors);
82+
this.setParticleOrigin(confetti, origin, 0.2, 0.15);
83+
confetti.style.background = `linear-gradient(180deg, rgba(255, 255, 255, 0.9) 0%, ${color} 28%, ${color} 100%)`;
84+
confetti.style.width = `${this.randomBetween(7, 12)}px`;
85+
confetti.style.height = `${this.randomBetween(12, 18)}px`;
86+
confetti.style.borderRadius = `${this.randomBetween(3, 6)}px`;
87+
confetti.style.setProperty('--dx', `${this.randomBetween(-140, 140)}px`);
88+
confetti.style.setProperty('--dy', `${this.randomBetween(-90, -24)}px`);
89+
confetti.style.setProperty('--fall', `${this.randomBetween(70, 150)}px`);
90+
confetti.style.setProperty('--twirl', `${this.randomBetween(-320, 320)}deg`);
91+
confetti.style.animationDuration = `${this.randomBetween(0.75, 1.05)}s`;
6092
return confetti;
6193
}
6294

63-
createHeart() {
95+
createHeart(origin) {
6496
const heart = this.createParticle('heart', this.pickRandom(['💙', '💚', '💛', '💖']));
65-
heart.style.left = `${Math.random() * this.viewportWidth()}px`;
66-
heart.style.top = `${this.randomBetween(this.viewportHeight() * 0.65, this.viewportHeight() * 0.9)}px`;
67-
heart.style.animationDuration = `${this.randomBetween(1.8, 2.4)}s`;
97+
this.setParticleOrigin(heart, origin, 0.15, 0.12);
98+
heart.style.setProperty('--dx', `${this.randomBetween(-60, 60)}px`);
99+
heart.style.setProperty('--dy', `${this.randomBetween(-165, -95)}px`);
100+
heart.style.animationDuration = `${this.randomBetween(0.9, 1.15)}s`;
68101
return heart;
69102
}
70103

71-
createSparkle() {
104+
createSparkle(origin) {
72105
const sparkle = this.createParticle('sparkle', this.pickRandom(['✨', '✦', '✧']));
73-
sparkle.style.left = `${Math.random() * this.viewportWidth()}px`;
74-
sparkle.style.top = `${this.randomBetween(this.viewportHeight() * 0.2, this.viewportHeight() * 0.75)}px`;
75-
sparkle.style.animationDuration = `${this.randomBetween(1.0, 1.8)}s`;
106+
this.setParticleOrigin(sparkle, origin, 0.22, 0.18);
107+
sparkle.style.setProperty('--dx', `${this.randomBetween(-80, 80)}px`);
108+
sparkle.style.setProperty('--dy', `${this.randomBetween(-95, -35)}px`);
109+
sparkle.style.setProperty('--twirl', `${this.randomBetween(-120, 120)}deg`);
110+
sparkle.style.animationDuration = `${this.randomBetween(0.55, 0.85)}s`;
76111
return sparkle;
77112
}
78113

79-
createBalloon() {
114+
createBalloon(origin) {
80115
const balloon = this.createParticle('balloon', this.pickRandom(['🎈', '🎈', '🎈', '🎉']));
81-
balloon.style.left = `${Math.random() * this.viewportWidth()}px`;
82-
balloon.style.top = `${this.viewportHeight() + this.randomBetween(10, 120)}px`;
83-
balloon.style.animationDuration = `${this.randomBetween(3.5, 4.8)}s`;
116+
this.setParticleOrigin(balloon, origin, 0.18, 0.12);
117+
balloon.style.setProperty('--dx', `${this.randomBetween(-40, 40)}px`);
118+
balloon.style.setProperty('--dy', `${this.randomBetween(-190, -120)}px`);
119+
balloon.style.animationDuration = `${this.randomBetween(0.95, 1.2)}s`;
84120
return balloon;
85121
}
86122

@@ -100,54 +136,54 @@ class AnimationSystem {
100136
setTimeout(() => particle.remove(), removeAfterMs);
101137
}
102138

103-
launchStars(container) {
104-
for (let index = 0; index < 10; index += 1) {
105-
this.appendAndCleanup(container, this.createStar(), 2200);
139+
launchStars(container, origin) {
140+
for (let index = 0; index < 8; index += 1) {
141+
this.appendAndCleanup(container, this.createStar(origin), 1200);
106142
}
107143
}
108144

109-
launchConfetti(container) {
110-
for (let index = 0; index < 24; index += 1) {
111-
this.appendAndCleanup(container, this.createConfetti(), 3800);
145+
launchConfetti(container, origin) {
146+
for (let index = 0; index < 18; index += 1) {
147+
this.appendAndCleanup(container, this.createConfetti(origin), 1400);
112148
}
113149
}
114150

115-
launchHearts(container) {
116-
for (let index = 0; index < 12; index += 1) {
117-
this.appendAndCleanup(container, this.createHeart(), 2600);
151+
launchHearts(container, origin) {
152+
for (let index = 0; index < 8; index += 1) {
153+
this.appendAndCleanup(container, this.createHeart(origin), 1400);
118154
}
119155
}
120156

121-
launchSparkles(container) {
122-
for (let index = 0; index < 16; index += 1) {
123-
this.appendAndCleanup(container, this.createSparkle(), 2200);
157+
launchSparkles(container, origin) {
158+
for (let index = 0; index < 12; index += 1) {
159+
this.appendAndCleanup(container, this.createSparkle(origin), 1000);
124160
}
125161
}
126162

127-
launchBalloons(container) {
128-
for (let index = 0; index < 8; index += 1) {
129-
this.appendAndCleanup(container, this.createBalloon(), 5200);
163+
launchBalloons(container, origin) {
164+
for (let index = 0; index < 5; index += 1) {
165+
this.appendAndCleanup(container, this.createBalloon(origin), 1500);
130166
}
131167
}
132168

133-
launchFireworks(container) {
134-
const burstCount = 3;
135-
const sparksPerBurst = 10;
169+
launchFireworks(container, origin) {
170+
const burstCount = 2;
171+
const sparksPerBurst = 9;
136172

137173
for (let burstIndex = 0; burstIndex < burstCount; burstIndex += 1) {
138-
const delay = burstIndex * 120;
174+
const delay = burstIndex * 90;
139175
setTimeout(() => {
140-
const originX = this.randomBetween(this.viewportWidth() * 0.15, this.viewportWidth() * 0.85);
141-
const originY = this.randomBetween(this.viewportHeight() * 0.2, this.viewportHeight() * 0.55);
176+
const originX = origin.x + this.randomBetween(-origin.width * 0.15, origin.width * 0.15);
177+
const originY = origin.y + this.randomBetween(-origin.height * 0.2, origin.height * 0.1);
142178

143179
for (let sparkIndex = 0; sparkIndex < sparksPerBurst; sparkIndex += 1) {
144180
const baseAngle = (Math.PI * 2 * sparkIndex) / sparksPerBurst;
145181
const angle = baseAngle + this.randomBetween(-0.18, 0.18);
146-
const distance = this.randomBetween(45, 110);
182+
const distance = this.randomBetween(38, 88);
147183
this.appendAndCleanup(
148184
container,
149185
this.createFireworkSpark(originX, originY, angle, distance),
150-
1300
186+
1000
151187
);
152188
}
153189
}, delay);
@@ -165,12 +201,13 @@ class AnimationSystem {
165201
if (!this.prefersReducedMotion) {
166202
const starsContainer = this.getStarsContainer();
167203
if (starsContainer && this.celebrationEffects.length) {
204+
const origin = this.getOriginMetrics(selectedOption);
168205
const effect = this.pickRandom(this.celebrationEffects);
169-
effect(starsContainer);
206+
effect(starsContainer, origin);
170207
}
171208
}
172209

173-
setTimeout(callback, this.prefersReducedMotion ? 250 : 1000);
210+
setTimeout(() => callback?.(), this.prefersReducedMotion ? 250 : 1000);
174211
}
175212

176213
handleWrongAnswer(option) {

0 commit comments

Comments
 (0)