@react-native-mapbox-gl/maps
Version:
A Mapbox GL react native module for creating custom maps
194 lines (171 loc) • 5.33 kB
JavaScript
import {Animated} from 'react-native';
/* eslint-disable guard-for-in */
// see
// https://github.com/facebook/react-native/blob/master/Libraries/Animated/src/nodes/AnimatedWithChildren.js
const AnimatedWithChildren = Object.getPrototypeOf(Animated.ValueXY);
if (__DEV__) {
if (AnimatedWithChildren.name !== 'AnimatedWithChildren') {
console.error(
'AnimatedCoordinatesArray could not obtain AnimatedWithChildren base class',
);
}
}
const defaultConfig = {
useNativeDriver: false,
};
class AnimatedCoordinatesArray extends AnimatedWithChildren {
constructor(...args) {
super();
this.state = this.onInitialState(...args);
}
/**
* Subclasses can override to calculate initial state
*
* @param {*} args - to value from animate
* @returns {object} - the state object
*/
onInitialState(coordinatesArray) {
return {coords: coordinatesArray.map(coord => [coord[0], coord[1]])};
}
/**
* Subclasses can override getValue to calculate value from state.
* Value is typically coordinates array, but can be anything
*
* @param {object} state - either state from initialState and/or from calculate
* @returns {object}
*/
onGetValue(state) {
return state.coords;
}
/**
* Calculates state based on startingState and progress, returns a new state
*
* @param {object} state - state object from initialState and/or from calculate
* @param {number} progress - value between 0 and 1
* @returns {object} next state
*/
onCalculate(state, progress) {
const {coords, targetCoords} = state;
const newF = progress;
const origF = 1.0 - newF;
// common
const commonLen = Math.min(coords.length, targetCoords.length);
const common = coords
.slice(0, commonLen)
.map((origCoord, i) => [
origCoord[0] * origF + targetCoords[i][0] * newF,
origCoord[1] * origF + targetCoords[i][1] * newF,
]);
if (targetCoords.length > coords.length) {
// only in new (adding)
const addingOrig =
coords.length > 0 ? coords[coords.length - 1] : targetCoords[0];
const adding = targetCoords
.slice(commonLen, targetCoords.length)
.map(newCoord => [
addingOrig[0] * origF + newCoord[0] * newF,
addingOrig[1] * origF + newCoord[1] * newF,
]);
return {coords: [...common, ...adding], targetCoords};
}
if (coords.length > targetCoords.length) {
// only in orig (dissapearing)
const dissapearingNew =
targetCoords.length > 0
? targetCoords[targetCoords.length - 1]
: coords[0];
const dissapearing = coords
.slice(commonLen, coords.length)
.map(origCoord => [
origCoord[0] * origF + dissapearingNew[0] * newF,
origCoord[1] * origF + dissapearingNew[1] * newF,
]);
return {coords: [...common, ...dissapearing], targetCoords};
}
return {coords: common, targetCoords};
}
/**
* Subclasses can override to start a new animation
*
* @param {*} toValue - to value from animate
* @param {*} actCoords - the current coordinates array to start from
* @returns {object} The state
*/
onStart(state, toValue) {
const targetCoords = toValue.map(coord => [coord[0], coord[1]]);
return {
...state,
targetCoords,
};
}
animate(progressValue, progressAnimation, config) {
const {toValue} = config;
const onAnimationStart = animation => {
if (this.animation) {
// there was a started but not finsihed animation
const actProgress = this.progressValue.__getValue();
this.animation.stop();
this.state = this.onCalculate(this.state, actProgress);
this.progressValue.__removeChild(this);
this.progressValue = null;
this.animation = null;
}
this.progressValue = progressValue;
this.progressValue.__addChild(this);
this.animation = animation;
this.state = this.onStart(this.state, toValue);
};
const origAnimationStart = progressAnimation.start;
const newAnimation = progressAnimation;
newAnimation.start = function start(...args) {
onAnimationStart(progressAnimation);
origAnimationStart(...args);
};
return newAnimation;
}
timing(config) {
const progressValue = new Animated.Value(0.0);
return this.animate(
progressValue,
Animated.timing(progressValue, {
...defaultConfig,
...config,
toValue: 1.0,
}),
config,
);
}
spring(config) {
const progressValue = new Animated.Value(0.0);
return this.animate(
progressValue,
Animated.spring(progressValue, {
...defaultConfig,
...config,
toValue: 1.0,
}),
config,
);
}
decay(config) {
const progressValue = new Animated.Value(0.0);
return this.animate(
progressValue,
Animated.decay(this.progressValue, {
...defaultConfig,
...config,
toValue: 1.0,
}),
config,
);
}
__getValue() {
if (!this.progressValue) {
return this.onGetValue(this.state);
}
return this.onGetValue(
this.onCalculate(this.state, this.progressValue.__getValue()),
);
}
}
export default AnimatedCoordinatesArray;