Skip to content

Commit

Permalink
Add AnimatedDiffClamp node
Browse files Browse the repository at this point in the history
Summary:
This adds a new type of node that clamps an animated value between 2 values with a special twist, it is based on the difference between the previous value so getting far from a bound doesn't matter and as soon as we start getting closer again the value will start changing. The main use case for this node is to create a collapsible navbar when scrolling a scrollview. This is a pretty in apps (fb, youtube, twitter, all use something like this).

It updates using the following: `value = clamp(value + diff, min, max)` where `diff` is the difference with the previous value.

This gives the following output for parameters min = 0, max = 30:
```
  in     out
  0      0
  15     15
  30     30
  100    30
  90     20
  30     0
  50     20
```

One issue I see is that this node is pretty specific to this use case but I can't see another simple way to do this with Animated that can also be offloaded to native easily. I'd be glad to discuss other solutions if some
Closes facebook#9419

Differential Revision: D3753920

fbshipit-source-id: 40a749d38fd003aab2d3cb5cb8f0535e467d8a2a
  • Loading branch information
janicduplessis authored and ide committed Sep 6, 2016
1 parent 87c5a25 commit 920b75c
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 0 deletions.
56 changes: 56 additions & 0 deletions Libraries/Animated/src/AnimatedImplementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,43 @@ class AnimatedModulo extends AnimatedWithChildren {
}
}

class AnimatedDiffClamp extends AnimatedWithChildren {
_a: Animated;
_min: number;
_max: number;
_value: number;
_lastValue: number;

constructor(a: Animated, min: number, max: number) {
super();

this._a = a;
this._min = min;
this._max = max;
this._value = this._lastValue = this._a.__getValue();
}

interpolate(config: InterpolationConfigType): AnimatedInterpolation {
return new AnimatedInterpolation(this, config);
}

__getValue(): number {
const value = this._a.__getValue();
const diff = value - this._lastValue;
this._lastValue = value;
this._value = Math.min(Math.max(this._value + diff, this._min), this._max);
return this._value;
}

__attach(): void {
this._a.__addChild(this);
}

__detach(): void {
this._a.__removeChild(this);
}
}

class AnimatedTransform extends AnimatedWithChildren {
_transforms: Array<Object>;

Expand Down Expand Up @@ -1712,6 +1749,14 @@ var modulo = function(
return new AnimatedModulo(a, modulus);
};

var diffClamp = function(
a: Animated,
min: number,
max: number,
): AnimatedDiffClamp {
return new AnimatedDiffClamp(a, min, max);
};

const _combineCallbacks = function(callback: ?EndCallback, config : AnimationConfig) {
if (callback && config.onComplete) {
return (...args) => {
Expand Down Expand Up @@ -2103,6 +2148,17 @@ module.exports = {
*/
modulo,

/**
* Create a new Animated value that is limited between 2 values. It uses the
* difference between the last value so even if the value is far from the bounds
* it will start changing when the value starts getting closer again.
* (`value = clamp(value + diff, min, max)`).
*
* This is useful with scroll events, for example, to show the navbar when
* scrolling up and to hide it when scrolling down.
*/
diffClamp,

/**
* Starts an animation after the given delay.
*/
Expand Down
13 changes: 13 additions & 0 deletions Libraries/Animated/src/__tests__/Animated-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -567,3 +567,16 @@ describe('Animated Listeners', () => {
expect(listener.mock.calls.length).toBe(4);
});
});

describe('Animated Diff Clamp', () => {
it('should get the proper value', () => {
const inputValues = [0, 20, 40, 30, 0, -40, -10, -20, 0];
const expectedValues = [0, 20, 20, 10, 0, 0, 20, 10, 20];
const value = new Animated.Value(0);
const diffClampValue = Animated.diffClamp(value, 0, 20);
for (let i = 0; i < inputValues.length; i++) {
value.setValue(inputValues[i]);
expect(diffClampValue.__getValue()).toBe(expectedValues[i]);
}
});
});

0 comments on commit 920b75c

Please sign in to comment.