Created
August 9, 2018 13:36
-
-
Save ericdvb/c650a9681e005b335862c3f477a7f459 to your computer and use it in GitHub Desktop.
Mapbox Component with animation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MapboxComponent extends React.Component { | |
async componentDidMount() { | |
// Some map instantiation | |
map.on('style.load', () => { | |
map.addSource('vehicles', { | |
"type": "geojson", | |
"buffer": 5, | |
"data": { | |
"type": "FeatureCollection", | |
"features":[] | |
} | |
}); | |
map.addLayer({ | |
"id": "vehicles", | |
"source": "vehicles", | |
"type": "symbol", | |
"layout": { | |
"icon-image": { | |
"property": "icon_type", | |
"type": "identity" | |
}, | |
"icon-size": { | |
"stops": [ | |
[0, 0.5], | |
[7, 1] | |
] | |
}, | |
"icon-rotate": { | |
"property": "icon_rotate", | |
"type": "identity" | |
}, | |
"icon-ignore-placement": true | |
} | |
}) | |
}); | |
} | |
componentDidUpdate(lastProps, lastState, snapshot) { | |
const { map } = this; | |
const { vehicles: stateVehicles } = this.state; | |
const { vehicles: nextVehicles } = this.props; | |
const animationLength = 5000; | |
const animationArray = [{ | |
type: 'location', | |
duration: 5000, | |
delay: 500, | |
}, { | |
type: 'rotation', | |
duration: 2000, | |
delay: 1000, | |
}]; | |
const animationIndex = 0; | |
const animateVehicles = (timestamp) => { | |
if (this.animationStartTime === null) { | |
this.animationStartTime = timestamp; | |
} | |
const elapsedTime = timestamp - this.animationStartTime + animationArray | |
.slice(0, animationIndex) | |
.reduce((offset, animation) => offset + animation.duration + animation.delay, 0); | |
const progress = elapsedTime / animationLength; | |
const safeProgress = Math.min( progress.toFixed(2), 1); | |
const nextData = { | |
"type": "FeatureCollection", | |
"features": stateVehicles | |
? stateVehicles.map(getNextVehicleMapCallback(safeProgress)) | |
: [] | |
}; | |
map.getSource('vehicles').setData(nextData); | |
if (safeProgress !== 1) { | |
this.animationFrame = requestAnimationFrame(animateVehicles); | |
} | |
} | |
// check whether current position of each vehicle in nextProps matches position in lastProps | |
// if it doesn't, put currentposition from lastprops in lastposition of nextState | |
let vehiclesChanged = false; | |
if (nextVehicles.length > 0 && lastProps.vehicles.length > 0) { | |
const nextStateVehicles = nextVehicles.map(nextVehicle => { | |
const matchingLastVehicle = lastProps.vehicles.find(lastVehicle => nextVehicle.device_id === lastVehicle.device_id); | |
if (matchingLastVehicle) { | |
if (matchingLastVehicle.latitude !== nextVehicle.latitude | |
|| matchingLastVehicle.longitude !== nextVehicle.longitude) { | |
vehiclesChanged = true; | |
return { | |
...nextVehicle, | |
lastLatitude: matchingLastVehicle.latitude, | |
lastLongitude: matchingLastVehicle.longitude, | |
bearing: bearing([matchingLastVehicle.longitude, matchingLastVehicle.latitude], [nextVehicle.longitude, nextVehicle.latitude]), | |
lastBearing: matchingLastVehicle.bearing, | |
}; | |
} | |
return { | |
...nextVehicle, | |
lastLatitude: matchingLastVehicle.latitude, | |
lastLongitude: matchingLastVehicle.longitude, | |
bearing: bearing([matchingLastVehicle.longitude, matchingLastVehicle.latitude], [nextVehicle.longitude, nextVehicle.latitude]), | |
lastBearing: bearing([matchingLastVehicle.longitude, matchingLastVehicle.latitude], [nextVehicle.longitude, nextVehicle.latitude]) | |
}; | |
} | |
return nextVehicle; | |
}); | |
if (vehiclesChanged) { | |
this.setState({ | |
vehicles: nextStateVehicles, | |
changedBySetState: true, | |
}, () => { | |
if (stateVehicles) { | |
cancelAnimationFrame(this.animationFrame); | |
const elapsedTime = performance.now() - this.animationStartTime; | |
if (elapsedTime < animationLength) { | |
const source = map.getSource('vehicles'); | |
if (source._data.features.length > 0) { | |
source.setData({ | |
"type": "FeatureCollection", | |
"features": stateVehicles | |
? stateVehicles.map(vehicle => { | |
const matchingVehicle = source._data.features.find(tweeningVehicle => tweeningVehicle.id === vehicle.id) | |
return { | |
"type": "Feature", | |
"properties": { | |
"id": vehicle.device_id, | |
"status": vehicle.status, | |
"type": vehicle.type | |
}, | |
"geometry": { | |
"type": "Point", | |
"coordinates": matchingVehicle.geometry.coordinates | |
} | |
}}) | |
: [] | |
}) | |
} | |
} | |
this.animationStartTime = null; | |
this.animationFrame = requestAnimationFrame(animateVehicles); | |
} | |
}); | |
} | |
} else if (nextVehicles.length > 0 && lastProps.vehicles.length === 0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment