@mapbox/react-map-gl
Version:
A React wrapper for MapboxGL-js and overlay API.
252 lines (208 loc) • 8.14 kB
JavaScript
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
import _createClass from "@babel/runtime/helpers/esm/createClass";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
/* global requestAnimationFrame, cancelAnimationFrame */
import assert from './assert';
import { TransitionInterpolator, LinearInterpolator } from './transition';
import MapState from './map-state';
var noop = function noop() {}; // crops the old easing function from x0 to 1 where x0 is the interruption point
// returns a new easing function with domain [0, 1] and range [0, 1]
export function cropEasingFunction(easing, x0) {
var y0 = easing(x0);
return function (t) {
return 1 / (1 - y0) * (easing(t * (1 - x0) + x0) - y0);
};
}
export var TRANSITION_EVENTS = {
BREAK: 1,
SNAP_TO_END: 2,
IGNORE: 3,
UPDATE: 4
};
var DEFAULT_PROPS = {
transitionDuration: 0,
transitionEasing: function transitionEasing(t) {
return t;
},
transitionInterpolator: new LinearInterpolator(),
transitionInterruption: TRANSITION_EVENTS.BREAK,
onTransitionStart: noop,
onTransitionInterrupt: noop,
onTransitionEnd: noop,
onViewportChange: noop,
onStateChange: noop
};
var TransitionManager =
/*#__PURE__*/
function () {
function TransitionManager(props) {
var _this = this;
_classCallCheck(this, TransitionManager);
_defineProperty(this, "props", void 0);
_defineProperty(this, "state", void 0);
_defineProperty(this, "_animationFrame", null);
_defineProperty(this, "_onTransitionFrame", function () {
// _updateViewport() may cancel the animation
_this._animationFrame = requestAnimationFrame(_this._onTransitionFrame);
_this._updateViewport();
});
if (props) {
this.props = props;
}
}
_createClass(TransitionManager, [{
key: "getViewportInTransition",
// Returns current transitioned viewport.
value: function getViewportInTransition() {
return this._animationFrame ? this.state.propsInTransition : null;
} // Process the viewport change, either ignore or trigger a new transiton.
// Return true if a new transition is triggered, false otherwise.
}, {
key: "processViewportChange",
value: function processViewportChange(nextProps) {
var currentProps = this.props; // Set this.props here as '_triggerTransition' calls '_updateViewport' that uses this.props.
this.props = nextProps; // NOTE: Be cautious re-ordering statements in this function.
if (this._shouldIgnoreViewportChange(currentProps, nextProps)) {
return false;
}
if (this._isTransitionEnabled(nextProps)) {
var startProps = Object.assign({}, currentProps);
var endProps = Object.assign({}, nextProps);
if (this._isTransitionInProgress()) {
currentProps.onTransitionInterrupt();
if (this.state.interruption === TRANSITION_EVENTS.SNAP_TO_END) {
Object.assign(startProps, this.state.endProps);
} else {
Object.assign(startProps, this.state.propsInTransition);
}
if (this.state.interruption === TRANSITION_EVENTS.UPDATE) {
var currentTime = Date.now();
var x0 = (currentTime - this.state.startTime) / this.state.duration;
endProps.transitionDuration = this.state.duration - (currentTime - this.state.startTime);
endProps.transitionEasing = cropEasingFunction(this.state.easing, x0);
endProps.transitionInterpolator = startProps.transitionInterpolator;
}
}
endProps.onTransitionStart();
this._triggerTransition(startProps, endProps);
return true;
}
if (this._isTransitionInProgress()) {
currentProps.onTransitionInterrupt();
this._endTransition();
}
return false;
} // Helper methods
}, {
key: "_isTransitionInProgress",
value: function _isTransitionInProgress() {
return Boolean(this._animationFrame);
}
}, {
key: "_isTransitionEnabled",
value: function _isTransitionEnabled(props) {
return props.transitionDuration > 0 && Boolean(props.transitionInterpolator);
}
}, {
key: "_isUpdateDueToCurrentTransition",
value: function _isUpdateDueToCurrentTransition(props) {
if (this.state.propsInTransition) {
return this.state.interpolator.arePropsEqual(props, this.state.propsInTransition);
}
return false;
}
}, {
key: "_shouldIgnoreViewportChange",
value: function _shouldIgnoreViewportChange(currentProps, nextProps) {
if (!currentProps) {
return true;
}
if (this._isTransitionInProgress()) {
// Ignore update if it is requested to be ignored
return this.state.interruption === TRANSITION_EVENTS.IGNORE || // Ignore update if it is due to current active transition.
this._isUpdateDueToCurrentTransition(nextProps);
}
if (this._isTransitionEnabled(nextProps)) {
// Ignore if none of the viewport props changed.
return nextProps.transitionInterpolator.arePropsEqual(currentProps, nextProps);
}
return true;
}
}, {
key: "_triggerTransition",
value: function _triggerTransition(startProps, endProps) {
assert(this._isTransitionEnabled(endProps), 'Transition is not enabled');
if (this._animationFrame) {
cancelAnimationFrame(this._animationFrame);
}
var initialProps = endProps.transitionInterpolator.initializeProps(startProps, endProps);
var interactionState = {
inTransition: true,
isZooming: startProps.zoom !== endProps.zoom,
isPanning: startProps.longitude !== endProps.longitude || startProps.latitude !== endProps.latitude,
isRotating: startProps.bearing !== endProps.bearing || startProps.pitch !== endProps.pitch
};
this.state = {
// Save current transition props
duration: endProps.transitionDuration,
easing: endProps.transitionEasing,
interpolator: endProps.transitionInterpolator,
interruption: endProps.transitionInterruption,
startTime: Date.now(),
startProps: initialProps.start,
endProps: initialProps.end,
animation: null,
propsInTransition: {},
interactionState: interactionState
};
this._onTransitionFrame();
this.props.onStateChange(interactionState);
}
}, {
key: "_endTransition",
value: function _endTransition() {
if (this._animationFrame) {
cancelAnimationFrame(this._animationFrame);
this._animationFrame = null;
}
this.props.onStateChange({
inTransition: false,
isZooming: false,
isPanning: false,
isRotating: false
});
}
}, {
key: "_updateViewport",
value: function _updateViewport() {
// NOTE: Be cautious re-ordering statements in this function.
var currentTime = Date.now();
var _this$state = this.state,
startTime = _this$state.startTime,
duration = _this$state.duration,
easing = _this$state.easing,
interpolator = _this$state.interpolator,
startProps = _this$state.startProps,
endProps = _this$state.endProps;
var shouldEnd = false;
var t = (currentTime - startTime) / duration;
if (t >= 1) {
t = 1;
shouldEnd = true;
}
t = easing(t);
var viewport = interpolator.interpolateProps(startProps, endProps, t); // Normalize viewport props
var mapState = new MapState(Object.assign({}, this.props, viewport));
this.state.propsInTransition = mapState.getViewportProps();
this.props.onViewportChange(this.state.propsInTransition, this.state.interactionState, this.props);
if (shouldEnd) {
this._endTransition();
this.props.onTransitionEnd();
}
}
}]);
return TransitionManager;
}();
_defineProperty(TransitionManager, "defaultProps", DEFAULT_PROPS);
export { TransitionManager as default };
//# sourceMappingURL=transition-manager.js.map