|
console.clear(); |
|
|
|
new Vue({ |
|
|
|
el: '#app', |
|
|
|
data: () => ({ |
|
distance: null, |
|
airports: [], |
|
currentAirport: null, |
|
lastAirport: null, |
|
markers: [ |
|
{ |
|
airport: null, |
|
x: 200, |
|
y: 300, |
|
startX: 0, |
|
startY: 0, |
|
fill: '#f47825', |
|
current: false |
|
}, |
|
{ |
|
airport: null, |
|
x: 500, |
|
y: 100, |
|
startX: 0, |
|
startY: 0, |
|
fill: '#00b26b', |
|
current: false |
|
} |
|
] |
|
}), |
|
|
|
filters: { |
|
numberWithCommas(val) { |
|
return ( val && val.toString ? val.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : val ); |
|
} |
|
}, |
|
|
|
mounted() { |
|
|
|
// D3 Projection |
|
var projection = d3.geoAlbersUsa().scale([1500]); // scale things down so see entire US |
|
this.projection = projection; |
|
|
|
// D3 US States map |
|
fetch('https://s3-us-west-2.amazonaws.com/s.cdpn.io/39255/us-states.json') |
|
.then( response => response.json() ) |
|
.then( (states) => { |
|
|
|
// Define path generator |
|
var path = d3.geoPath() // path generator that will convert GeoJSON to SVG paths |
|
.projection(projection); // tell path generator to use albersUsa projection |
|
|
|
// Bind the data to the SVG and create one path per GeoJSON feature |
|
d3.select( this.$refs.states ) |
|
.selectAll("path") |
|
.data(states.features) |
|
.enter() |
|
.append("path") |
|
.attr("d", path); |
|
}); |
|
|
|
// Airports |
|
fetch('https://s3-us-west-2.amazonaws.com/s.cdpn.io/39255/airports.json') |
|
.then( response => response.json() ) |
|
.then( (airports) => { |
|
|
|
// Let's only focus on the top 100 |
|
airports = airports.slice(0,100); |
|
var i = airports.length, d, proj; |
|
|
|
while(i--){ |
|
d = airports[i]; |
|
proj = projection([d.Lng, d.Lat]); |
|
|
|
if ( proj ) { |
|
d.x = proj[0]; |
|
d.y = proj[1]; |
|
} else { |
|
airports.splice(i, 1); |
|
} |
|
} |
|
|
|
this.airports = airports.reverse(); |
|
|
|
var la = null; |
|
|
|
this.markers.forEach((marker)=>{ |
|
var ra = this.randomAirport(); |
|
if ( ra == la ) { ra = this.randomAirport(); } |
|
la = ra; |
|
marker.airport = ra; |
|
marker.x = ra.x; |
|
marker.y = ra.y; |
|
}); |
|
this.markerDistance(); |
|
}); |
|
|
|
|
|
}, |
|
|
|
methods: { |
|
|
|
randomAirport(){ |
|
return this.airports[ Math.floor(Math.random() * this.airports.length) ]; |
|
}, |
|
|
|
markerSet(e, marker){ |
|
if ( e ) { e.preventDefault(); } |
|
|
|
marker = marker || this.markers[0]; |
|
marker.airport = null; |
|
marker.current = true; |
|
marker.startX = marker.x; |
|
marker.startY = marker.y; |
|
|
|
this.currentAirport = null; |
|
this.currentMarker = marker; |
|
|
|
if ( this.airplaneTween ) { |
|
|
|
var oldTween = this.airplaneTween; |
|
|
|
var tl = new TimelineLite(); |
|
tl.to(this.$refs.airplane,0.2,{ |
|
opacity: 0, |
|
ease: "Linear.easeNone", |
|
onComplete: function(){ |
|
if ( oldTween ) { oldTween.kill(); } |
|
} |
|
}); |
|
|
|
this.airplaneFade = tl; |
|
} |
|
|
|
this.markerDrag(e); |
|
document.addEventListener('mousemove',this.markerDrag); |
|
document.addEventListener('mouseup', this.markerStop); |
|
this.$refs.map.addEventListener('mouseleave', this.markerLeave); |
|
}, |
|
|
|
markerDrag(e){ |
|
this.currentMarker.airport = this.currentAirport; |
|
|
|
if ( this.currentAirport ) { |
|
this.currentMarker.x = this.currentAirport.x; |
|
this.currentMarker.y = this.currentAirport.y; |
|
} else { |
|
d3.event = e; |
|
var mouse = d3.mouse(this.$refs.map); |
|
this.currentMarker.x = mouse[0]; |
|
this.currentMarker.y = mouse[1]; |
|
} |
|
this.markerDistance(); |
|
}, |
|
|
|
markerLeave(){ |
|
this.currentMarker.x = this.currentMarker.startX; |
|
this.currentMarker.y = this.currentMarker.startY; |
|
|
|
this.markerStop(); |
|
}, |
|
|
|
markerStop(){ |
|
console.log('stop!'); |
|
document.removeEventListener('mousemove',this.markerDrag); |
|
document.removeEventListener('mouseup', this.markerStop); |
|
this.$refs.map.removeEventListener('mouseleave', this.markerLeave); |
|
|
|
this.currentMarker.current = false; |
|
this.currentMarker = null; |
|
|
|
this.markerDistance(); |
|
}, |
|
|
|
markerConnect(){ |
|
var m1 = this.markers[1], |
|
m2 = this.markers[0]; |
|
|
|
if ( m1.x < m2.x ) { |
|
m1 = this.markers[0]; |
|
m2 = this.markers[1]; |
|
} |
|
|
|
var dx = m2.x - m1.x, |
|
dy = m2.y - m1.y, |
|
dr = Math.sqrt(dx * dx + dy * dy); |
|
|
|
return "M" + m2.x + "," + m2.y + "A" + dr + "," + dr + " 0 0,1 " + m1.x + "," + m1.y ; |
|
}, |
|
|
|
calcDistance(lat1, lon1, lat2, lon2, unit) { |
|
var radlat1 = Math.PI * lat1/180 |
|
var radlat2 = Math.PI * lat2/180 |
|
var theta = lon1-lon2 |
|
var radtheta = Math.PI * theta/180 |
|
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta); |
|
dist = Math.acos(dist) |
|
dist = dist * 180/Math.PI |
|
dist = dist * 60 * 1.1515 |
|
if (unit=="K") { dist = dist * 1.609344 } |
|
if (unit=="N") { dist = dist * 0.8684 } |
|
return dist; |
|
|
|
}, |
|
|
|
markerDistance(){ |
|
var marker1 = this.markers[0]; |
|
var latLng1 = this.projection.invert([ marker1.x, marker1.y ]).reverse(); |
|
|
|
var marker2 = this.markers[1]; |
|
var latLng2 = this.projection.invert([ marker2.x, marker2.y ]).reverse(); |
|
|
|
this.distance = Math.round( this.calcDistance(latLng1[0], latLng1[1], latLng2[0], latLng2[1]) ); |
|
this.airplaneAnimate(); |
|
}, |
|
|
|
airportSnap(e, airport){ |
|
if ( airport !== this.currentAirport ) { |
|
airport.current = true; |
|
this.currentAirport = airport; |
|
if ( this.currentMarker ) { this.currentMarker.airport = airport; } |
|
} |
|
}, |
|
|
|
airportClick(e,airport){ |
|
if ( !this.currentMarker ) { this.markerSet(e); } |
|
this.currentAirport = airport; |
|
this.currentMarker.x = this.currentAirport.x; |
|
this.currentMarker.y = this.currentAirport.y; |
|
if ( this.currentMarker ) { this.currentMarker.airport = airport; } |
|
}, |
|
|
|
airportLeave(e,airport){ |
|
airport.current = false; |
|
this.currentAirport = null; |
|
this.lastAirport = airport; |
|
}, |
|
|
|
airplaneAnimate(){ |
|
var path = this.markerConnect(); |
|
var newTween = new TimelineMax({ repeat: -1, delay: -0.2 }); |
|
var duration = Math.min( this.distance / 80, 15 ); |
|
var opacityDuration = Math.min(duration * 0.2, 0.3); |
|
|
|
if ( this.airplaneFade && this.airplaneFade.isActive() ) { |
|
newTween.pause(); |
|
|
|
this.airplaneFade.eventCallback('onComplete',function(){ |
|
newTween.play(); |
|
}); |
|
} else if ( this.airplaneTween ) { |
|
this.airplaneTween.kill(); |
|
} |
|
|
|
var bez = MorphSVGPlugin.pathDataToBezier(path) |
|
|
|
newTween.to(this.$refs.airplane, duration, { |
|
bezier: { |
|
values: bez, |
|
curviness: 1, |
|
autoRotate: -90, |
|
type:"cubic" |
|
}, |
|
reversed: true, |
|
ease: Linear.easeNone |
|
}, 0); |
|
|
|
newTween.fromTo(this.$refs.airplane, opacityDuration, { |
|
opacity: 0 |
|
},{ |
|
opacity: 1, |
|
delay: opacityDuration/2, |
|
ease: "Linear.easeNone" |
|
}, 0); |
|
|
|
newTween.to(this.$refs.airplane, opacityDuration, { |
|
opacity: 0, |
|
ease: 'Linear.easeNone' |
|
}, '-='+opacityDuration); |
|
|
|
this.airplaneTween = newTween; |
|
}, |
|
|
|
} |
|
}); |