From 5794ff61bc205300f634674d8679281a4cba7fb3 Mon Sep 17 00:00:00 2001 From: Ryan Gomba Date: Thu, 20 Oct 2016 14:12:37 -0700 Subject: [PATCH] Add support for clamping for NativeAnimated on iOS Summary: This diff adds support for clamping on iOS. It separates out code originally submitted in #9048. Test plan (required) Run UIExplorer NativeAnimated examples before and after - compare the results. Pay special attention to the new clamped spring example. Closes https://github.com/facebook/react-native/pull/9625 Differential Revision: D4053231 fbshipit-source-id: 29048de444ff5f6d7fe7dce7897399b483ee6d2d --- .../UIExplorer/js/NativeAnimationsExample.js | 5 ++- .../Nodes/RCTAnimationDriverNode.m | 13 ++++++- .../Nodes/RCTInterpolationAnimatedNode.m | 21 ++++++---- Libraries/NativeAnimation/RCTAnimationUtils.h | 14 +++++-- Libraries/NativeAnimation/RCTAnimationUtils.m | 38 ++++++++++++++++--- 5 files changed, 70 insertions(+), 21 deletions(-) diff --git a/Examples/UIExplorer/js/NativeAnimationsExample.js b/Examples/UIExplorer/js/NativeAnimationsExample.js index ecafb4751b..62f7100f8c 100644 --- a/Examples/UIExplorer/js/NativeAnimationsExample.js +++ b/Examples/UIExplorer/js/NativeAnimationsExample.js @@ -320,7 +320,7 @@ exports.examples = [ }, }, { - title: 'Scale interpolation', + title: 'Scale interpolation with clamping', description: 'description', render: function() { return ( @@ -335,8 +335,9 @@ exports.examples = [ transform: [ { scale: anim.interpolate({ - inputRange: [0, 1], + inputRange: [0, 0.5], outputRange: [1, 1.4], + extrapolateRight: 'clamp', }) } ], diff --git a/Libraries/NativeAnimation/Nodes/RCTAnimationDriverNode.m b/Libraries/NativeAnimation/Nodes/RCTAnimationDriverNode.m index eb0a8d802a..ae15dc84c2 100644 --- a/Libraries/NativeAnimation/Nodes/RCTAnimationDriverNode.m +++ b/Libraries/NativeAnimation/Nodes/RCTAnimationDriverNode.m @@ -118,14 +118,23 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) fromInterval, toInterval, fromFrameValue.doubleValue, - toFrameValue.doubleValue); + toFrameValue.doubleValue, + EXTRAPOLATE_TYPE_EXTEND, + EXTRAPOLATE_TYPE_EXTEND); [self updateOutputWithFrameOutput:frameOutput]; } - (void)updateOutputWithFrameOutput:(CGFloat)frameOutput { - CGFloat outputValue = RCTInterpolateValue(frameOutput, 0, 1, _fromValue, _toValue); + CGFloat outputValue = RCTInterpolateValue(frameOutput, + 0, + 1, + _fromValue, + _toValue, + EXTRAPOLATE_TYPE_EXTEND, + EXTRAPOLATE_TYPE_EXTEND); + _outputValue = @(outputValue); _valueNode.value = outputValue; [_valueNode setNeedsUpdate]; diff --git a/Libraries/NativeAnimation/Nodes/RCTInterpolationAnimatedNode.m b/Libraries/NativeAnimation/Nodes/RCTInterpolationAnimatedNode.m index bae72c5914..a87f3c485a 100644 --- a/Libraries/NativeAnimation/Nodes/RCTInterpolationAnimatedNode.m +++ b/Libraries/NativeAnimation/Nodes/RCTInterpolationAnimatedNode.m @@ -15,6 +15,8 @@ __weak RCTValueAnimatedNode *_parentNode; NSArray *_inputRange; NSArray *_outputRange; + NSString *_extrapolateLeft; + NSString *_extrapolateRight; } - (instancetype)initWithTag:(NSNumber *)tag @@ -29,6 +31,8 @@ } } _outputRange = [outputRange copy]; + _extrapolateLeft = config[@"extrapolateLeft"]; + _extrapolateRight = config[@"extrapolateRight"]; } return self; } @@ -70,19 +74,20 @@ return; } - NSUInteger rangeIndex = [self findIndexOfNearestValue:_parentNode.value - inRange:_inputRange]; + CGFloat inputValue = _parentNode.value; + NSUInteger rangeIndex = [self findIndexOfNearestValue:inputValue inRange:_inputRange]; NSNumber *inputMin = _inputRange[rangeIndex]; NSNumber *inputMax = _inputRange[rangeIndex + 1]; NSNumber *outputMin = _outputRange[rangeIndex]; NSNumber *outputMax = _outputRange[rangeIndex + 1]; - CGFloat outputValue = RCTInterpolateValue(_parentNode.value, - inputMin.doubleValue, - inputMax.doubleValue, - outputMin.doubleValue, - outputMax.doubleValue); - self.value = outputValue; + self.value = RCTInterpolateValue(inputValue, + inputMin.doubleValue, + inputMax.doubleValue, + outputMin.doubleValue, + outputMax.doubleValue, + _extrapolateLeft, + _extrapolateRight); } @end diff --git a/Libraries/NativeAnimation/RCTAnimationUtils.h b/Libraries/NativeAnimation/RCTAnimationUtils.h index f34a9c6ee8..9f0d3c1686 100644 --- a/Libraries/NativeAnimation/RCTAnimationUtils.h +++ b/Libraries/NativeAnimation/RCTAnimationUtils.h @@ -12,11 +12,17 @@ #import "RCTDefines.h" +static NSString * const EXTRAPOLATE_TYPE_IDENTITY = @"identity"; +static NSString * const EXTRAPOLATE_TYPE_CLAMP = @"clamp"; +static NSString * const EXTRAPOLATE_TYPE_EXTEND = @"extend"; + RCT_EXTERN CGFloat RCTInterpolateValue(CGFloat value, - CGFloat fromMin, - CGFloat fromMax, - CGFloat toMin, - CGFloat toMax); + CGFloat inputMin, + CGFloat inputMax, + CGFloat outputMin, + CGFloat outputMax, + NSString *extrapolateLeft, + NSString *extrapolateRight); RCT_EXTERN CGFloat RCTRadiansToDegrees(CGFloat radians); RCT_EXTERN CGFloat RCTDegreesToRadians(CGFloat degrees); diff --git a/Libraries/NativeAnimation/RCTAnimationUtils.m b/Libraries/NativeAnimation/RCTAnimationUtils.m index 720edddbd4..2ba15b0af4 100644 --- a/Libraries/NativeAnimation/RCTAnimationUtils.m +++ b/Libraries/NativeAnimation/RCTAnimationUtils.m @@ -9,16 +9,44 @@ #import "RCTAnimationUtils.h" +#import "RCTLog.h" + /** * Interpolates value by remapping it linearly fromMin->fromMax to toMin->toMax */ CGFloat RCTInterpolateValue(CGFloat value, - CGFloat fromMin, - CGFloat fromMax, - CGFloat toMin, - CGFloat toMax) + CGFloat inputMin, + CGFloat inputMax, + CGFloat outputMin, + CGFloat outputMax, + NSString *extrapolateLeft, + NSString *extrapolateRight) { - return toMin + (value - fromMin) * (toMax - toMin) / (fromMax - fromMin); + if (value < inputMin) { + if ([extrapolateLeft isEqualToString:EXTRAPOLATE_TYPE_IDENTITY]) { + return value; + } else if ([extrapolateLeft isEqualToString:EXTRAPOLATE_TYPE_CLAMP]) { + value = inputMin; + } else if ([extrapolateLeft isEqualToString:EXTRAPOLATE_TYPE_EXTEND]) { + // noop + } else { + RCTLogError(@"Invalid extrapolation type %@ for left extrapolation", extrapolateLeft); + } + } + + if (value > inputMax) { + if ([extrapolateRight isEqualToString:EXTRAPOLATE_TYPE_IDENTITY]) { + return value; + } else if ([extrapolateRight isEqualToString:EXTRAPOLATE_TYPE_CLAMP]) { + value = inputMax; + } else if ([extrapolateRight isEqualToString:EXTRAPOLATE_TYPE_EXTEND]) { + // noop + } else { + RCTLogError(@"Invalid extrapolation type %@ for right extrapolation", extrapolateRight); + } + } + + return outputMin + (value - inputMin) * (outputMax - outputMin) / (inputMax - inputMin); } CGFloat RCTRadiansToDegrees(CGFloat radians)