|
9 | 9 |
|
10 | 10 | #import "RCTAnimationUtils.h"
|
11 | 11 |
|
| 12 | +static NSRegularExpression *regex; |
| 13 | + |
12 | 14 | @implementation RCTInterpolationAnimatedNode
|
13 | 15 | {
|
14 | 16 | __weak RCTValueAnimatedNode *_parentNode;
|
15 | 17 | NSArray<NSNumber *> *_inputRange;
|
16 | 18 | NSArray<NSNumber *> *_outputRange;
|
| 19 | + NSArray<NSArray<NSNumber *> *> *_outputs; |
| 20 | + NSArray<NSString *> *_soutputRange; |
17 | 21 | NSString *_extrapolateLeft;
|
18 | 22 | NSString *_extrapolateRight;
|
| 23 | + NSUInteger _numVals; |
| 24 | + bool _hasStringOutput; |
| 25 | + bool _shouldRound; |
| 26 | + NSArray<NSTextCheckingResult*> *_matches; |
19 | 27 | }
|
20 | 28 |
|
21 | 29 | - (instancetype)initWithTag:(NSNumber *)tag
|
22 | 30 | config:(NSDictionary<NSString *, id> *)config
|
23 | 31 | {
|
| 32 | + static dispatch_once_t onceToken; |
| 33 | + dispatch_once(&onceToken, ^{ |
| 34 | + NSString *fpRegex = @"[+-]?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?"; |
| 35 | + regex = [NSRegularExpression regularExpressionWithPattern:fpRegex options:NSRegularExpressionCaseInsensitive error:nil]; |
| 36 | + }); |
24 | 37 | if ((self = [super initWithTag:tag config:config])) {
|
25 | 38 | _inputRange = [config[@"inputRange"] copy];
|
26 | 39 | NSMutableArray *outputRange = [NSMutableArray array];
|
| 40 | + NSMutableArray *soutputRange = [NSMutableArray array]; |
| 41 | + NSMutableArray<NSMutableArray<NSNumber *> *> *_outputRanges = [NSMutableArray array]; |
| 42 | + |
| 43 | + _hasStringOutput = NO; |
27 | 44 | for (id value in config[@"outputRange"]) {
|
28 | 45 | if ([value isKindOfClass:[NSNumber class]]) {
|
29 | 46 | [outputRange addObject:value];
|
| 47 | + } else if ([value isKindOfClass:[NSString class]]) { |
| 48 | + /** |
| 49 | + * Supports string shapes by extracting numbers so new values can be computed, |
| 50 | + * and recombines those values into new strings of the same shape. Supports |
| 51 | + * things like: |
| 52 | + * |
| 53 | + * rgba(123, 42, 99, 0.36) // colors |
| 54 | + * -45deg // values with units |
| 55 | + */ |
| 56 | + NSMutableArray *output = [NSMutableArray array]; |
| 57 | + [_outputRanges addObject:output]; |
| 58 | + [soutputRange addObject:value]; |
| 59 | + |
| 60 | + _matches = [regex matchesInString:value options:0 range:NSMakeRange(0, [value length])]; |
| 61 | + for (NSTextCheckingResult *match in _matches) { |
| 62 | + NSString* strNumber = [value substringWithRange:match.range]; |
| 63 | + [output addObject:[NSNumber numberWithDouble:strNumber.doubleValue]]; |
| 64 | + } |
| 65 | + |
| 66 | + _hasStringOutput = YES; |
| 67 | + [outputRange addObject:[output objectAtIndex:0]]; |
| 68 | + } |
| 69 | + } |
| 70 | + if (_hasStringOutput) { |
| 71 | + // ['rgba(0, 100, 200, 0)', 'rgba(50, 150, 250, 0.5)'] |
| 72 | + // -> |
| 73 | + // [ |
| 74 | + // [0, 50], |
| 75 | + // [100, 150], |
| 76 | + // [200, 250], |
| 77 | + // [0, 0.5], |
| 78 | + // ] |
| 79 | + _numVals = [_matches count]; |
| 80 | + NSString *value = [soutputRange objectAtIndex:0]; |
| 81 | + _shouldRound = [value containsString:@"rgb"]; |
| 82 | + _matches = [regex matchesInString:value options:0 range:NSMakeRange(0, [value length])]; |
| 83 | + NSMutableArray<NSMutableArray<NSNumber *> *> *outputs = [NSMutableArray arrayWithCapacity:_numVals]; |
| 84 | + NSUInteger size = [soutputRange count]; |
| 85 | + for (NSUInteger j = 0; j < _numVals; j++) { |
| 86 | + NSMutableArray *output = [NSMutableArray arrayWithCapacity:size]; |
| 87 | + [outputs addObject:output]; |
| 88 | + for (int i = 0; i < size; i++) { |
| 89 | + [output addObject:[[_outputRanges objectAtIndex:i] objectAtIndex:j]]; |
| 90 | + } |
30 | 91 | }
|
| 92 | + _outputs = [outputs copy]; |
31 | 93 | }
|
32 | 94 | _outputRange = [outputRange copy];
|
| 95 | + _soutputRange = [soutputRange copy]; |
33 | 96 | _extrapolateLeft = config[@"extrapolateLeft"];
|
34 | 97 | _extrapolateRight = config[@"extrapolateRight"];
|
35 | 98 | }
|
@@ -61,11 +124,48 @@ - (void)performUpdate
|
61 | 124 |
|
62 | 125 | CGFloat inputValue = _parentNode.value;
|
63 | 126 |
|
64 |
| - self.value = RCTInterpolateValueInRange(inputValue, |
65 |
| - _inputRange, |
66 |
| - _outputRange, |
67 |
| - _extrapolateLeft, |
68 |
| - _extrapolateRight); |
| 127 | + CGFloat interpolated = RCTInterpolateValueInRange(inputValue, |
| 128 | + _inputRange, |
| 129 | + _outputRange, |
| 130 | + _extrapolateLeft, |
| 131 | + _extrapolateRight); |
| 132 | + self.value = interpolated; |
| 133 | + if (_hasStringOutput) { |
| 134 | + // 'rgba(0, 100, 200, 0)' |
| 135 | + // -> |
| 136 | + // 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...' |
| 137 | + if (_numVals > 1) { |
| 138 | + NSString *text = _soutputRange[0]; |
| 139 | + NSMutableString *formattedText = [NSMutableString stringWithString:text]; |
| 140 | + NSUInteger i = _numVals; |
| 141 | + for (NSTextCheckingResult *match in [_matches reverseObjectEnumerator]) { |
| 142 | + CGFloat val = RCTInterpolateValueInRange(inputValue, |
| 143 | + _inputRange, |
| 144 | + _outputs[--i], |
| 145 | + _extrapolateLeft, |
| 146 | + _extrapolateRight); |
| 147 | + NSString *str; |
| 148 | + if (_shouldRound) { |
| 149 | + // rgba requires that the r,g,b are integers.... so we want to round them, but we *dont* want to |
| 150 | + // round the opacity (4th column). |
| 151 | + bool isAlpha = i == 3; |
| 152 | + CGFloat rounded = isAlpha ? round(val * 1000) / 1000 : round(val); |
| 153 | + str = isAlpha ? [NSString stringWithFormat:@"%1.3f", rounded] : [NSString stringWithFormat:@"%1.0f", rounded]; |
| 154 | + } else { |
| 155 | + NSNumber *numberValue = [NSNumber numberWithDouble:val]; |
| 156 | + str = [numberValue stringValue]; |
| 157 | + } |
| 158 | + |
| 159 | + [formattedText replaceCharactersInRange:[match range] withString:str]; |
| 160 | + } |
| 161 | + self.animatedObject = formattedText; |
| 162 | + } else { |
| 163 | + self.animatedObject = [regex stringByReplacingMatchesInString:_soutputRange[0] |
| 164 | + options:0 |
| 165 | + range:NSMakeRange(0, _soutputRange[0].length) |
| 166 | + withTemplate:[NSString stringWithFormat:@"%1f", interpolated]]; |
| 167 | + } |
| 168 | + } |
69 | 169 | }
|
70 | 170 |
|
71 | 171 | @end
|
0 commit comments