Skip to content

Commit 3f49e67

Browse files
genkikondofacebook-github-bot
authored andcommitted
Support color animation with native driver for Android
Summary: Adds support for Animated.Color with native driver for Android. Reads the native config for the rbga channel AnimatedNodes, and on update(), converts the values into an integer (0xaarrggbb) Followup changes will include support for iOS and platform colors. Changelog: [Android][Added] - Support running animations with AnimatedColor with native driver Reviewed By: javache Differential Revision: D33833600 fbshipit-source-id: 2bf05c9715b603cf014ace09e9308b2bfd67f30a
1 parent 0ab0c5a commit 3f49e67

File tree

10 files changed

+128
-10
lines changed

10 files changed

+128
-10
lines changed

Libraries/Animated/animations/TimingAnimation.js

+2-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const {shouldUseNativeDriver} = require('../NativeAnimatedHelper');
1919

2020
import type {PlatformConfig} from '../AnimatedPlatformConfig';
2121
import type {AnimationConfig, EndCallback} from './Animation';
22+
import type {RgbaValue} from '../nodes/AnimatedColor';
2223

2324
import AnimatedColor from '../nodes/AnimatedColor';
2425

@@ -33,13 +34,7 @@ export type TimingAnimationConfig = {
3334
...
3435
}
3536
| AnimatedValueXY
36-
| {
37-
r: number,
38-
g: number,
39-
b: number,
40-
a: number,
41-
...
42-
}
37+
| RgbaValue
4338
| AnimatedColor
4439
| AnimatedInterpolation,
4540
easing?: (value: number) => number,

Libraries/Animated/nodes/AnimatedColor.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ import AnimatedWithChildren from './AnimatedWithChildren';
1515
import normalizeColor from '../../StyleSheet/normalizeColor';
1616
import {processColorObject} from '../../StyleSheet/PlatformColorValueTypes';
1717

18+
import type {PlatformConfig} from '../AnimatedPlatformConfig';
1819
import type {ColorValue} from '../../StyleSheet/StyleSheet';
1920
import type {NativeColorValue} from '../../StyleSheet/PlatformColorValueTypes';
2021

