'use strict'; var landingColor = '#fe761e'; var takeOffColor = '#3081dd'; var lineColor = '#5297ee'; var fps = 40; var curveFactor = 40; var speed = 0.9; // between 0 & 1 var drawLines = true; var batchSize = 12; function initMap(selector) { var airportMap = {}; var $flightmap = d3.select(selector).node(); var width = 950; var height = 500; var mapScale = 1050; var projection = d3.geoAlbersUsa() .scale(mapScale).translate([width / 2, height / 2]); var path = d3.geoPath().pointRadius(1.2).projection(projection); var svg = d3.select(selector).append('svg') .attr('preserveAspectRatio', 'xMidYMid') .attr('viewBox', '0 0 ' + width + ' ' + height) .attr('width', width).attr('height', height); var canvasEl = d3.select(selector) .append('canvas') .attr('style', 'position:absolute; top:0; left:0') .attr('width', width) .attr('height', height); var canvas = canvasEl.node(); var ctx = canvas.getContext('2d'); var points = []; var batchCount = 0; function animate() { draw(); // request another frame setTimeout(function () { window.requestAnimationFrame(animate); }, 1000 / fps); } // draw the current frame based on sliderValue function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); for (var i = 0; i < points.length; i++) { if (i > batchCount) { // for progressive animation batchCount += batchSize; break; } var point = points[i], start = point.start, end = point.end, progress = point.progress; var wayPoint = helpers.getWaypointXY(start, end, 90); var controlPt = { x: wayPoint.x, y: wayPoint.y - curveFactor }; ctx.beginPath(); var percentFactor = progress / 100; var xy = helpers.getQuadraticBezierXY(start, controlPt, end, percentFactor); point.progress += speed; // when to draw lines if (drawLines && progress < 101) { helpers.drawLine(ctx, start, end, controlPt, lineColor); } // when to draw dots if (progress > 0 && progress < 10) { helpers.drawDot(ctx, xy, 2.5, takeOffColor, takeOffColor); } else if (progress < 90) { helpers.drawDot(ctx, xy, 4); } else if (progress > 90 && progress < 101) { helpers.drawDot(ctx, xy, 2.5, landingColor, landingColor); } else { point.progress = 0; // reset progress } } } function ready(error, topo, airports) { if (error) throw error; svg.append('g').attr('class','states').selectAll('path') .data(topojson.feature(topo, topo.objects.states).features).enter() .append('path').attr('d', path); svg.append('g').attr('class', 'airports').selectAll('path') .data(topojson.feature(airports, airports.objects.airports).features).enter() .append('path').attr('id', function (d) { return d.id; }).attr('d', path); var geos = topojson.feature(airports, airports.objects.airports).features; geos.forEach(function (geo) { airportMap[geo.id] = geo.geometry.coordinates; }); points = helpers.generatePointsFromMap(airportMap, window.routes, projection); animate(); } d3.queue() .defer(d3.json, 'https://nodeassets.nbcnews.com/cdnassets/projects/2017/08/airplane-mode/us-states.json') .defer(d3.json, 'https://nodeassets.nbcnews.com/cdnassets/projects/2017/08/airplane-mode/us-airports-major.topo.json') .await(ready); }