From d057dfc3f57123865e1d65025d0d73e116b08005 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Fri, 28 Jun 2019 14:37:04 -0700 Subject: [PATCH] [change] Update Animated react-native@v0.60.0-rc.3 Fix #1378 Fix #1325 Close #1306 --- .../src/exports/Animated/index.js | 12 +- .../react-native/Animated/AnimatedEvent.js | 2 +- .../Animated/AnimatedImplementation.js | 48 +++- .../vendor/react-native/Animated/Easing.js | 47 ++-- .../Animated/NativeAnimatedHelper.js | 126 +++++++--- .../Animated/NativeAnimatedModule.js | 70 ++++++ .../react-native/Animated/SpringConfig.js | 31 +-- .../Animated/animations/Animation.js | 4 +- .../Animated/animations/DecayAnimation.js | 11 +- .../Animated/animations/SpringAnimation.js | 47 ++-- .../Animated/animations/TimingAnimation.js | 11 +- .../vendor/react-native/Animated/bezier.js | 231 +++++++++++------- .../Animated/createAnimatedComponent.js | 15 +- .../Animated/nodes/AnimatedInterpolation.js | 31 +-- .../Animated/nodes/AnimatedNode.js | 114 ++++++++- .../Animated/nodes/AnimatedProps.js | 1 + .../Animated/nodes/AnimatedStyle.js | 6 +- .../Animated/nodes/AnimatedSubtraction.js | 62 +++++ .../Animated/nodes/AnimatedValue.js | 96 +------- .../Animated/nodes/AnimatedWithChildren.js | 16 +- .../react-native/TurboModule/RCTExport.js | 35 +++ .../TurboModule/TurboModuleRegistry.js | 28 +++ 22 files changed, 692 insertions(+), 352 deletions(-) create mode 100644 packages/react-native-web/src/vendor/react-native/Animated/NativeAnimatedModule.js create mode 100644 packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedSubtraction.js create mode 100644 packages/react-native-web/src/vendor/react-native/TurboModule/RCTExport.js create mode 100644 packages/react-native-web/src/vendor/react-native/TurboModule/TurboModuleRegistry.js diff --git a/packages/react-native-web/src/exports/Animated/index.js b/packages/react-native-web/src/exports/Animated/index.js index 3c06c4aa4..06b550240 100644 --- a/packages/react-native-web/src/exports/Animated/index.js +++ b/packages/react-native-web/src/exports/Animated/index.js @@ -8,15 +8,25 @@ */ import AnimatedImplementation from '../../vendor/react-native/Animated/AnimatedImplementation'; +import FlatList from '../FlatList'; import Image from '../Image'; +import SectionList from '../SectionList'; import ScrollView from '../ScrollView'; import Text from '../Text'; import View from '../View'; const Animated = { ...AnimatedImplementation, + FlatList: AnimatedImplementation.createAnimatedComponent(FlatList, { + scrollEventThrottle: 0.0001 + }), Image: AnimatedImplementation.createAnimatedComponent(Image), - ScrollView: AnimatedImplementation.createAnimatedComponent(ScrollView), + ScrollView: AnimatedImplementation.createAnimatedComponent(ScrollView, { + scrollEventThrottle: 0.0001 + }), + SectionList: AnimatedImplementation.createAnimatedComponent(SectionList, { + scrollEventThrottle: 0.0001 + }), View: AnimatedImplementation.createAnimatedComponent(View), Text: AnimatedImplementation.createAnimatedComponent(Text) }; diff --git a/packages/react-native-web/src/vendor/react-native/Animated/AnimatedEvent.js b/packages/react-native-web/src/vendor/react-native/Animated/AnimatedEvent.js index e34991835..7195bcded 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/AnimatedEvent.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/AnimatedEvent.js @@ -158,7 +158,7 @@ class AnimatedEvent { }; } - _callListeners(...args) { + _callListeners(...args: any) { this._listeners.forEach(listener => listener(...args)); } diff --git a/packages/react-native-web/src/vendor/react-native/Animated/AnimatedImplementation.js b/packages/react-native-web/src/vendor/react-native/Animated/AnimatedImplementation.js index 242636d6a..8bd2ae198 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/AnimatedImplementation.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/AnimatedImplementation.js @@ -19,6 +19,7 @@ import AnimatedModulo from './nodes/AnimatedModulo'; import AnimatedMultiplication from './nodes/AnimatedMultiplication'; import AnimatedNode from './nodes/AnimatedNode'; import AnimatedProps from './nodes/AnimatedProps'; +import AnimatedSubtraction from './nodes/AnimatedSubtraction'; import AnimatedTracking from './nodes/AnimatedTracking'; import AnimatedValue from './nodes/AnimatedValue'; import AnimatedValueXY from './nodes/AnimatedValueXY'; @@ -28,11 +29,15 @@ import TimingAnimation from './animations/TimingAnimation'; import createAnimatedComponent from './createAnimatedComponent'; -import type { AnimationConfig, EndCallback, EndResult } from './animations/Animation'; -import type { TimingAnimationConfig } from './animations/TimingAnimation'; -import type { DecayAnimationConfig } from './animations/DecayAnimation'; -import type { SpringAnimationConfig } from './animations/SpringAnimation'; -import type { Mapping, EventConfig } from './AnimatedEvent'; +import type { + AnimationConfig, + EndCallback, + EndResult, +} from './animations/Animation'; +import type {TimingAnimationConfig} from './animations/TimingAnimation'; +import type {DecayAnimationConfig} from './animations/DecayAnimation'; +import type {SpringAnimationConfig} from './animations/SpringAnimation'; +import type {Mapping, EventConfig} from './AnimatedEvent'; export type CompositeAnimation = { start: (callback?: ?EndCallback) => void, @@ -49,6 +54,13 @@ const add = function( return new AnimatedAddition(a, b); }; +const subtract = function( + a: AnimatedNode | number, + b: AnimatedNode | number, +): AnimatedSubtraction { + return new AnimatedSubtraction(a, b); +}; + const divide = function( a: AnimatedNode | number, b: AnimatedNode | number, @@ -405,11 +417,14 @@ const stagger = function( ); }; -type LoopAnimationConfig = {iterations: number}; +type LoopAnimationConfig = { + iterations: number, + resetBeforeIteration?: boolean, +}; const loop = function( animation: CompositeAnimation, - {iterations = -1}: LoopAnimationConfig = {}, + {iterations = -1, resetBeforeIteration = true}: LoopAnimationConfig = {}, ): CompositeAnimation { let isFinished = false; let iterationsSoFar = 0; @@ -424,7 +439,7 @@ const loop = function( callback && callback(result); } else { iterationsSoFar++; - animation.reset(); + resetBeforeIteration && animation.reset(); animation.start(restart); } }; @@ -502,6 +517,8 @@ const event = function(argMapping: Array, config?: EventConfig): any { * easy to build and maintain. `Animated` focuses on declarative relationships * between inputs and outputs, with configurable transforms in between, and * simple `start`/`stop` methods to control time-based animation execution. + * If additional transforms are added, be sure to include them in + * AnimatedMock.js as well. * * See http://facebook.github.io/react-native/docs/animated.html */ @@ -516,7 +533,7 @@ const AnimatedImplementation = { /** * 2D value class for driving 2D animations, such as pan gestures. * - * See https://facebook.github.io/react-native/releases/next/docs/animatedvaluexy.html + * See https://facebook.github.io/react-native/docs/animatedvaluexy.html */ ValueXY: AnimatedValueXY, /** @@ -563,6 +580,14 @@ const AnimatedImplementation = { */ add, + /** + * Creates a new Animated value composed by subtracting the second Animated + * value from the first Animated value. + * + * See http://facebook.github.io/react-native/docs/animated.html#subtract + */ + subtract, + /** * Creates a new Animated value composed by dividing the first Animated value * by the second Animated value. @@ -665,6 +690,11 @@ const AnimatedImplementation = { forkEvent, unforkEvent, + /** + * Expose Event class, so it can be used as a type for type checkers. + */ + Event: AnimatedEvent, + __PropsOnlyForTests: AnimatedProps, }; diff --git a/packages/react-native-web/src/vendor/react-native/Animated/Easing.js b/packages/react-native-web/src/vendor/react-native/Animated/Easing.js index 3c5eb5b03..ed0e9c96a 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/Easing.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/Easing.js @@ -1,11 +1,13 @@ /** - * Copyright (c) 2015-present, Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @format + * @flow strict */ + 'use strict'; import _bezier from './bezier'; @@ -131,7 +133,7 @@ class Easing { * http://easings.net/#easeInSine */ static sin(t: number) { - return 1 - Math.cos(t * Math.PI / 2); + return 1 - Math.cos((t * Math.PI) / 2); } /** @@ -164,7 +166,7 @@ class Easing { */ static elastic(bounciness: number = 1): (t: number) => number { const p = bounciness * Math.PI; - return (t) => 1 - Math.pow(Math.cos(t * Math.PI / 2), 3) * Math.cos(t * p); + return t => 1 - Math.pow(Math.cos((t * Math.PI) / 2), 3) * Math.cos(t * p); } /** @@ -175,11 +177,8 @@ class Easing { * * - http://tiny.cc/back_default (s = 1.70158, default) */ - static back(s: number): (t: number) => number { - if (s === undefined) { - s = 1.70158; - } - return (t) => t * t * ((s + 1) * t - s); + static back(s: number = 1.70158): (t: number) => number { + return t => t * t * ((s + 1) * t - s); } /** @@ -193,17 +192,17 @@ class Easing { } if (t < 2 / 2.75) { - t -= 1.5 / 2.75; - return 7.5625 * t * t + 0.75; + const t2 = t - 1.5 / 2.75; + return 7.5625 * t2 * t2 + 0.75; } if (t < 2.5 / 2.75) { - t -= 2.25 / 2.75; - return 7.5625 * t * t + 0.9375; + const t2 = t - 2.25 / 2.75; + return 7.5625 * t2 * t2 + 0.9375; } - t -= 2.625 / 2.75; - return 7.5625 * t * t + 0.984375; + const t2 = t - 2.625 / 2.75; + return 7.5625 * t2 * t2 + 0.984375; } /** @@ -217,7 +216,7 @@ class Easing { x1: number, y1: number, x2: number, - y2: number + y2: number, ): (t: number) => number { return _bezier(x1, y1, x2, y2); } @@ -225,19 +224,15 @@ class Easing { /** * Runs an easing function forwards. */ - static in( - easing: (t: number) => number, - ): (t: number) => number { + static in(easing: (t: number) => number): (t: number) => number { return easing; } /** * Runs an easing function backwards. */ - static out( - easing: (t: number) => number, - ): (t: number) => number { - return (t) => 1 - easing(1 - t); + static out(easing: (t: number) => number): (t: number) => number { + return t => 1 - easing(1 - t); } /** @@ -245,10 +240,8 @@ class Easing { * forwards for half of the duration, then backwards for the rest of the * duration. */ - static inOut( - easing: (t: number) => number, - ): (t: number) => number { - return (t) => { + static inOut(easing: (t: number) => number): (t: number) => number { + return t => { if (t < 0.5) { return easing(t * 2) / 2; } diff --git a/packages/react-native-web/src/vendor/react-native/Animated/NativeAnimatedHelper.js b/packages/react-native-web/src/vendor/react-native/Animated/NativeAnimatedHelper.js index 8598714d1..cd032c770 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/NativeAnimatedHelper.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/NativeAnimatedHelper.js @@ -1,70 +1,86 @@ /** - * Copyright (c) 2015-present, Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ 'use strict'; -import invariant from 'fbjs/lib/invariant'; -import NativeModules from '../../../exports/NativeModules'; import NativeEventEmitter from '../NativeEventEmitter'; +import type { + EventMapping, + AnimatedNodeConfig, + AnimatingNodeConfig, +} from './NativeAnimatedModule'; +import NativeAnimatedModule from './NativeAnimatedModule'; +import invariant from 'fbjs/lib/invariant'; -import type {AnimationConfig} from './animations/Animation'; +import type {AnimationConfig, EndCallback} from './animations/Animation'; +import type {InterpolationConfigType} from './nodes/AnimatedInterpolation'; import type {EventConfig} from './AnimatedEvent'; -const NativeAnimatedModule = NativeModules.NativeAnimatedModule; - let __nativeAnimatedNodeTagCount = 1; /* used for animated nodes */ let __nativeAnimationIdCount = 1; /* used for started animations */ -type EndResult = {finished: boolean}; -type EndCallback = (result: EndResult) => void; -type EventMapping = { - nativeEventPath: Array, - animatedValueTag: ?number, -}; - let nativeEventEmitter; +let queueConnections = false; +let queue = []; + /** * Simple wrappers around NativeAnimatedModule to provide flow and autocmplete support for * the native module methods */ const API = { - createAnimatedNode: function(tag: ?number, config: Object): void { - assertNativeAnimatedModule(); + enableQueue: function(): void { + queueConnections = true; + }, + disableQueue: function(): void { + invariant(NativeAnimatedModule, 'Native animated module is not available'); + queueConnections = false; + for (let q = 0, l = queue.length; q < l; q++) { + const args = queue[q]; + NativeAnimatedModule.connectAnimatedNodes(args[0], args[1]); + } + queue.length = 0; + }, + createAnimatedNode: function(tag: ?number, config: AnimatedNodeConfig): void { + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.createAnimatedNode(tag, config); }, startListeningToAnimatedNodeValue: function(tag: ?number) { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.startListeningToAnimatedNodeValue(tag); }, stopListeningToAnimatedNodeValue: function(tag: ?number) { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.stopListeningToAnimatedNodeValue(tag); }, connectAnimatedNodes: function(parentTag: ?number, childTag: ?number): void { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); + if (queueConnections) { + queue.push([parentTag, childTag]); + return; + } NativeAnimatedModule.connectAnimatedNodes(parentTag, childTag); }, disconnectAnimatedNodes: function( parentTag: ?number, childTag: ?number, ): void { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.disconnectAnimatedNodes(parentTag, childTag); }, startAnimatingNode: function( animationId: ?number, nodeTag: ?number, - config: Object, + config: AnimatingNodeConfig, endCallback: EndCallback, ): void { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.startAnimatingNode( animationId, nodeTag, @@ -73,41 +89,41 @@ const API = { ); }, stopAnimation: function(animationId: ?number) { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.stopAnimation(animationId); }, setAnimatedNodeValue: function(nodeTag: ?number, value: ?number): void { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.setAnimatedNodeValue(nodeTag, value); }, setAnimatedNodeOffset: function(nodeTag: ?number, offset: ?number): void { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.setAnimatedNodeOffset(nodeTag, offset); }, flattenAnimatedNodeOffset: function(nodeTag: ?number): void { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.flattenAnimatedNodeOffset(nodeTag); }, extractAnimatedNodeOffset: function(nodeTag: ?number): void { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.extractAnimatedNodeOffset(nodeTag); }, connectAnimatedNodeToView: function( nodeTag: ?number, viewTag: ?number, ): void { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.connectAnimatedNodeToView(nodeTag, viewTag); }, disconnectAnimatedNodeFromView: function( nodeTag: ?number, viewTag: ?number, ): void { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.disconnectAnimatedNodeFromView(nodeTag, viewTag); }, dropAnimatedNode: function(tag: ?number): void { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.dropAnimatedNode(tag); }, addAnimatedEventToView: function( @@ -115,7 +131,7 @@ const API = { eventName: string, eventMapping: EventMapping, ) { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.addAnimatedEventToView( viewTag, eventName, @@ -127,7 +143,7 @@ const API = { eventName: string, animatedNodeTag: ?number, ) { - assertNativeAnimatedModule(); + invariant(NativeAnimatedModule, 'Native animated module is not available'); NativeAnimatedModule.removeAnimatedEventFromView( viewTag, eventName, @@ -145,6 +161,16 @@ const API = { const STYLES_WHITELIST = { opacity: true, transform: true, + borderRadius: true, + borderBottomEndRadius: true, + borderBottomLeftRadius: true, + borderBottomRightRadius: true, + borderBottomStartRadius: true, + borderTopEndRadius: true, + borderTopLeftRadius: true, + borderTopRightRadius: true, + borderTopStartRadius: true, + elevation: true, /* ios styles */ shadowOpacity: true, shadowRadius: true, @@ -187,7 +213,12 @@ function addWhitelistedInterpolationParam(param: string): void { SUPPORTED_INTERPOLATION_PARAMS[param] = true; } -function validateTransform(configs: Array): void { +function validateTransform( + configs: Array< + | {type: 'animated', property: string, nodeTag: ?number} + | {type: 'static', property: string, value: number | string}, + >, +): void { configs.forEach(config => { if (!TRANSFORM_WHITELIST.hasOwnProperty(config.property)) { throw new Error( @@ -199,8 +230,8 @@ function validateTransform(configs: Array): void { }); } -function validateStyles(styles: Object): void { - for (var key in styles) { +function validateStyles(styles: {[key: string]: ?number}): void { + for (const key in styles) { if (!STYLES_WHITELIST.hasOwnProperty(key)) { throw new Error( `Style property '${key}' is not supported by native animated module`, @@ -209,8 +240,8 @@ function validateStyles(styles: Object): void { } } -function validateInterpolation(config: Object): void { - for (var key in config) { +function validateInterpolation(config: InterpolationConfigType): void { + for (const key in config) { if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) { throw new Error( `Interpolation property '${key}' is not supported by native animated module`, @@ -234,7 +265,7 @@ function assertNativeAnimatedModule(): void { let _warnedMissingNativeAnimated = false; function shouldUseNativeDriver(config: AnimationConfig | EventConfig): boolean { - if (config.useNativeDriver && !NativeAnimatedModule) { + if (config.useNativeDriver === true && !NativeAnimatedModule) { if (!_warnedMissingNativeAnimated) { console.warn( 'Animated: `useNativeDriver` is not supported because the native ' + @@ -251,6 +282,21 @@ function shouldUseNativeDriver(config: AnimationConfig | EventConfig): boolean { return config.useNativeDriver || false; } +function transformDataType(value: number | string): number | string { + // Change the string type to number type so we can reuse the same logic in + // iOS and Android platform + if (typeof value !== 'string') { + return value; + } + if (/deg$/.test(value)) { + const degrees = parseFloat(value) || 0; + const radians = (degrees * Math.PI) / 180.0; + return radians; + } else { + return value; + } +} + const NativeAnimatedHelper = { API, addWhitelistedStyleProp, @@ -263,6 +309,7 @@ const NativeAnimatedHelper = { generateNewAnimationId, assertNativeAnimatedModule, shouldUseNativeDriver, + transformDataType, get nativeEventEmitter() { if (!nativeEventEmitter) { nativeEventEmitter = new NativeEventEmitter(NativeAnimatedModule); @@ -282,7 +329,8 @@ export { generateNewNodeTag, generateNewAnimationId, assertNativeAnimatedModule, - shouldUseNativeDriver + shouldUseNativeDriver, + transformDataType }; export default NativeAnimatedHelper; diff --git a/packages/react-native-web/src/vendor/react-native/Animated/NativeAnimatedModule.js b/packages/react-native-web/src/vendor/react-native/Animated/NativeAnimatedModule.js new file mode 100644 index 000000000..fdaa7f65f --- /dev/null +++ b/packages/react-native-web/src/vendor/react-native/Animated/NativeAnimatedModule.js @@ -0,0 +1,70 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type {TurboModule} from '../TurboModule/RCTExport'; +import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; + +type EndResult = {finished: boolean}; +type EndCallback = (result: EndResult) => void; + +export type EventMapping = {| + nativeEventPath: Array, + animatedValueTag: ?number, +|}; + +export type AnimatedNodeConfig = {| + // TODO: Type this with better enums. + type: string, +|}; + +export type AnimatingNodeConfig = {| + // TODO: Type this with better enums. + type: string, +|}; + +export interface Spec extends TurboModule { + +createAnimatedNode: (tag: ?number, config: AnimatedNodeConfig) => void; + +startListeningToAnimatedNodeValue: (tag: ?number) => void; + +stopListeningToAnimatedNodeValue: (tag: ?number) => void; + +connectAnimatedNodes: (parentTag: ?number, childTag: ?number) => void; + +disconnectAnimatedNodes: (parentTag: ?number, childTag: ?number) => void; + +startAnimatingNode: ( + animationId: ?number, + nodeTag: ?number, + config: AnimatingNodeConfig, + endCallback: EndCallback, + ) => void; + +stopAnimation: (animationId: ?number) => void; + +setAnimatedNodeValue: (nodeTag: ?number, value: ?number) => void; + +setAnimatedNodeOffset: (nodeTag: ?number, offset: ?number) => void; + +flattenAnimatedNodeOffset: (nodeTag: ?number) => void; + +extractAnimatedNodeOffset: (nodeTag: ?number) => void; + +connectAnimatedNodeToView: (nodeTag: ?number, viewTag: ?number) => void; + +disconnectAnimatedNodeFromView: (nodeTag: ?number, viewTag: ?number) => void; + +dropAnimatedNode: (tag: ?number) => void; + +addAnimatedEventToView: ( + viewTag: ?number, + eventName: string, + eventMapping: EventMapping, + ) => void; + +removeAnimatedEventFromView: ( + viewTag: ?number, + eventName: string, + animatedNodeTag: ?number, + ) => void; + + // Events + +addListener: (eventName: string) => void; + +removeListeners: (count: number) => void; +} + +export default TurboModuleRegistry.get('NativeAnimatedModule'); diff --git a/packages/react-native-web/src/vendor/react-native/Animated/SpringConfig.js b/packages/react-native-web/src/vendor/react-native/Animated/SpringConfig.js index 10df50cbd..0524c9bb5 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/SpringConfig.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/SpringConfig.js @@ -1,10 +1,11 @@ /** - * Copyright (c) 2015-present, Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @format + * @flow strict */ 'use strict'; @@ -41,7 +42,7 @@ function fromBouncinessAndSpeed( } function projectNormal(n, start, end) { - return start + (n * (end - start)); + return start + n * (end - start); } function linearInterpolation(t, start, end) { @@ -53,18 +54,20 @@ function fromBouncinessAndSpeed( } function b3Friction1(x) { - return (0.0007 * Math.pow(x, 3)) - - (0.031 * Math.pow(x, 2)) + 0.64 * x + 1.28; + return 0.0007 * Math.pow(x, 3) - 0.031 * Math.pow(x, 2) + 0.64 * x + 1.28; } function b3Friction2(x) { - return (0.000044 * Math.pow(x, 3)) - - (0.006 * Math.pow(x, 2)) + 0.36 * x + 2; + return 0.000044 * Math.pow(x, 3) - 0.006 * Math.pow(x, 2) + 0.36 * x + 2; } function b3Friction3(x) { - return (0.00000045 * Math.pow(x, 3)) - - (0.000332 * Math.pow(x, 2)) + 0.1078 * x + 5.84; + return ( + 0.00000045 * Math.pow(x, 3) - + 0.000332 * Math.pow(x, 2) + + 0.1078 * x + + 5.84 + ); } function b3Nobounce(tension) { @@ -77,14 +80,14 @@ function fromBouncinessAndSpeed( } } - var b = normalize(bounciness / 1.7, 0, 20); + let b = normalize(bounciness / 1.7, 0, 20); b = projectNormal(b, 0, 0.8); - var s = normalize(speed / 1.7, 0, 20); - var bouncyTension = projectNormal(s, 0.5, 200); - var bouncyFriction = quadraticOutInterpolation( + const s = normalize(speed / 1.7, 0, 20); + const bouncyTension = projectNormal(s, 0.5, 200); + const bouncyFriction = quadraticOutInterpolation( b, b3Nobounce(bouncyTension), - 0.01 + 0.01, ); return { diff --git a/packages/react-native-web/src/vendor/react-native/Animated/animations/Animation.js b/packages/react-native-web/src/vendor/react-native/Animated/animations/Animation.js index c15d87575..5911eb87a 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/animations/Animation.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/animations/Animation.js @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015-present, Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. @@ -56,7 +56,9 @@ class Animation { onEnd && onEnd(result); } __startNativeAnimation(animatedValue: AnimatedValue): void { + NativeAnimatedHelper.API.enableQueue(); animatedValue.__makeNative(); + NativeAnimatedHelper.API.disableQueue(); this.__nativeId = NativeAnimatedHelper.generateNewAnimationId(); NativeAnimatedHelper.API.startAnimatingNode( this.__nativeId, diff --git a/packages/react-native-web/src/vendor/react-native/Animated/animations/DecayAnimation.js b/packages/react-native-web/src/vendor/react-native/Animated/animations/DecayAnimation.js index e6e631286..05e5734a4 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/animations/DecayAnimation.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/animations/DecayAnimation.js @@ -38,13 +38,11 @@ class DecayAnimation extends Animation { constructor(config: DecayAnimationConfigSingle) { super(); - this._deceleration = - config.deceleration !== undefined ? config.deceleration : 0.998; + this._deceleration = config.deceleration ?? 0.998; this._velocity = config.velocity; this._useNativeDriver = shouldUseNativeDriver(config); - this.__isInteraction = - config.isInteraction !== undefined ? config.isInteraction : true; - this.__iterations = config.iterations !== undefined ? config.iterations : 1; + this.__isInteraction = config.isInteraction ?? !this._useNativeDriver; + this.__iterations = config.iterations ?? 1; } __getNativeAnimationConfig() { @@ -81,8 +79,7 @@ class DecayAnimation extends Animation { const value = this._fromValue + - this._velocity / - (1 - this._deceleration) * + (this._velocity / (1 - this._deceleration)) * (1 - Math.exp(-(1 - this._deceleration) * (now - this._startTime))); this._onUpdate(value); diff --git a/packages/react-native-web/src/vendor/react-native/Animated/animations/SpringAnimation.js b/packages/react-native-web/src/vendor/react-native/Animated/animations/SpringAnimation.js index 259a92020..6371f0a67 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/animations/SpringAnimation.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/animations/SpringAnimation.js @@ -51,13 +51,6 @@ export type SpringAnimationConfigSingle = AnimationConfig & { delay?: number, }; -function withDefault(value: ?T, defaultValue: T): T { - if (value === undefined || value === null) { - return defaultValue; - } - return value; -} - class SpringAnimation extends Animation { _overshootClamping: boolean; _restDisplacementThreshold: number; @@ -83,20 +76,16 @@ class SpringAnimation extends Animation { constructor(config: SpringAnimationConfigSingle) { super(); - this._overshootClamping = withDefault(config.overshootClamping, false); - this._restDisplacementThreshold = withDefault( - config.restDisplacementThreshold, - 0.001, - ); - this._restSpeedThreshold = withDefault(config.restSpeedThreshold, 0.001); - this._initialVelocity = withDefault(config.velocity, 0); - this._lastVelocity = withDefault(config.velocity, 0); + this._overshootClamping = config.overshootClamping ?? false; + this._restDisplacementThreshold = config.restDisplacementThreshold ?? 0.001; + this._restSpeedThreshold = config.restSpeedThreshold ?? 0.001; + this._initialVelocity = config.velocity ?? 0; + this._lastVelocity = config.velocity ?? 0; this._toValue = config.toValue; - this._delay = withDefault(config.delay, 0); + this._delay = config.delay ?? 0; this._useNativeDriver = shouldUseNativeDriver(config); - this.__isInteraction = - config.isInteraction !== undefined ? config.isInteraction : true; - this.__iterations = config.iterations !== undefined ? config.iterations : 1; + this.__isInteraction = config.isInteraction ?? !this._useNativeDriver; + this.__iterations = config.iterations ?? 1; if ( config.stiffness !== undefined || @@ -110,9 +99,9 @@ class SpringAnimation extends Animation { config.friction === undefined, 'You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one', ); - this._stiffness = withDefault(config.stiffness, 100); - this._damping = withDefault(config.damping, 10); - this._mass = withDefault(config.mass, 1); + this._stiffness = config.stiffness ?? 100; + this._damping = config.damping ?? 10; + this._mass = config.mass ?? 1; } else if (config.bounciness !== undefined || config.speed !== undefined) { // Convert the origami bounciness/speed values to stiffness/damping // We assume mass is 1. @@ -125,8 +114,8 @@ class SpringAnimation extends Animation { 'You can define one of bounciness/speed, tension/friction, or stiffness/damping/mass, but not more than one', ); const springConfig = SpringConfig.fromBouncinessAndSpeed( - withDefault(config.bounciness, 8), - withDefault(config.speed, 12), + config.bounciness ?? 8, + config.speed ?? 12, ); this._stiffness = springConfig.stiffness; this._damping = springConfig.damping; @@ -135,8 +124,8 @@ class SpringAnimation extends Animation { // Convert the origami tension/friction values to stiffness/damping // We assume mass is 1. const springConfig = SpringConfig.fromOrigamiTensionAndFriction( - withDefault(config.tension, 40), - withDefault(config.friction, 7), + config.tension ?? 40, + config.friction ?? 7, ); this._stiffness = springConfig.stiffness; this._damping = springConfig.damping; @@ -157,7 +146,7 @@ class SpringAnimation extends Animation { stiffness: this._stiffness, damping: this._damping, mass: this._mass, - initialVelocity: withDefault(this._initialVelocity, this._lastVelocity), + initialVelocity: this._initialVelocity ?? this._lastVelocity, toValue: this._toValue, iterations: this.__iterations, }; @@ -266,7 +255,7 @@ class SpringAnimation extends Animation { position = this._toValue - envelope * - ((v0 + zeta * omega0 * x0) / omega1 * Math.sin(omega1 * t) + + (((v0 + zeta * omega0 * x0) / omega1) * Math.sin(omega1 * t) + x0 * Math.cos(omega1 * t)); // This looks crazy -- it's actually just the derivative of the // oscillation function @@ -274,7 +263,7 @@ class SpringAnimation extends Animation { zeta * omega0 * envelope * - (Math.sin(omega1 * t) * (v0 + zeta * omega0 * x0) / omega1 + + ((Math.sin(omega1 * t) * (v0 + zeta * omega0 * x0)) / omega1 + x0 * Math.cos(omega1 * t)) - envelope * (Math.cos(omega1 * t) * (v0 + zeta * omega0 * x0) - diff --git a/packages/react-native-web/src/vendor/react-native/Animated/animations/TimingAnimation.js b/packages/react-native-web/src/vendor/react-native/Animated/animations/TimingAnimation.js index 464bb225c..a2f95c1f2 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/animations/TimingAnimation.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/animations/TimingAnimation.js @@ -55,13 +55,12 @@ class TimingAnimation extends Animation { constructor(config: TimingAnimationConfigSingle) { super(); this._toValue = config.toValue; - this._easing = config.easing !== undefined ? config.easing : easeInOut(); - this._duration = config.duration !== undefined ? config.duration : 500; - this._delay = config.delay !== undefined ? config.delay : 0; - this.__iterations = config.iterations !== undefined ? config.iterations : 1; - this.__isInteraction = - config.isInteraction !== undefined ? config.isInteraction : true; + this._easing = config.easing ?? easeInOut(); + this._duration = config.duration ?? 500; + this._delay = config.delay ?? 0; + this.__iterations = config.iterations ?? 1; this._useNativeDriver = shouldUseNativeDriver(config); + this.__isInteraction = config.isInteraction ?? !this._useNativeDriver; } __getNativeAnimationConfig(): any { diff --git a/packages/react-native-web/src/vendor/react-native/Animated/bezier.js b/packages/react-native-web/src/vendor/react-native/Animated/bezier.js index 9aa777a12..b9fc08eec 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/bezier.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/bezier.js @@ -1,107 +1,152 @@ /** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * * BezierEasing - use bezier curve for transition easing function * https://github.com/gre/bezier-easing * + * @flow strict + * @format * @copyright 2014-2015 Gaƫtan Renaudeau. MIT License. - * @noflow */ + 'use strict'; - // These values are established by empiricism with tests (tradeoff: performance VS precision) - var NEWTON_ITERATIONS = 4; - var NEWTON_MIN_SLOPE = 0.001; - var SUBDIVISION_PRECISION = 0.0000001; - var SUBDIVISION_MAX_ITERATIONS = 10; - - var kSplineTableSize = 11; - var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); - - var float32ArraySupported = typeof Float32Array === 'function'; - - function A (aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } - function B (aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } - function C (aA1) { return 3.0 * aA1; } - - // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. - function calcBezier (aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } - - // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. - function getSlope (aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } - - function binarySubdivide (aX, aA, aB, mX1, mX2) { - var currentX, currentT, i = 0; - do { - currentT = aA + (aB - aA) / 2.0; - currentX = calcBezier(currentT, mX1, mX2) - aX; - if (currentX > 0.0) { - aB = currentT; - } else { - aA = currentT; - } - } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); - return currentT; - } - - function newtonRaphsonIterate (aX, aGuessT, mX1, mX2) { - for (var i = 0; i < NEWTON_ITERATIONS; ++i) { - var currentSlope = getSlope(aGuessT, mX1, mX2); +// These values are established by empiricism with tests (tradeoff: performance VS precision) +const NEWTON_ITERATIONS = 4; +const NEWTON_MIN_SLOPE = 0.001; +const SUBDIVISION_PRECISION = 0.0000001; +const SUBDIVISION_MAX_ITERATIONS = 10; + +const kSplineTableSize = 11; +const kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); + +const float32ArraySupported = typeof Float32Array === 'function'; + +function A(aA1, aA2) { + return 1.0 - 3.0 * aA2 + 3.0 * aA1; +} +function B(aA1, aA2) { + return 3.0 * aA2 - 6.0 * aA1; +} +function C(aA1) { + return 3.0 * aA1; +} + +// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. +function calcBezier(aT, aA1, aA2) { + return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; +} + +// Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. +function getSlope(aT, aA1, aA2) { + return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); +} + +function binarySubdivide(aX, _aA, _aB, mX1, mX2) { + let currentX, + currentT, + i = 0, + aA = _aA, + aB = _aB; + do { + currentT = aA + (aB - aA) / 2.0; + currentX = calcBezier(currentT, mX1, mX2) - aX; + if (currentX > 0.0) { + aB = currentT; + } else { + aA = currentT; + } + } while ( + Math.abs(currentX) > SUBDIVISION_PRECISION && + ++i < SUBDIVISION_MAX_ITERATIONS + ); + return currentT; +} + +function newtonRaphsonIterate(aX, _aGuessT, mX1, mX2) { + let aGuessT = _aGuessT; + for (let i = 0; i < NEWTON_ITERATIONS; ++i) { + const currentSlope = getSlope(aGuessT, mX1, mX2); if (currentSlope === 0.0) { return aGuessT; } - var currentX = calcBezier(aGuessT, mX1, mX2) - aX; + const currentX = calcBezier(aGuessT, mX1, mX2) - aX; aGuessT -= currentX / currentSlope; } return aGuessT; - } - - module.exports = function bezier (mX1, mY1, mX2, mY2) { - if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { // eslint-disable-line yoda - throw new Error('bezier x values must be in [0, 1] range'); - } - - // Precompute samples table - var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); - if (mX1 !== mY1 || mX2 !== mY2) { - for (var i = 0; i < kSplineTableSize; ++i) { - sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); - } - } - - function getTForX (aX) { - var intervalStart = 0.0; - var currentSample = 1; - var lastSample = kSplineTableSize - 1; - - for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) { - intervalStart += kSampleStepSize; - } - --currentSample; - - // Interpolate to provide an initial guess for t - var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]); - var guessForT = intervalStart + dist * kSampleStepSize; - - var initialSlope = getSlope(guessForT, mX1, mX2); - if (initialSlope >= NEWTON_MIN_SLOPE) { - return newtonRaphsonIterate(aX, guessForT, mX1, mX2); - } else if (initialSlope === 0.0) { - return guessForT; - } else { - return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2); - } - } - - return function BezierEasing (x) { - if (mX1 === mY1 && mX2 === mY2) { - return x; // linear - } - // Because JavaScript number are imprecise, we should guarantee the extremes are right. - if (x === 0) { - return 0; - } - if (x === 1) { - return 1; - } - return calcBezier(getTForX(x), mY1, mY2); - }; - }; +} + +module.exports = function bezier( + mX1: number, + mY1: number, + mX2: number, + mY2: number, +) { + if (!(mX1 >= 0 && mX1 <= 1 && mX2 >= 0 && mX2 <= 1)) { + throw new Error('bezier x values must be in [0, 1] range'); + } + + // Precompute samples table + const sampleValues = float32ArraySupported + ? new Float32Array(kSplineTableSize) + : new Array(kSplineTableSize); + if (mX1 !== mY1 || mX2 !== mY2) { + for (let i = 0; i < kSplineTableSize; ++i) { + sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); + } + } + + function getTForX(aX) { + let intervalStart = 0.0; + let currentSample = 1; + const lastSample = kSplineTableSize - 1; + + for ( + ; + currentSample !== lastSample && sampleValues[currentSample] <= aX; + ++currentSample + ) { + intervalStart += kSampleStepSize; + } + --currentSample; + + // Interpolate to provide an initial guess for t + const dist = + (aX - sampleValues[currentSample]) / + (sampleValues[currentSample + 1] - sampleValues[currentSample]); + const guessForT = intervalStart + dist * kSampleStepSize; + + const initialSlope = getSlope(guessForT, mX1, mX2); + if (initialSlope >= NEWTON_MIN_SLOPE) { + return newtonRaphsonIterate(aX, guessForT, mX1, mX2); + } else if (initialSlope === 0.0) { + return guessForT; + } else { + return binarySubdivide( + aX, + intervalStart, + intervalStart + kSampleStepSize, + mX1, + mX2, + ); + } + } + + return function BezierEasing(x: number): number { + if (mX1 === mY1 && mX2 === mY2) { + return x; // linear + } + // Because JavaScript number are imprecise, we should guarantee the extremes are right. + if (x === 0) { + return 0; + } + if (x === 1) { + return 1; + } + return calcBezier(getTForX(x), mY1, mY2); + }; +}; + diff --git a/packages/react-native-web/src/vendor/react-native/Animated/createAnimatedComponent.js b/packages/react-native-web/src/vendor/react-native/Animated/createAnimatedComponent.js index 9ba7aa016..4f9d31e72 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/createAnimatedComponent.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/createAnimatedComponent.js @@ -15,9 +15,9 @@ import React from 'react'; import ViewStylePropTypes from '../../../exports/View/ViewStylePropTypes'; import invariant from 'fbjs/lib/invariant'; -function createAnimatedComponent(Component: any): any { +function createAnimatedComponent(Component: any, defaultProps: any): any { invariant( - typeof Component === 'string' || + typeof Component !== 'function' || (Component.prototype && Component.prototype.isReactComponent), '`createAnimatedComponent` does not support stateless functional components; ' + 'use a class component instead.', @@ -29,13 +29,11 @@ function createAnimatedComponent(Component: any): any { _prevComponent: any; _propsAnimated: AnimatedProps; _eventDetachers: Array = []; - _setComponentRef: Function; static __skipSetNativeProps_FOR_TESTS_ONLY = false; constructor(props: Object) { super(props); - this._setComponentRef = this._setComponentRef.bind(this); } componentWillUnmount() { @@ -150,23 +148,22 @@ function createAnimatedComponent(Component: any): any { const props = this._propsAnimated.__getValue(); return ( ); } - _setComponentRef(c) { + _setComponentRef = c => { this._prevComponent = this._component; this._component = c; - } + }; // A third party library can use getNode() // to get the node reference of the decorated component diff --git a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedInterpolation.js b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedInterpolation.js index 2aca36bad..1b0f58d8f 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedInterpolation.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedInterpolation.js @@ -181,7 +181,7 @@ function colorToRgba(input: string): string { return `rgba(${r}, ${g}, ${b}, ${a})`; } -const stringShapeRegex = /[0-9\.-]+/g; +const stringShapeRegex = /[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/g; /** * Supports string shapes by extracting numbers so new values can be computed, @@ -242,10 +242,11 @@ function createInterpolationFromStringOutputRange( // -> // 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...' return outputRange[0].replace(stringShapeRegex, () => { - const val = +interpolations[i++](input); - const rounded = - shouldRound && i < 4 ? Math.round(val) : Math.round(val * 1000) / 1000; - return String(rounded); + let val = +interpolations[i++](input); + if (shouldRound) { + val = i < 4 ? Math.round(val) : Math.round(val * 1000) / 1000; + } + return String(val); }); }; } @@ -285,7 +286,7 @@ function checkValidInputRange(arr: Array) { * mean this implicit string conversion, you can do something like * String(myThing) */ - 'inputRange must be monotonically increasing ' + arr, + 'inputRange must be monotonically non-decreasing ' + arr, ); } } @@ -346,22 +347,8 @@ class AnimatedInterpolation extends AnimatedWithChildren { super.__detach(); } - __transformDataType(range: Array): Array { - // Change the string array type to number array - // So we can reuse the same logic in iOS and Android platform - return range.map(function(value) { - if (typeof value !== 'string') { - return value; - } - if (/deg$/.test(value)) { - const degrees = parseFloat(value) || 0; - const radians = degrees * Math.PI / 180.0; - return radians; - } else { - // Assume radians - return parseFloat(value) || 0; - } - }); + __transformDataType(range: Array) { + return range.map(NativeAnimatedHelper.transformDataType); } __getNativeConfig(): any { diff --git a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedNode.js b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedNode.js index 2accb0167..8b903ad57 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedNode.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedNode.js @@ -13,11 +13,17 @@ import NativeAnimatedHelper from '../NativeAnimatedHelper'; import invariant from 'fbjs/lib/invariant'; +const NativeAnimatedAPI = NativeAnimatedHelper.API; + +type ValueListenerCallback = (state: {value: number}) => mixed; + +let _uniqueId = 1; + // Note(vjeux): this would be better as an interface but flow doesn't // support them yet class AnimatedNode { - +update: () => void - + _listeners: {[key: string]: ValueListenerCallback}; + __nativeAnimatedValueListener: ?any; __attach(): void {} __detach(): void { if (this.__isNative && this.__nativeTag != null) { @@ -38,11 +44,112 @@ class AnimatedNode { /* Methods and props used by native Animated impl */ __isNative: boolean; __nativeTag: ?number; + __shouldUpdateListenersForNewNativeTag: boolean; + + constructor() { + this._listeners = {}; + } + __makeNative() { if (!this.__isNative) { throw new Error('This node cannot be made a "native" animated node'); } + + if (this.hasListeners()) { + this._startListeningToNativeValueUpdates(); + } + } + + /** + * Adds an asynchronous listener to the value so you can observe updates from + * animations. This is useful because there is no way to + * synchronously read the value because it might be driven natively. + * + * See http://facebook.github.io/react-native/docs/animatedvalue.html#addlistener + */ + addListener(callback: (value: any) => mixed): string { + const id = String(_uniqueId++); + this._listeners[id] = callback; + if (this.__isNative) { + this._startListeningToNativeValueUpdates(); + } + return id; + } + + /** + * Unregister a listener. The `id` param shall match the identifier + * previously returned by `addListener()`. + * + * See http://facebook.github.io/react-native/docs/animatedvalue.html#removelistener + */ + removeListener(id: string): void { + delete this._listeners[id]; + if (this.__isNative && !this.hasListeners()) { + this._stopListeningForNativeValueUpdates(); + } + } + + /** + * Remove all registered listeners. + * + * See http://facebook.github.io/react-native/docs/animatedvalue.html#removealllisteners + */ + removeAllListeners(): void { + this._listeners = {}; + if (this.__isNative) { + this._stopListeningForNativeValueUpdates(); + } + } + + hasListeners(): boolean { + return !!Object.keys(this._listeners).length; + } + + _startListeningToNativeValueUpdates() { + if ( + this.__nativeAnimatedValueListener && + !this.__shouldUpdateListenersForNewNativeTag + ) { + return; + } + + if (this.__shouldUpdateListenersForNewNativeTag) { + this.__shouldUpdateListenersForNewNativeTag = false; + this._stopListeningForNativeValueUpdates(); + } + + NativeAnimatedAPI.startListeningToAnimatedNodeValue(this.__getNativeTag()); + this.__nativeAnimatedValueListener = NativeAnimatedHelper.nativeEventEmitter.addListener( + 'onAnimatedValueUpdate', + data => { + if (data.tag !== this.__getNativeTag()) { + return; + } + this._onAnimatedValueUpdateReceived(data.value); + }, + ); } + + _onAnimatedValueUpdateReceived(value: number) { + this.__callListeners(value); + } + + __callListeners(value: number): void { + for (const key in this._listeners) { + this._listeners[key]({value}); + } + } + + _stopListeningForNativeValueUpdates() { + if (!this.__nativeAnimatedValueListener) { + return; + } + + this.__nativeAnimatedValueListener.remove(); + this.__nativeAnimatedValueListener = null; + NativeAnimatedAPI.stopListeningToAnimatedNodeValue(this.__getNativeTag()); + } + __getNativeTag(): ?number { NativeAnimatedHelper.assertNativeAnimatedModule(); invariant( @@ -51,11 +158,12 @@ class AnimatedNode { ); if (this.__nativeTag == null) { const nativeTag: ?number = NativeAnimatedHelper.generateNewNodeTag(); + this.__nativeTag = nativeTag; NativeAnimatedHelper.API.createAnimatedNode( nativeTag, this.__getNativeConfig(), ); - this.__nativeTag = nativeTag; + this.__shouldUpdateListenersForNewNativeTag = true; } return this.__nativeTag; } diff --git a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedProps.js b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedProps.js index fd5ec69f7..af184604e 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedProps.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedProps.js @@ -151,6 +151,7 @@ class AnimatedProps extends AnimatedNode { for (const propKey in this._props) { const value = this._props[propKey]; if (value instanceof AnimatedNode) { + value.__makeNative(); propsConfig[propKey] = value.__getNativeTag(); } } diff --git a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedStyle.js b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedStyle.js index 0dbd893c9..b07a485f7 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedStyle.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedStyle.js @@ -96,20 +96,22 @@ class AnimatedStyle extends AnimatedWithChildren { } __makeNative() { - super.__makeNative(); for (const key in this._style) { const value = this._style[key]; if (value instanceof AnimatedNode) { value.__makeNative(); } } + super.__makeNative(); } __getNativeConfig(): Object { const styleConfig = {}; for (const styleKey in this._style) { if (this._style[styleKey] instanceof AnimatedNode) { - styleConfig[styleKey] = this._style[styleKey].__getNativeTag(); + const style = this._style[styleKey]; + style.__makeNative(); + styleConfig[styleKey] = style.__getNativeTag(); } // Non-animated styles are set using `setNativeProps`, no need // to pass those as a part of the node config diff --git a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedSubtraction.js b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedSubtraction.js new file mode 100644 index 000000000..f171a0fab --- /dev/null +++ b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedSubtraction.js @@ -0,0 +1,62 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ +'use strict'; + +import AnimatedInterpolation from './AnimatedInterpolation'; +import AnimatedNode from './AnimatedNode'; +import AnimatedValue from './AnimatedValue'; +import AnimatedWithChildren from './AnimatedWithChildren'; + +import type {InterpolationConfigType} from './AnimatedInterpolation'; + +class AnimatedSubtraction extends AnimatedWithChildren { + _a: AnimatedNode; + _b: AnimatedNode; + + constructor(a: AnimatedNode | number, b: AnimatedNode | number) { + super(); + this._a = typeof a === 'number' ? new AnimatedValue(a) : a; + this._b = typeof b === 'number' ? new AnimatedValue(b) : b; + } + + __makeNative() { + this._a.__makeNative(); + this._b.__makeNative(); + super.__makeNative(); + } + + __getValue(): number { + return this._a.__getValue() - this._b.__getValue(); + } + + interpolate(config: InterpolationConfigType): AnimatedInterpolation { + return new AnimatedInterpolation(this, config); + } + + __attach(): void { + this._a.__addChild(this); + this._b.__addChild(this); + } + + __detach(): void { + this._a.__removeChild(this); + this._b.__removeChild(this); + super.__detach(); + } + + __getNativeConfig(): any { + return { + type: 'subtraction', + input: [this._a.__getNativeTag(), this._b.__getNativeTag()], + }; + } +} + +export default AnimatedSubtraction; diff --git a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedValue.js b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedValue.js index 3570fd76a..bac5fc42b 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedValue.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedValue.js @@ -10,7 +10,6 @@ 'use strict'; import AnimatedInterpolation from './AnimatedInterpolation'; -import AnimatedNode from './AnimatedNode'; import AnimatedWithChildren from './AnimatedWithChildren'; import InteractionManager from '../../../../exports/InteractionManager'; import NativeAnimatedHelper from '../NativeAnimatedHelper'; @@ -21,10 +20,6 @@ import type AnimatedTracking from './AnimatedTracking'; const NativeAnimatedAPI = NativeAnimatedHelper.API; -type ValueListenerCallback = (state: {value: number}) => void; - -let _uniqueId = 1; - /** * Animated works by building a directed acyclic graph of dependencies * transparently when you render your Animated components. @@ -50,6 +45,9 @@ let _uniqueId = 1; function _flush(rootNode: AnimatedValue): void { const animatedStyles = new Set(); function findAnimatedStyles(node) { + /* $FlowFixMe(>=0.68.0 site=react_native_fb) This comment suppresses an + * error found when Flow v0.68 was deployed. To see the error delete this + * comment and run Flow. */ if (typeof node.update === 'function') { animatedStyles.add(node); } else { @@ -57,6 +55,7 @@ function _flush(rootNode: AnimatedValue): void { } } findAnimatedStyles(rootNode); + /* $FlowFixMe */ animatedStyles.forEach(animatedStyle => animatedStyle.update()); } @@ -74,15 +73,12 @@ class AnimatedValue extends AnimatedWithChildren { _offset: number; _animation: ?Animation; _tracking: ?AnimatedTracking; - _listeners: {[key: string]: ValueListenerCallback}; - __nativeAnimatedValueListener: ?any; constructor(value: number) { super(); this._startingValue = this._value = value; this._offset = 0; this._animation = null; - this._listeners = {}; } __detach() { @@ -94,14 +90,6 @@ class AnimatedValue extends AnimatedWithChildren { return this._value + this._offset; } - __makeNative() { - super.__makeNative(); - - if (Object.keys(this._listeners).length) { - this._startListeningToNativeValueUpdates(); - } - } - /** * Directly set the value. This will stop any animations running on the value * and update all the bound properties. @@ -164,74 +152,6 @@ class AnimatedValue extends AnimatedWithChildren { } } - /** - * Adds an asynchronous listener to the value so you can observe updates from - * animations. This is useful because there is no way to - * synchronously read the value because it might be driven natively. - * - * See http://facebook.github.io/react-native/docs/animatedvalue.html#addlistener - */ - addListener(callback: ValueListenerCallback): string { - const id = String(_uniqueId++); - this._listeners[id] = callback; - if (this.__isNative) { - this._startListeningToNativeValueUpdates(); - } - return id; - } - - /** - * Unregister a listener. The `id` param shall match the identifier - * previously returned by `addListener()`. - * - * See http://facebook.github.io/react-native/docs/animatedvalue.html#removelistener - */ - removeListener(id: string): void { - delete this._listeners[id]; - if (this.__isNative && Object.keys(this._listeners).length === 0) { - this._stopListeningForNativeValueUpdates(); - } - } - - /** - * Remove all registered listeners. - * - * See http://facebook.github.io/react-native/docs/animatedvalue.html#removealllisteners - */ - removeAllListeners(): void { - this._listeners = {}; - if (this.__isNative) { - this._stopListeningForNativeValueUpdates(); - } - } - - _startListeningToNativeValueUpdates() { - if (this.__nativeAnimatedValueListener) { - return; - } - - NativeAnimatedAPI.startListeningToAnimatedNodeValue(this.__getNativeTag()); - this.__nativeAnimatedValueListener = NativeAnimatedHelper.nativeEventEmitter.addListener( - 'onAnimatedValueUpdate', - data => { - if (data.tag !== this.__getNativeTag()) { - return; - } - this._updateValue(data.value, false /* flush */); - }, - ); - } - - _stopListeningForNativeValueUpdates() { - if (!this.__nativeAnimatedValueListener) { - return; - } - - this.__nativeAnimatedValueListener.remove(); - this.__nativeAnimatedValueListener = null; - NativeAnimatedAPI.stopListeningToAnimatedNodeValue(this.__getNativeTag()); - } - /** * Stops any running animation or tracking. `callback` is invoked with the * final value after stopping the animation, which is useful for updating @@ -256,6 +176,10 @@ class AnimatedValue extends AnimatedWithChildren { this._value = this._startingValue; } + _onAnimatedValueUpdateReceived(value: number): void { + this._updateValue(value, false /*flush*/); + } + /** * Interpolates the value before updating the property, e.g. mapping 0-1 to * 0-10. @@ -318,9 +242,7 @@ class AnimatedValue extends AnimatedWithChildren { if (flush) { _flush(this); } - for (const key in this._listeners) { - this._listeners[key]({value: this.__getValue()}); - } + super.__callListeners(this.__getValue()); } __getNativeConfig(): Object { diff --git a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedWithChildren.js b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedWithChildren.js index df1847758..19dc3c6a0 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedWithChildren.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/nodes/AnimatedWithChildren.js @@ -1,10 +1,10 @@ /** - * Copyright (c) 2015-present, Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @flow + * @flow strict-local * @format */ 'use strict'; @@ -31,6 +31,7 @@ class AnimatedWithChildren extends AnimatedNode { ); } } + super.__makeNative(); } __addChild(child: AnimatedNode): void { @@ -69,6 +70,17 @@ class AnimatedWithChildren extends AnimatedNode { __getChildren(): Array { return this._children; } + + __callListeners(value: number): void { + super.__callListeners(value); + if (!this.__isNative) { + for (const child of this._children) { + if (child.__getValue) { + child.__callListeners(child.__getValue()); + } + } + } + } } export default AnimatedWithChildren; diff --git a/packages/react-native-web/src/vendor/react-native/TurboModule/RCTExport.js b/packages/react-native-web/src/vendor/react-native/TurboModule/RCTExport.js new file mode 100644 index 000000000..bf608d6fe --- /dev/null +++ b/packages/react-native-web/src/vendor/react-native/TurboModule/RCTExport.js @@ -0,0 +1,35 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +'use strict'; + +/** + * NOTE: This is React Native specific export type. + * + * RCTExport is an interface type that allows native code generation for React + * Native native modules. It exists as a hint to the codegen tool that any + * interface that extends it needs to be codegen'ed. Example usage: + * + * export interface RCTFoobar extends RCTExport {} + * + * Native definition for RCTFoobar will then be generated. + * + * The type param T is a placeholder for future codegen hinting, like versioning + * information, native base classes, etc. For now, simply use `void` type as + * there's nothing to give hint about. + */ + +// eslint-disable-next-line no-unused-vars +export interface RCTExport { + +getConstants?: () => {}; +} + +// eslint-disable-next-line lint/react-native-modules +export interface TurboModule extends RCTExport {} diff --git a/packages/react-native-web/src/vendor/react-native/TurboModule/TurboModuleRegistry.js b/packages/react-native-web/src/vendor/react-native/TurboModule/TurboModuleRegistry.js new file mode 100644 index 000000000..80292778e --- /dev/null +++ b/packages/react-native-web/src/vendor/react-native/TurboModule/TurboModuleRegistry.js @@ -0,0 +1,28 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + */ + +'use strict'; + +import type {TurboModule} from './RCTExport'; +import invariant from 'invariant'; + +export function get(name: string): ?T { + return null; +} + +export function getEnforcing(name: string): T { + const module = get(name); + invariant( + module != null, + `TurboModuleRegistry.getEnforcing(...): '${name}' could not be found. ` + + 'Verify that a module by this name is registered in the native binary.', + ); + return module; +}