@deck.gl/core
Version:
deck.gl core library
149 lines • 6.35 kB
JavaScript
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import Transition from "../transitions/transition.js";
const noop = () => { };
// Enums cannot be directly exported as they are not transpiled correctly into ES5, see https://github.com/visgl/deck.gl/issues/7130
export const TRANSITION_EVENTS = {
BREAK: 1,
SNAP_TO_END: 2,
IGNORE: 3
};
const DEFAULT_EASING = t => t;
const DEFAULT_INTERRUPTION = TRANSITION_EVENTS.BREAK;
export default class TransitionManager {
constructor(opts) {
this._onTransitionUpdate = transition => {
// NOTE: Be cautious re-ordering statements in this function.
const { time, settings: { interpolator, startProps, endProps, duration, easing } } = transition;
const t = easing(time / duration);
const viewport = interpolator.interpolateProps(startProps, endProps, t);
// This gurantees all props (e.g. bearing, longitude) are normalized
// So when viewports are compared they are in same range.
this.propsInTransition = this.getControllerState({
...this.props,
...viewport
}).getViewportProps();
this.onViewStateChange({
viewState: this.propsInTransition,
oldViewState: this.props
});
};
this.getControllerState = opts.getControllerState;
this.propsInTransition = null;
this.transition = new Transition(opts.timeline);
this.onViewStateChange = opts.onViewStateChange || noop;
this.onStateChange = opts.onStateChange || noop;
}
finalize() {
this.transition.cancel();
}
// Returns current transitioned viewport.
getViewportInTransition() {
return this.propsInTransition;
}
// Process the vewiport change, either ignore or trigger a new transition.
// Return true if a new transition is triggered, false otherwise.
processViewStateChange(nextProps) {
let transitionTriggered = false;
const 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 (!currentProps || this._shouldIgnoreViewportChange(currentProps, nextProps)) {
return false;
}
if (this._isTransitionEnabled(nextProps)) {
let startProps = currentProps;
if (this.transition.inProgress) {
// @ts-expect-error
const { interruption, endProps } = this.transition.settings;
startProps = {
...currentProps,
...(interruption === TRANSITION_EVENTS.SNAP_TO_END
? endProps
: this.propsInTransition || currentProps)
};
}
this._triggerTransition(startProps, nextProps);
transitionTriggered = true;
}
else {
this.transition.cancel();
}
return transitionTriggered;
}
updateTransition() {
this.transition.update();
}
// Helper methods
_isTransitionEnabled(props) {
const { transitionDuration, transitionInterpolator } = props;
return ((transitionDuration > 0 || transitionDuration === 'auto') &&
Boolean(transitionInterpolator));
}
_isUpdateDueToCurrentTransition(props) {
if (this.transition.inProgress && this.propsInTransition) {
// @ts-expect-error
return this.transition.settings.interpolator.arePropsEqual(props, this.propsInTransition);
}
return false;
}
_shouldIgnoreViewportChange(currentProps, nextProps) {
if (this.transition.inProgress) {
// @ts-expect-error
const transitionSettings = this.transition.settings;
// Ignore update if it is requested to be ignored
return (transitionSettings.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;
}
_triggerTransition(startProps, endProps) {
const startViewstate = this.getControllerState(startProps);
const endViewStateProps = this.getControllerState(endProps).shortestPathFrom(startViewstate);
// update transitionDuration for 'auto' mode
const transitionInterpolator = endProps.transitionInterpolator;
const duration = transitionInterpolator.getDuration
? transitionInterpolator.getDuration(startProps, endProps)
: endProps.transitionDuration;
if (duration === 0) {
return;
}
const initialProps = transitionInterpolator.initializeProps(startProps, endViewStateProps);
this.propsInTransition = {};
const transitionSettings = {
duration,
easing: endProps.transitionEasing || DEFAULT_EASING,
interpolator: transitionInterpolator,
interruption: endProps.transitionInterruption || DEFAULT_INTERRUPTION,
startProps: initialProps.start,
endProps: initialProps.end,
onStart: endProps.onTransitionStart,
onUpdate: this._onTransitionUpdate,
onInterrupt: this._onTransitionEnd(endProps.onTransitionInterrupt),
onEnd: this._onTransitionEnd(endProps.onTransitionEnd)
};
this.transition.start(transitionSettings);
this.onStateChange({ inTransition: true });
this.updateTransition();
}
_onTransitionEnd(callback) {
return transition => {
this.propsInTransition = null;
this.onStateChange({
inTransition: false,
isZooming: false,
isPanning: false,
isRotating: false
});
callback?.(transition);
};
}
}
//# sourceMappingURL=transition-manager.js.map