hlarcher HF Staff commited on
Commit
099d0c4
·
unverified ·
1 Parent(s): 70f6fef

Add fading contrail effect to flight paths

Browse files

Each completed flight path creates an independent trail layer
that fades out over 5 seconds. Multiple trails can coexist
and fade simultaneously.

Files changed (1) hide show
  1. src/webgl/FlightPathLayer.js +78 -0
src/webgl/FlightPathLayer.js CHANGED
@@ -10,6 +10,9 @@ export class FlightPathLayer {
10
  this.fullPath = null;
11
  this.layersAdded = false;
12
  this.jetMarker = null;
 
 
 
13
  }
14
 
15
  createArcPath(start, end, numPoints = 50) {
@@ -190,6 +193,77 @@ export class FlightPathLayer {
190
  }
191
  }
192
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  // Show full static path
194
  showPath(from, to) {
195
  this.stopAnimation();
@@ -240,6 +314,9 @@ export class FlightPathLayer {
240
  } else {
241
  this.animationFrame = null;
242
  this.hideJet();
 
 
 
243
  if (onComplete) onComplete();
244
  }
245
  };
@@ -257,6 +334,7 @@ export class FlightPathLayer {
257
 
258
  hide() {
259
  this.stopAnimation();
 
260
  this.updatePath([]);
261
  this.fullPath = null;
262
  }
 
10
  this.fullPath = null;
11
  this.layersAdded = false;
12
  this.jetMarker = null;
13
+ // Trail system with multiple independent fading trails
14
+ this.trails = [];
15
+ this.trailCounter = 0;
16
  }
17
 
18
  createArcPath(start, end, numPoints = 50) {
 
193
  }
194
  }
195
 
196
+ createTrail(coords) {
197
+ const trailId = `trail-${this.trailCounter++}`;
198
+ const sourceId = `${this.id}-${trailId}-source`;
199
+ const layerId = `${this.id}-${trailId}`;
200
+
201
+ // Add source
202
+ this.map.addSource(sourceId, {
203
+ type: 'geojson',
204
+ data: {
205
+ type: 'Feature',
206
+ geometry: { type: 'LineString', coordinates: coords }
207
+ }
208
+ });
209
+
210
+ // Add layer
211
+ this.map.addLayer({
212
+ id: layerId,
213
+ type: 'line',
214
+ source: sourceId,
215
+ paint: {
216
+ 'line-color': COLORS.white,
217
+ 'line-width': 1.5,
218
+ 'line-opacity': 0.5
219
+ }
220
+ }, `${this.id}-glow`); // Insert below the main path layers
221
+
222
+ // Start fade animation
223
+ const fadeDuration = 5000;
224
+ const startTime = performance.now();
225
+
226
+ const trail = { sourceId, layerId, fadeFrame: null };
227
+
228
+ const fade = (currentTime) => {
229
+ const elapsed = currentTime - startTime;
230
+ const progress = Math.min(elapsed / fadeDuration, 1);
231
+ const opacity = 0.5 * (1 - progress);
232
+
233
+ if (this.map.getLayer(layerId)) {
234
+ this.map.setPaintProperty(layerId, 'line-opacity', opacity);
235
+ }
236
+
237
+ if (progress < 1) {
238
+ trail.fadeFrame = requestAnimationFrame(fade);
239
+ } else {
240
+ // Clean up when fully faded
241
+ this.removeTrail(trail);
242
+ }
243
+ };
244
+
245
+ trail.fadeFrame = requestAnimationFrame(fade);
246
+ this.trails.push(trail);
247
+ }
248
+
249
+ removeTrail(trail) {
250
+ if (trail.fadeFrame) {
251
+ cancelAnimationFrame(trail.fadeFrame);
252
+ }
253
+ if (this.map.getLayer(trail.layerId)) {
254
+ this.map.removeLayer(trail.layerId);
255
+ }
256
+ if (this.map.getSource(trail.sourceId)) {
257
+ this.map.removeSource(trail.sourceId);
258
+ }
259
+ this.trails = this.trails.filter(t => t !== trail);
260
+ }
261
+
262
+ clearAllTrails() {
263
+ this.trails.forEach(trail => this.removeTrail(trail));
264
+ this.trails = [];
265
+ }
266
+
267
  // Show full static path
268
  showPath(from, to) {
269
  this.stopAnimation();
 
314
  } else {
315
  this.animationFrame = null;
316
  this.hideJet();
317
+ // Create fading trail from completed path
318
+ this.createTrail(this.fullPath);
319
+ this.updatePath([]);
320
  if (onComplete) onComplete();
321
  }
322
  };
 
334
 
335
  hide() {
336
  this.stopAnimation();
337
+ this.clearAllTrails();
338
  this.updatePath([]);
339
  this.fullPath = null;
340
  }