Skip to content

Commit 8fe5dc2

Browse files
zeyapfacebook-github-bot
authored andcommitted
(js part) Support PlatformColor type of toValue and interpolation outputRange (facebook#54457)
Summary: ## Changelog: [General] [Added] (js part) Support PlatformColor type of toValue and interpolation outputRange Differential Revision: D86255713
1 parent 6bc4f8d commit 8fe5dc2

File tree

7 files changed

+130
-48
lines changed

7 files changed

+130
-48
lines changed

packages/react-native/Libraries/Animated/AnimatedImplementation.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,10 @@ const maybeVectorAnim = function (
129129
const configB = {...config};
130130
const configA = {...config};
131131
for (const key in config) {
132-
const {r, g, b, a} = config[key];
132+
const configValue = config[key];
133+
const {r, g, b, a} = configValue;
133134
if (
135+
// is RGBA
134136
r !== undefined &&
135137
g !== undefined &&
136138
b !== undefined &&
@@ -140,6 +142,34 @@ const maybeVectorAnim = function (
140142
configG[key] = g;
141143
configB[key] = b;
142144
configA[key] = a;
145+
} else if (
146+
// is PlatformColor
147+
typeof configValue === 'object' ||
148+
(configValue instanceof AnimatedColor &&
149+
configValue.nativeColor != null)
150+
) {
151+
const nativeColor =
152+
configValue instanceof AnimatedColor
153+
? configValue.nativeColor
154+
: configValue;
155+
if (nativeColor) {
156+
configR[key] = {
157+
nativeColor,
158+
channel: 'r',
159+
};
160+
configG[key] = {
161+
nativeColor,
162+
channel: 'g',
163+
};
164+
configB[key] = {
165+
nativeColor,
166+
channel: 'b',
167+
};
168+
configA[key] = {
169+
nativeColor,
170+
channel: 'a',
171+
};
172+
}
143173
}
144174
}
145175
const aR = anim((value: AnimatedColor).r, configR);

packages/react-native/Libraries/Animated/animations/TimingAnimation.js

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* @format
99
*/
1010

11+
import type {NativeColorValue} from '../../StyleSheet/StyleSheetTypes';
1112
import type {PlatformConfig} from '../AnimatedPlatformConfig';
1213
import type {RgbaValue} from '../nodes/AnimatedColor';
1314
import type AnimatedInterpolation from '../nodes/AnimatedInterpolation';
@@ -31,16 +32,24 @@ export type TimingAnimationConfig = $ReadOnly<{
3132
| AnimatedValueXY
3233
| RgbaValue
3334
| AnimatedColor
34-
| AnimatedInterpolation<number>,
35+
| AnimatedInterpolation<number>
36+
| NativeColorValue,
3537
easing?: (value: number) => number,
3638
duration?: number,
3739
delay?: number,
3840
...
3941
}>;
4042

43+
type TimingAnimationConfigSingleToValue =
44+
| number
45+
| $ReadOnly<{
46+
nativeColor: NativeColorValue,
47+
channel: 'r' | 'g' | 'b' | 'a',
48+
}>;
49+
4150
export type TimingAnimationConfigSingle = $ReadOnly<{
4251
...AnimationConfig,
43-
toValue: number,
52+
toValue: TimingAnimationConfigSingleToValue,
4453
easing?: (value: number) => number,
4554
duration?: number,
4655
delay?: number,
@@ -61,7 +70,7 @@ function easeInOut() {
6170
export default class TimingAnimation extends Animation {
6271
_startTime: number;
6372
_fromValue: number;
64-
_toValue: number;
73+
_toValue: TimingAnimationConfigSingleToValue;
6574
_duration: number;
6675
_delay: number;
6776
_easing: (value: number) => number;
@@ -83,7 +92,7 @@ export default class TimingAnimation extends Animation {
8392
__getNativeAnimationConfig(): $ReadOnly<{
8493
type: 'frames',
8594
frames: $ReadOnlyArray<number>,
86-
toValue: number,
95+
toValue: TimingAnimationConfigSingleToValue,
8796
iterations: number,
8897
platformConfig: ?PlatformConfig,
8998
...
@@ -122,14 +131,20 @@ export default class TimingAnimation extends Animation {
122131

123132
const useNativeDriver = this.__startAnimationIfNative(animatedValue);
124133
if (!useNativeDriver) {
125-
// Animations that sometimes have 0 duration and sometimes do not
126-
// still need to use the native driver when duration is 0 so as to
127-
// not cause intermixed JS and native animations.
128-
if (this._duration === 0) {
129-
this._onUpdate(this._toValue);
130-
this.__notifyAnimationEnd({finished: true});
131-
} else {
132-
this._animationFrame = requestAnimationFrame(() => this.onUpdate());
134+
if (typeof this._toValue === 'number') {
135+
// Animations that sometimes have 0 duration and sometimes do not
136+
// still need to use the native driver when duration is 0 so as to
137+
// not cause intermixed JS and native animations.
138+
if (this._duration === 0) {
139+
this._onUpdate(this._toValue);
140+
this.__notifyAnimationEnd({finished: true});
141+
} else {
142+
this._animationFrame = requestAnimationFrame(() => this.onUpdate());
143+
}
144+
} else if (this._toValue.nativeColor != null) {
145+
throw new Error(
146+
'Cannot run TimingAnimation to nativeColor with js driver',
147+
);
133148
}
134149
}
135150
};
@@ -141,13 +156,17 @@ export default class TimingAnimation extends Animation {
141156
}
142157

143158
onUpdate(): void {
159+
if (typeof this._toValue !== 'number') {
160+
return;
161+
}
162+
const toValue: number = this._toValue;
144163
const now = Date.now();
145164
if (now >= this._startTime + this._duration) {
146165
if (this._duration === 0) {
147-
this._onUpdate(this._toValue);
166+
this._onUpdate(toValue);
148167
} else {
149168
this._onUpdate(
150-
this._fromValue + this._easing(1) * (this._toValue - this._fromValue),
169+
this._fromValue + this._easing(1) * (toValue - this._fromValue),
151170
);
152171
}
153172
this.__notifyAnimationEnd({finished: true});
@@ -157,7 +176,7 @@ export default class TimingAnimation extends Animation {
157176
this._onUpdate(
158177
this._fromValue +
159178
this._easing((now - this._startTime) / this._duration) *
160-
(this._toValue - this._fromValue),
179+
(toValue - this._fromValue),
161180
);
162181
if (this.__active) {
163182
// $FlowFixMe[method-unbinding] added when improving typing for this parameters

packages/react-native/Libraries/Animated/createAnimatedComponent.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* @format
99
*/
1010

11+
import type {NativeColorValue} from '../StyleSheet/StyleSheetTypes';
1112
import type AnimatedAddition from './nodes/AnimatedAddition';
1213
import type AnimatedDiffClamp from './nodes/AnimatedDiffClamp';
1314
import type AnimatedDivision from './nodes/AnimatedDivision';
@@ -46,6 +47,7 @@ export type WithAnimatedValue<+T> = T extends Builtin | Nullable
4647
| AnimatedInterpolation<number | string>
4748
| AnimatedInterpolation<number>
4849
| AnimatedInterpolation<string>
50+
| AnimatedInterpolation<NativeColorValue>
4951
: T extends $ReadOnlyArray<infer P>
5052
? $ReadOnlyArray<WithAnimatedValue<P>>
5153
: T extends {...}

packages/react-native/Libraries/Animated/nodes/AnimatedInterpolation.js

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
'use strict';
1414

15+
import type {NativeColorValue} from '../../StyleSheet/StyleSheetTypes';
1516
import type {PlatformConfig} from '../AnimatedPlatformConfig';
1617
import type AnimatedNode from './AnimatedNode';
1718
import type {AnimatedNodeConfig} from './AnimatedNode';
@@ -26,7 +27,9 @@ import invariant from 'invariant';
2627

2728
type ExtrapolateType = 'extend' | 'identity' | 'clamp';
2829

29-
export type InterpolationConfigType<OutputT: number | string> = $ReadOnly<{
30+
export type InterpolationConfigType<
31+
OutputT: number | string | NativeColorValue,
32+
> = $ReadOnly<{
3033
...AnimatedNodeConfig,
3134
inputRange: $ReadOnlyArray<number>,
3235
outputRange: $ReadOnlyArray<OutputT>,
@@ -82,6 +85,23 @@ function createNumericInterpolation(
8285
};
8386
}
8487

88+
function createPlatformColorInterpolation(
89+
config: InterpolationConfigType<NativeColorValue>,
90+
): (input: number) => NativeColorValue {
91+
const outputRange = config.outputRange;
92+
const outputRangeIndices = Array.from(Array(outputRange.length).keys());
93+
const interpolateIndex = createNumericInterpolation({
94+
...config,
95+
inputRange: config.inputRange,
96+
outputRange: outputRangeIndices,
97+
});
98+
99+
return input => {
100+
// PlatformColor interpolation should happen natively, here we just use the closest color
101+
return outputRange[Math.floor(interpolateIndex(input))];
102+
};
103+
}
104+
85105
function interpolate(
86106
input: number,
87107
inputMin: number,
@@ -277,7 +297,7 @@ function findRange(input: number, inputRange: $ReadOnlyArray<number>) {
277297
return i - 1;
278298
}
279299

280-
function checkValidRanges<OutputT: number | string>(
300+
function checkValidRanges<OutputT: number | string | NativeColorValue>(
281301
inputRange: $ReadOnlyArray<number>,
282302
outputRange: $ReadOnlyArray<OutputT>,
283303
) {
@@ -304,7 +324,7 @@ function checkValidInputRange(arr: $ReadOnlyArray<number>) {
304324
}
305325
}
306326

307-
function checkInfiniteRange<OutputT: number | string>(
327+
function checkInfiniteRange<OutputT: number | string | NativeColorValue>(
308328
name: string,
309329
arr: $ReadOnlyArray<OutputT>,
310330
) {
@@ -322,7 +342,7 @@ function checkInfiniteRange<OutputT: number | string>(
322342
}
323343

324344
export default class AnimatedInterpolation<
325-
OutputT: number | string,
345+
OutputT: number | string | NativeColorValue,
326346
> extends AnimatedWithChildren {
327347
_parent: AnimatedNode;
328348
_config: InterpolationConfigType<OutputT>;
@@ -347,6 +367,10 @@ export default class AnimatedInterpolation<
347367
const config = this._config;
348368
if (config.outputRange && typeof config.outputRange[0] === 'string') {
349369
this._interpolation = (createStringInterpolation((config: any)): any);
370+
} else if (typeof config.outputRange[0] === 'object') {
371+
this._interpolation = (createPlatformColorInterpolation(
372+
(config: any),
373+
): any);
350374
} else {
351375
this._interpolation = (createNumericInterpolation((config: any)): any);
352376
}
@@ -403,6 +427,8 @@ export default class AnimatedInterpolation<
403427
return NativeAnimatedHelper.transformDataType(value);
404428
}
405429
}): any);
430+
} else if (typeof outputRange[0] === 'object') {
431+
outputType = 'platform_color';
406432
}
407433

408434
return {

packages/react-native/Libraries/Animated/nodes/AnimatedValue.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* @format
99
*/
1010

11+
import type {NativeColorValue} from '../../StyleSheet/StyleSheetTypes';
1112
import type {EventSubscription} from '../../vendor/emitter/EventEmitter';
1213
import type {PlatformConfig} from '../AnimatedPlatformConfig';
1314
import type Animation from '../animations/Animation';
@@ -298,7 +299,7 @@ export default class AnimatedValue extends AnimatedWithChildren {
298299
* Interpolates the value before updating the property, e.g. mapping 0-1 to
299300
* 0-10.
300301
*/
301-
interpolate<OutputT: number | string>(
302+
interpolate<OutputT: number | string | NativeColorValue>(
302303
config: InterpolationConfigType<OutputT>,
303304
): AnimatedInterpolation<OutputT> {
304305
return new AnimatedInterpolation(this, config);

0 commit comments

Comments
 (0)