Skip to content

Commit

Permalink
Use the same animation loop for render, attribute transitions and vie…
Browse files Browse the repository at this point in the history
…wport transitions (#2921)
  • Loading branch information
Pessimistress committed Apr 9, 2019
1 parent ead325c commit 5f42779
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 29 deletions.
4 changes: 4 additions & 0 deletions modules/core/src/controllers/controller.js
Expand Up @@ -180,6 +180,10 @@ export default class Controller {
}
/* eslint-enable complexity, max-statements */

updateTransition(timestamp) {
this.transitionManager.updateTransition(timestamp);
}

toggleEvents(eventNames, enabled) {
if (this.eventManager) {
eventNames.forEach(eventName => {
Expand Down
25 changes: 10 additions & 15 deletions modules/core/src/controllers/transition-manager.js
@@ -1,4 +1,3 @@
/* global requestAnimationFrame, cancelAnimationFrame */
import LinearInterpolator from '../transitions/linear-interpolator';
import Transition from '../transitions/transition';
import assert from '../utils/assert';
Expand Down Expand Up @@ -26,19 +25,16 @@ export default class TransitionManager {
assert(ControllerState);
this.ControllerState = ControllerState;
this.props = Object.assign({}, DEFAULT_PROPS, props);
this.animation = null;
this.propsInTransition = null;
this.time = 0;
this.transition = new Transition();

this.onViewStateChange = props.onViewStateChange;

this._onTransitionFrame = this._onTransitionFrame.bind(this);
this._onTransitionUpdate = this._onTransitionUpdate.bind(this);
}

finalize() {
cancelAnimationFrame(this.animation);
}
finalize() {}

// Returns current transitioned viewport.
getViewportInTransition() {
Expand Down Expand Up @@ -78,6 +74,11 @@ export default class TransitionManager {
return transitionTriggered;
}

updateTransition(timestamp) {
this.time = timestamp;
this._updateTransition();
}

// Helper methods

_isTransitionEnabled(props) {
Expand Down Expand Up @@ -109,8 +110,6 @@ export default class TransitionManager {
_triggerTransition(startProps, endProps) {
assert(this._isTransitionEnabled(endProps), 'Transition is not enabled');

cancelAnimationFrame(this.animation);

const startViewstate = new this.ControllerState(startProps);
const endViewStateProps = new this.ControllerState(endProps).shortestPathFrom(startViewstate);

Expand All @@ -134,19 +133,15 @@ export default class TransitionManager {
onInterrupt: this._onTransitionEnd(endProps.onTransitionInterrupt),
onEnd: this._onTransitionEnd(endProps.onTransitionEnd)
});

this._onTransitionFrame();
this._updateTransition();
}

_onTransitionFrame() {
// _updateViewport() may cancel the animation
this.animation = requestAnimationFrame(this._onTransitionFrame);
this.transition.update(Date.now());
_updateTransition() {
this.transition.update(this.time);
}

_onTransitionEnd(callback) {
return transition => {
cancelAnimationFrame(this.animation);
this.propsInTransition = null;
callback(transition);
};
Expand Down
4 changes: 2 additions & 2 deletions modules/core/src/lib/attribute-manager.js
Expand Up @@ -266,9 +266,9 @@ export default class AttributeManager {

// Update attribute transition to the current timestamp
// Returns `true` if any transition is in progress
updateTransition() {
updateTransition(timestamp) {
const {attributeTransitionManager} = this;
const transitionUpdated = attributeTransitionManager.setCurrentTime(Date.now());
const transitionUpdated = attributeTransitionManager.setCurrentTime(timestamp);
this.needsRedraw = this.needsRedraw || transitionUpdated;
return transitionUpdated;
}
Expand Down
9 changes: 8 additions & 1 deletion modules/core/src/lib/deck.js
Expand Up @@ -654,7 +654,7 @@ export default class Deck {

// Update layers if needed (e.g. some async prop has loaded)
// Note: This can trigger a redraw
this.layerManager.updateLayers();
this.layerManager.updateLayers(animationProps);

// Needs to be done before drawing
this._updateAnimationProps(animationProps);
Expand All @@ -664,6 +664,13 @@ export default class Deck {

// Redraw if necessary
this.redraw(false);

// Update viewport transition if needed
// Note: this can trigger `onViewStateChange`, and affect layers
// We want to defer these changes to the next frame
if (this.viewManager) {
this.viewManager.updateViewStates(animationProps);
}
}

// Callbacks
Expand Down
9 changes: 6 additions & 3 deletions modules/core/src/lib/layer-manager.js
Expand Up @@ -45,6 +45,7 @@ const INITIAL_CONTEXT = Object.seal({
layerManager: null,
deck: null,
gl: null,
time: -1,

// Settings
useDevicePixels: true, // Exposed in case custom layers need to adjust sizes
Expand Down Expand Up @@ -199,7 +200,10 @@ export default class LayerManager {
}

// Update layers from last cycle if `setNeedsUpdate()` has been called
updateLayers() {
updateLayers(animationProps = {}) {
if ('time' in animationProps) {
this.context.time = animationProps.time;
}
// NOTE: For now, even if only some layer has changed, we update all layers
// to ensure that layer id maps etc remain consistent even if different
// sublayers are rendered
Expand Down Expand Up @@ -459,7 +463,6 @@ export default class LayerManager {
}

setPropOverrides(payload.itemKey, payload.valuePath.slice(1), payload.value);
const newLayers = this.layers.map(layer => new layer.constructor(layer.props));
this.updateLayers({newLayers});
this.updateLayers();
}
}
2 changes: 1 addition & 1 deletion modules/core/src/lib/layer.js
Expand Up @@ -404,7 +404,7 @@ export default class Layer extends Component {
updateTransition() {
const attributeManager = this.getAttributeManager();
if (attributeManager) {
attributeManager.updateTransition();
attributeManager.updateTransition(this.context.time);
}
}

Expand Down
12 changes: 12 additions & 0 deletions modules/core/src/lib/view-manager.js
Expand Up @@ -77,6 +77,18 @@ export default class ViewManager {
this._needsRedraw = this._needsRedraw || reason;
}

// Checks each viewport for transition updates
updateViewStates(animationProps = {}) {
if ('time' in animationProps) {
for (const viewId in this.controllers) {
const controller = this.controllers[viewId];
if (controller) {
controller.updateTransition(animationProps.time);
}
}
}
}

/** Get a set of viewports for a given width and height
* TODO - Intention is for deck.gl to autodeduce width and height and drop the need for props
* @param rect (object, optional) - filter the viewports
Expand Down
17 changes: 10 additions & 7 deletions test/modules/core/lib/transition-manager.spec.js
Expand Up @@ -174,11 +174,14 @@ test('TransitionManager#callbacks', t => {
transitionManager.processViewStateChange(transitionProps);
});

setTimeout(() => {
t.is(startCount, 2, 'onTransitionStart() called twice');
t.is(interruptCount, 1, 'onTransitionInterrupt() called once');
t.is(endCount, 1, 'onTransitionEnd() called once');
t.ok(updateCount > 2, 'onViewStateChange() called');
t.end();
}, 1000);
transitionManager.updateTransition(200);
transitionManager.updateTransition(400);
transitionManager.updateTransition(600);
transitionManager.updateTransition(800);

t.is(startCount, 2, 'onTransitionStart() called twice');
t.is(interruptCount, 1, 'onTransitionInterrupt() called once');
t.is(endCount, 1, 'onTransitionEnd() called once');
t.is(updateCount, 3, 'onViewStateChange() called');
t.end();
});

0 comments on commit 5f42779

Please sign in to comment.