From 73191edb7255b1ba5e9a0955a25c14250186a676 Mon Sep 17 00:00:00 2001 From: Genki Kondo Date: Fri, 20 May 2022 10:42:40 -0700 Subject: [PATCH] Add optional configs to constructors for AnimatedValue/ValueXY/Color Summary: There are use cases of needing to setValue multiple times per second (for example, using PanResponder to update a slider component). This requires the use of the native driver for perf reasons as using the JS driver will cause a rerender. Currently, the only way to make an animated node native is via setting useNativeDriver: true on an animation config. For example: ``` Animated.timing(animatedValue, { toValue: newValue, duration: 0, useNativeDriver: true }).start(); ``` To avoid needing to call the above, add a useNativeDriver param to the constructor. When set to true, the node will be made native immediately. ``` const animatedValue = new Animated.Value(0, useNativeDriver); ... animatedValue.setValue(newValue); ``` Note that, like with useNativeDriver in the animation config, once a node is made native, it cannot be reverted to JS-only. --- As an aside, PanResponder uses JS-side events, and thus we cannot use Animated.event with native driver; we instead need to setValue on a native AnimatedValue. A much more thorough explanation is in D34564598. --- Changelog: [General][Added] - [Animated] Add useNativeDriver as a param for setValue Reviewed By: JoshuaGross Differential Revision: D36459457 fbshipit-source-id: 284148a6d16537429efeab8b07184019990909cd --- Libraries/Animated/nodes/AnimatedColor.js | 17 +++++++------ Libraries/Animated/nodes/AnimatedValue.js | 9 ++++++- Libraries/Animated/nodes/AnimatedValueXY.js | 27 +++++++++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/Libraries/Animated/nodes/AnimatedColor.js b/Libraries/Animated/nodes/AnimatedColor.js index 34ee9dcb68..8901a97d0d 100644 --- a/Libraries/Animated/nodes/AnimatedColor.js +++ b/Libraries/Animated/nodes/AnimatedColor.js @@ -21,6 +21,9 @@ import type {ColorValue} from '../../StyleSheet/StyleSheet'; import type {NativeColorValue} from '../../StyleSheet/PlatformColorValueTypes'; import type {ProcessedColorValue} from '../../StyleSheet/processColor'; +export type AnimatedColorConfig = $ReadOnly<{ + useNativeDriver: boolean, +}>; type ColorListenerCallback = (value: string) => mixed; export type RgbaValue = { +r: number, @@ -116,7 +119,10 @@ export default class AnimatedColor extends AnimatedWithChildren { ... } = {}; - constructor(valueIn?: ?(RgbaValue | RgbaAnimatedValue | ColorValue)) { + constructor( + valueIn?: ?(RgbaValue | RgbaAnimatedValue | ColorValue), + config?: ?AnimatedColorConfig, + ) { super(); let value: RgbaValue | RgbaAnimatedValue | ColorValue = valueIn ?? defaultColor; @@ -144,12 +150,9 @@ export default class AnimatedColor extends AnimatedWithChildren { this.g = new AnimatedValue(initColor.g); this.b = new AnimatedValue(initColor.b); this.a = new AnimatedValue(initColor.a); - - if (this.nativeColor) { - if (!this.__isNative) { - this.__makeNative(); - } - } + } + if (this.nativeColor || (config && config.useNativeDriver)) { + this.__makeNative(); } } diff --git a/Libraries/Animated/nodes/AnimatedValue.js b/Libraries/Animated/nodes/AnimatedValue.js index c6f021467f..0625c83687 100644 --- a/Libraries/Animated/nodes/AnimatedValue.js +++ b/Libraries/Animated/nodes/AnimatedValue.js @@ -20,6 +20,10 @@ import type Animation, {EndCallback} from '../animations/Animation'; import type {InterpolationConfigType} from './AnimatedInterpolation'; import type AnimatedTracking from './AnimatedTracking'; +export type AnimatedValueConfig = $ReadOnly<{ + useNativeDriver: boolean, +}>; + const NativeAnimatedAPI = NativeAnimatedHelper.API; /** @@ -87,7 +91,7 @@ class AnimatedValue extends AnimatedWithChildren { _animation: ?Animation; _tracking: ?AnimatedTracking; - constructor(value: number) { + constructor(value: number, config?: ?AnimatedValueConfig) { super(); if (typeof value !== 'number') { throw new Error('AnimatedValue: Attempting to set value to undefined'); @@ -95,6 +99,9 @@ class AnimatedValue extends AnimatedWithChildren { this._startingValue = this._value = value; this._offset = 0; this._animation = null; + if (config && config.useNativeDriver) { + this.__makeNative(); + } } __detach() { diff --git a/Libraries/Animated/nodes/AnimatedValueXY.js b/Libraries/Animated/nodes/AnimatedValueXY.js index bc1f80fe29..71d1129c6b 100644 --- a/Libraries/Animated/nodes/AnimatedValueXY.js +++ b/Libraries/Animated/nodes/AnimatedValueXY.js @@ -10,11 +10,16 @@ 'use strict'; +import type {PlatformConfig} from '../AnimatedPlatformConfig'; + const AnimatedValue = require('./AnimatedValue'); const AnimatedWithChildren = require('./AnimatedWithChildren'); const invariant = require('invariant'); +export type AnimatedValueXYConfig = $ReadOnly<{ + useNativeDriver: boolean, +}>; type ValueXYListenerCallback = (value: { x: number, y: number, @@ -47,6 +52,7 @@ class AnimatedValueXY extends AnimatedWithChildren { +y: number | AnimatedValue, ... }, + config?: ?AnimatedValueXYConfig, ) { super(); const value: any = valueIn || {x: 0, y: 0}; // @flowfixme: shouldn't need `: any` @@ -63,6 +69,9 @@ class AnimatedValueXY extends AnimatedWithChildren { this.y = value.y; } this._listeners = {}; + if (config && config.useNativeDriver) { + this.__makeNative(); + } } /** @@ -221,6 +230,24 @@ class AnimatedValueXY extends AnimatedWithChildren { getTranslateTransform(): Array<{[key: string]: AnimatedValue, ...}> { return [{translateX: this.x}, {translateY: this.y}]; } + + __attach(): void { + this.x.__addChild(this); + this.y.__addChild(this); + super.__attach(); + } + + __detach(): void { + this.x.__removeChild(this); + this.y.__removeChild(this); + super.__detach(); + } + + __makeNative(platformConfig: ?PlatformConfig) { + this.x.__makeNative(platformConfig); + this.y.__makeNative(platformConfig); + super.__makeNative(platformConfig); + } } module.exports = AnimatedValueXY;