2122
type ColorListenerCallback = (value: string) => mixed;
22-
type RgbaValue = {
23+
export type RgbaValue = {
2324
+r: number,
2425
+g: number,
2526
+b: number,
@@ -263,4 +264,22 @@ export default class AnimatedColor extends AnimatedWithChildren {
263264
this.a.__removeChild(this);
264265
super.__detach();
265266
}
267+
268+
__makeNative(platformConfig: ?PlatformConfig) {
269+
this.r.__makeNative(platformConfig);
270+
this.g.__makeNative(platformConfig);
271+
this.b.__makeNative(platformConfig);
272+
this.a.__makeNative(platformConfig);
273+
super.__makeNative(platformConfig);
274+
}
275+
276+
__getNativeConfig(): {...} {
277+
return {
278+
type: 'color',
279+
r: this.r.__getNativeTag(),
280+
g: this.g.__getNativeTag(),
281+
b: this.b.__getNativeTag(),
282+
a: this.a.__getNativeTag(),
283+
};
284+
}
266285
}

ReactAndroid/src/main/java/com/facebook/react/animated/BUCK

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ rn_android_library(
2828
react_native_target("java/com/facebook/react/modules/core:core"),
2929
react_native_target("java/com/facebook/react/uimanager:uimanager"),
3030
react_native_target("java/com/facebook/react/uimanager/annotations:annotations"),
31+
react_native_target("java/com/facebook/react/views/view:view"),
3132
],
3233
exported_deps = [react_native_root_target(":FBReactNativeSpec")],
3334
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.animated;
9+
10+
import com.facebook.react.bridge.ReadableMap;
11+
import com.facebook.react.views.view.ColorUtil;
12+
13+
/** Animated node that represents a color. */
14+
/*package*/ class ColorAnimatedNode extends AnimatedNode {
15+
16+
private final NativeAnimatedNodesManager mNativeAnimatedNodesManager;
17+
private final int mRNodeId;
18+
private final int mGNodeId;
19+
private final int mBNodeId;
20+
private final int mANodeId;
21+
private int mColor;
22+
23+
public ColorAnimatedNode(
24+
ReadableMap config, NativeAnimatedNodesManager nativeAnimatedNodesManager) {
25+
mNativeAnimatedNodesManager = nativeAnimatedNodesManager;
26+
mRNodeId = config.getInt("r");
27+
mGNodeId = config.getInt("g");
28+
mBNodeId = config.getInt("b");
29+
mANodeId = config.getInt("a");
30+
31+
// TODO (T110930421): Support platform color
32+
}
33+
34+
public int getColor() {
35+
return mColor;
36+
}
37+
38+
@Override
39+
public void update() {
40+
AnimatedNode rNode = mNativeAnimatedNodesManager.getNodeById(mRNodeId);
41+
AnimatedNode gNode = mNativeAnimatedNodesManager.getNodeById(mGNodeId);
42+
AnimatedNode bNode = mNativeAnimatedNodesManager.getNodeById(mBNodeId);
43+
AnimatedNode aNode = mNativeAnimatedNodesManager.getNodeById(mANodeId);
44+
45+
double r = ((ValueAnimatedNode) rNode).getValue();
46+
double g = ((ValueAnimatedNode) gNode).getValue();
47+
double b = ((ValueAnimatedNode) bNode).getValue();
48+
double a = ((ValueAnimatedNode) aNode).getValue();
49+
50+
mColor = ColorUtil.normalize(r, g, b, a);
51+
}
52+
53+
@Override
54+
public String prettyPrint() {
55+
return "ColorAnimatedNode["
56+
+ mTag
57+
+ "]: r: "
58+
+ mRNodeId
59+
+ " g: "
60+
+ mGNodeId
61+
+ " b: "
62+
+ mBNodeId
63+
+ " a: "
64+
+ mANodeId;
65+
}
66+
}

ReactAndroid/src/main/java/com/facebook/react/animated/NativeAnimatedNodesManager.java

+2
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ public void createAnimatedNode(int tag, ReadableMap config) {
129129
node = new StyleAnimatedNode(config, this);
130130
} else if ("value".equals(type)) {
131131
node = new ValueAnimatedNode(config);
132+
} else if ("color".equals(type)) {
133+
node = new ColorAnimatedNode(config, this);
132134
} else if ("props".equals(type)) {
133135
node = new PropsAnimatedNode(config, this);
134136
} else if ("interpolation".equals(type)) {

ReactAndroid/src/main/java/com/facebook/react/animated/PropsAnimatedNode.java

+2
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ public final void updateView() {
105105
} else {
106106
mPropMap.putDouble(entry.getKey(), ((ValueAnimatedNode) node).getValue());
107107
}
108+
} else if (node instanceof ColorAnimatedNode) {
109+
mPropMap.putInt(entry.getKey(), ((ColorAnimatedNode) node).getColor());
108110
} else {
109111
throw new IllegalArgumentException(
110112
"Unsupported type of node used in property node " + node.getClass());

ReactAndroid/src/main/java/com/facebook/react/animated/StyleAnimatedNode.java

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public void collectViewUpdates(JavaOnlyMap propsMap) {
4343
((TransformAnimatedNode) node).collectViewUpdates(propsMap);
4444
} else if (node instanceof ValueAnimatedNode) {
4545
propsMap.putDouble(entry.getKey(), ((ValueAnimatedNode) node).getValue());
46+
} else if (node instanceof ColorAnimatedNode) {
47+
propsMap.putInt(entry.getKey(), ((ColorAnimatedNode) node).getColor());
4648
} else {
4749
throw new IllegalArgumentException(
4850
"Unsupported type of node used in property node " + node.getClass());

ReactAndroid/src/main/java/com/facebook/react/views/view/ColorUtil.java

+19
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public static int multiplyColorAlpha(int color, int alpha) {
3939
/**
4040
* Gets the opacity from a color. Inspired by Android ColorDrawable.
4141
*
42+
* @param color color to get opacity from
4243
* @return opacity expressed by one of PixelFormat constants
4344
*/
4445
public static int getOpacityFromColor(int color) {
@@ -51,4 +52,22 @@ public static int getOpacityFromColor(int color) {
5152
return PixelFormat.TRANSLUCENT;
5253
}
5354
}
55+
56+
/**
57+
* Converts individual {r, g, b, a} channel values to a single integer representation of the color
58+
* as 0xAARRGGBB.
59+
*
60+
* @param r red channel value, [0, 255]
61+
* @param g green channel value, [0, 255]
62+
* @param b blue channel value, [0, 255]
63+
* @param a alpha channel value, [0, 1]
64+
* @return integer representation of the color as 0xAARRGGBB
65+
*/
66+
public static int normalize(double r, double g, double b, double a) {
67+
return (clamp255(a * 255) << 24) | (clamp255(r) << 16) | (clamp255(g) << 8) | clamp255(b);
68+
}
69+
70+
private static int clamp255(double value) {
71+
return Math.max(0, Math.min(255, (int) Math.round(value)));
72+
}
5473
}

ReactAndroid/src/test/java/com/facebook/react/views/BUCK

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ load("//tools/build_defs/oss:rn_defs.bzl", "YOGA_TARGET", "react_native_dep", "r
22

33
rn_robolectric_test(
44
name = "views",
5-
# TODO Disabled temporarily until Yoga linking is fixed t14964130
5+
# TODO (T110934492): Disabled temporarily until tests are fixed
66
# srcs = glob(['**/*.java']),
7-
srcs = glob(["image/*.java"]),
7+
srcs = glob([
8+
"image/*.java",
9+
"view/*.java",
10+
]),
811
contacts = ["[email protected]"],
912
deps = [
1013
YOGA_TARGET,

ReactAndroid/src/test/java/com/facebook/react/views/view/ColorUtilTest.java

+9
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,13 @@ public void testGetOpacityFromColor() {
4444
assertEquals(PixelFormat.OPAQUE, ColorUtil.getOpacityFromColor(0xFF123456));
4545
assertEquals(PixelFormat.OPAQUE, ColorUtil.getOpacityFromColor(0xFFFFFFFF));
4646
}
47+
48+
@Test
49+
public void testNormalize() {
50+
assertEquals(0x800B1621, ColorUtil.normalize(11, 22, 33, 0.5));
51+
assertEquals(0x00000000, ColorUtil.normalize(0, 0, 0, 0));
52+
assertEquals(0xFFFFFFFF, ColorUtil.normalize(255, 255, 255, 1));
53+
assertEquals(0xFF00FFFF, ColorUtil.normalize(-1, 256, 255, 1.1));
54+
assertEquals(0x000001FF, ColorUtil.normalize(0.4, 0.5, 255.4, -1));
55+
}
4756
}

0 commit comments

Comments
 (0)