@@ -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