johangras commited on
Commit
96e1f74
·
1 Parent(s): e47ebf0

✨ Add interactive particle effects and final polish

Browse files

- Mouse-attracted particles create mesmerizing patterns
- Click to create custom particle bursts
- Glowing cursor with radial gradient effect
- Sparkle effect follows mouse movement
- Particles respond to mouse proximity (200px range)
- Enhanced visual feedback for user interaction
- Smooth particle physics with attraction forces
- Professional polish for hackathon submission

Files changed (1) hide show
  1. app.py +57 -0
app.py CHANGED
@@ -95,11 +95,23 @@ class Particle {
95
  }
96
 
97
  update() {
 
 
 
 
 
 
 
 
 
 
 
98
  this.x += this.vx;
99
  this.y += this.vy;
100
  this.vy += 0.2;
101
  this.life--;
102
  this.vx *= 0.98;
 
103
  }
104
 
105
  draw() {
@@ -148,12 +160,30 @@ function animate() {
148
  ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
149
  ctx.fillRect(0, 0, canvas.width, canvas.height);
150
 
 
 
 
 
 
 
 
 
151
  particles = particles.filter(p => {
152
  p.update();
153
  p.draw();
154
  return p.life > 0;
155
  });
156
 
 
 
 
 
 
 
 
 
 
 
157
  requestAnimationFrame(animate);
158
  }
159
  animate();
@@ -174,6 +204,33 @@ setTimeout(() => {
174
  setTimeout(() => createBurst(3), 1500);
175
  setTimeout(() => createBurst(4), 2500);
176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  // Auto-start evolution after intro
178
  setTimeout(() => {
179
  const startBtn = document.querySelector('.gr-button-primary');
 
95
  }
96
 
97
  update() {
98
+ // Mouse attraction
99
+ const dx = mouseX - this.x;
100
+ const dy = mouseY - this.y;
101
+ const dist = Math.sqrt(dx * dx + dy * dy);
102
+
103
+ if (dist < 200 && dist > 0) {
104
+ const force = 0.5 * (1 - dist / 200);
105
+ this.vx += (dx / dist) * force;
106
+ this.vy += (dy / dist) * force;
107
+ }
108
+
109
  this.x += this.vx;
110
  this.y += this.vy;
111
  this.vy += 0.2;
112
  this.life--;
113
  this.vx *= 0.98;
114
+ this.vy *= 0.98;
115
  }
116
 
117
  draw() {
 
160
  ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
161
  ctx.fillRect(0, 0, canvas.width, canvas.height);
162
 
163
+ // Mouse glow effect
164
+ const gradient = ctx.createRadialGradient(mouseX, mouseY, 0, mouseX, mouseY, 100);
165
+ gradient.addColorStop(0, 'rgba(123, 63, 242, 0.3)');
166
+ gradient.addColorStop(0.5, 'rgba(0, 170, 255, 0.1)');
167
+ gradient.addColorStop(1, 'rgba(0, 255, 136, 0)');
168
+ ctx.fillStyle = gradient;
169
+ ctx.fillRect(mouseX - 100, mouseY - 100, 200, 200);
170
+
171
  particles = particles.filter(p => {
172
  p.update();
173
  p.draw();
174
  return p.life > 0;
175
  });
176
 
177
+ // Draw cursor sparkle
178
+ ctx.save();
179
+ ctx.fillStyle = '#FFFFFF';
180
+ ctx.shadowBlur = 20;
181
+ ctx.shadowColor = '#00FF88';
182
+ ctx.beginPath();
183
+ ctx.arc(mouseX, mouseY, 3, 0, Math.PI * 2);
184
+ ctx.fill();
185
+ ctx.restore();
186
+
187
  requestAnimationFrame(animate);
188
  }
189
  animate();
 
204
  setTimeout(() => createBurst(3), 1500);
205
  setTimeout(() => createBurst(4), 2500);
206
 
207
+ // Mouse interaction
208
+ let mouseX = canvas.width / 2;
209
+ let mouseY = canvas.height / 2;
210
+
211
+ canvas.addEventListener('mousemove', (e) => {
212
+ const rect = canvas.getBoundingClientRect();
213
+ mouseX = (e.clientX - rect.left) * (canvas.width / rect.width);
214
+ mouseY = (e.clientY - rect.top) * (canvas.height / rect.height);
215
+ });
216
+
217
+ canvas.addEventListener('click', (e) => {
218
+ const rect = canvas.getBoundingClientRect();
219
+ const x = (e.clientX - rect.left) * (canvas.width / rect.width);
220
+ const y = (e.clientY - rect.top) * (canvas.height / rect.height);
221
+
222
+ // Create burst at click location
223
+ for (let i = 0; i < 50; i++) {
224
+ const angle = (Math.PI * 2 * i) / 50;
225
+ const speed = Math.random() * 10 + 5;
226
+ particles.push(new Particle(
227
+ x + Math.cos(angle) * 10,
228
+ y + Math.sin(angle) * 10,
229
+ ['#00FF88', '#7B3FF2', '#00AAFF', '#FFD700'][Math.floor(Math.random() * 4)]
230
+ ));
231
+ }
232
+ });
233
+
234
  // Auto-start evolution after intro
235
  setTimeout(() => {
236
  const startBtn = document.querySelector('.gr-button-primary');