2016-03-24 16:18:39 +03:00
/ * *
2021-12-31 02:08:43 +03:00
* Copyright ( c ) Meta Platforms , Inc . and affiliates .
2016-03-24 16:18:39 +03:00
*
2018-02-17 05:24:55 +03:00
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree .
2016-03-24 16:18:39 +03:00
*
2019-05-23 03:09:00 +03:00
* @ flow strict - local
2017-08-23 03:57:38 +03:00
* @ format
2016-03-24 16:18:39 +03:00
* /
2019-10-16 20:03:47 +03:00
2020-07-28 01:06:57 +03:00
import NativeAnimatedNonTurboModule from './NativeAnimatedModule' ;
import NativeAnimatedTurboModule from './NativeAnimatedTurboModule' ;
2020-08-26 00:12:19 +03:00
import NativeEventEmitter from '../EventEmitter/NativeEventEmitter' ;
import Platform from '../Utilities/Platform' ;
2019-08-09 20:06:53 +03:00
import type { EventConfig } from './AnimatedEvent' ;
2019-05-23 03:09:00 +03:00
import type {
EventMapping ,
AnimatedNodeConfig ,
AnimatingNodeConfig ,
} from './NativeAnimatedModule' ;
import type { AnimationConfig , EndCallback } from './animations/Animation' ;
import type { InterpolationConfigType } from './nodes/AnimatedInterpolation' ;
2019-08-09 20:06:53 +03:00
import invariant from 'invariant' ;
2017-08-23 03:57:38 +03:00
2020-07-28 01:06:57 +03:00
// TODO T69437152 @petetheheat - Delete this fork when Fabric ships to 100%.
const NativeAnimatedModule =
2021-08-26 11:05:06 +03:00
Platform . OS === 'ios' && global . RN$Bridgeless === true
2020-07-28 01:06:57 +03:00
? NativeAnimatedTurboModule
: NativeAnimatedNonTurboModule ;
2016-08-12 04:10:16 +03:00
let _ _nativeAnimatedNodeTagCount = 1 ; /* used for animated nodes */
let _ _nativeAnimationIdCount = 1 ; /* used for started animations */
2016-03-24 16:18:39 +03:00
2016-08-12 04:10:16 +03:00
let nativeEventEmitter ;
2020-07-29 22:37:11 +03:00
let waitingForQueuedOperations = new Set ( ) ;
let queueOperations = false ;
let queue : Array < ( ) => void > = [ ] ;
2019-06-06 14:48:32 +03:00
2016-03-24 16:18:39 +03:00
/ * *
2019-07-23 13:19:41 +03:00
* Simple wrappers around NativeAnimatedModule to provide flow and autocomplete support for
2016-03-24 16:18:39 +03:00
* the native module methods
* /
2016-08-12 04:10:16 +03:00
const API = {
2021-11-03 08:10:46 +03:00
getValue : function (
2020-06-30 03:06:34 +03:00
tag : number ,
saveValueCallback : ( value : number ) => void ,
) : void {
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2021-08-03 14:02:46 +03:00
API . queueOperation ( ( ) => {
2020-06-30 03:06:34 +03:00
NativeAnimatedModule . getValue ( tag , saveValueCallback ) ;
2021-08-03 14:02:46 +03:00
} ) ;
2020-06-30 03:06:34 +03:00
} ,
2021-11-03 08:10:46 +03:00
setWaitingForIdentifier : function ( id : string ) : void {
2020-07-29 22:37:11 +03:00
waitingForQueuedOperations . add ( id ) ;
queueOperations = true ;
} ,
2021-11-03 08:10:46 +03:00
unsetWaitingForIdentifier : function ( id : string ) : void {
2020-07-29 22:37:11 +03:00
waitingForQueuedOperations . delete ( id ) ;
if ( waitingForQueuedOperations . size === 0 ) {
queueOperations = false ;
API . disableQueue ( ) ;
}
} ,
2021-11-03 08:10:46 +03:00
disableQueue : function ( ) : void {
2019-06-06 14:48:32 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-10-24 10:00:10 +03:00
if ( Platform . OS === 'android' ) {
NativeAnimatedModule . startOperationBatch ( ) ;
2020-07-29 22:37:11 +03:00
}
2020-10-24 10:00:10 +03:00
for ( let q = 0 , l = queue . length ; q < l ; q ++ ) {
queue [ q ] ( ) ;
}
queue . length = 0 ;
if ( Platform . OS === 'android' ) {
NativeAnimatedModule . finishOperationBatch ( ) ;
2020-07-29 22:37:11 +03:00
}
} ,
queueOperation : ( fn : ( ) => void ) : void => {
if ( queueOperations ) {
queue . push ( fn ) ;
} else {
fn ( ) ;
2019-06-06 14:48:32 +03:00
}
} ,
2021-11-03 08:10:46 +03:00
createAnimatedNode : function ( tag : number , config : AnimatedNodeConfig ) : void {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . createAnimatedNode ( tag , config ) ,
) ;
2016-03-24 16:18:39 +03:00
} ,
2021-11-03 08:10:46 +03:00
startListeningToAnimatedNodeValue : function ( tag : number ) {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . startListeningToAnimatedNodeValue ( tag ) ,
) ;
2016-08-04 23:11:37 +03:00
} ,
2021-11-03 08:10:46 +03:00
stopListeningToAnimatedNodeValue : function ( tag : number ) {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . stopListeningToAnimatedNodeValue ( tag ) ,
) ;
2016-08-04 23:11:37 +03:00
} ,
2021-11-03 08:10:46 +03:00
connectAnimatedNodes : function ( parentTag : number , childTag : number ) : void {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-10-24 10:00:10 +03:00
API . queueOperation ( ( ) =>
2020-07-29 22:37:11 +03:00
NativeAnimatedModule . connectAnimatedNodes ( parentTag , childTag ) ,
) ;
2016-03-24 16:18:39 +03:00
} ,
2021-11-03 08:10:46 +03:00
disconnectAnimatedNodes : function (
parentTag : number ,
childTag : number ,
) : void {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . disconnectAnimatedNodes ( parentTag , childTag ) ,
) ;
2016-03-24 16:18:39 +03:00
} ,
2021-11-03 08:10:46 +03:00
startAnimatingNode : function (
2019-10-17 04:43:00 +03:00
animationId : number ,
nodeTag : number ,
2019-05-23 03:09:00 +03:00
config : AnimatingNodeConfig ,
2017-08-23 03:57:38 +03:00
endCallback : EndCallback ,
) : void {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . startAnimatingNode (
animationId ,
nodeTag ,
config ,
endCallback ,
) ,
2017-08-23 03:57:38 +03:00
) ;
2016-04-22 10:01:55 +03:00
} ,
2021-11-03 08:10:46 +03:00
stopAnimation : function ( animationId : number ) {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) => NativeAnimatedModule . stopAnimation ( animationId ) ) ;
2016-03-24 16:18:39 +03:00
} ,
2021-11-03 08:10:46 +03:00
setAnimatedNodeValue : function ( nodeTag : number , value : number ) : void {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . setAnimatedNodeValue ( nodeTag , value ) ,
) ;
2016-03-24 16:18:39 +03:00
} ,
2021-11-03 08:10:46 +03:00
setAnimatedNodeOffset : function ( nodeTag : number , offset : number ) : void {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . setAnimatedNodeOffset ( nodeTag , offset ) ,
) ;
2016-09-26 20:17:29 +03:00
} ,
2021-11-03 08:10:46 +03:00
flattenAnimatedNodeOffset : function ( nodeTag : number ) : void {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . flattenAnimatedNodeOffset ( nodeTag ) ,
) ;
2016-09-26 20:17:29 +03:00
} ,
2021-11-03 08:10:46 +03:00
extractAnimatedNodeOffset : function ( nodeTag : number ) : void {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . extractAnimatedNodeOffset ( nodeTag ) ,
) ;
2016-11-08 07:36:52 +03:00
} ,
2021-11-03 08:10:46 +03:00
connectAnimatedNodeToView : function ( nodeTag : number , viewTag : number ) : void {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . connectAnimatedNodeToView ( nodeTag , viewTag ) ,
) ;
2016-03-24 16:18:39 +03:00
} ,
2021-11-03 08:10:46 +03:00
disconnectAnimatedNodeFromView : function (
2019-10-17 04:43:00 +03:00
nodeTag : number ,
viewTag : number ,
2017-08-23 03:57:38 +03:00
) : void {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . disconnectAnimatedNodeFromView ( nodeTag , viewTag ) ,
) ;
2016-03-24 16:18:39 +03:00
} ,
2021-11-03 08:10:46 +03:00
restoreDefaultValues : function ( nodeTag : number ) : void {
2019-11-05 02:37:45 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
// Backwards compat with older native runtimes, can be removed later.
if ( NativeAnimatedModule . restoreDefaultValues != null ) {
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . restoreDefaultValues ( nodeTag ) ,
) ;
2019-11-05 02:37:45 +03:00
}
} ,
2021-11-03 08:10:46 +03:00
dropAnimatedNode : function ( tag : number ) : void {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) => NativeAnimatedModule . dropAnimatedNode ( tag ) ) ;
2016-03-24 16:18:39 +03:00
} ,
2021-11-03 08:10:46 +03:00
addAnimatedEventToView : function (
2019-10-17 04:43:00 +03:00
viewTag : number ,
2017-08-23 03:57:38 +03:00
eventName : string ,
eventMapping : EventMapping ,
) {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . addAnimatedEventToView (
viewTag ,
eventName ,
eventMapping ,
) ,
2017-08-23 03:57:38 +03:00
) ;
} ,
removeAnimatedEventFromView (
2019-10-17 04:43:00 +03:00
viewTag : number ,
2017-08-23 03:57:38 +03:00
eventName : string ,
2019-10-17 04:43:00 +03:00
animatedNodeTag : number ,
2017-08-23 03:57:38 +03:00
) {
2019-05-23 03:09:00 +03:00
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
2020-07-29 22:37:11 +03:00
API . queueOperation ( ( ) =>
NativeAnimatedModule . removeAnimatedEventFromView (
viewTag ,
eventName ,
animatedNodeTag ,
) ,
2017-08-23 03:57:38 +03:00
) ;
2016-09-19 14:07:36 +03:00
} ,
2016-03-24 16:18:39 +03:00
} ;
/ * *
2017-01-27 05:14:40 +03:00
* Styles allowed by the native animated implementation .
2016-03-24 16:18:39 +03:00
*
* In general native animated implementation should support any numeric property that doesn ' t need
2017-01-27 05:14:40 +03:00
* to be updated through the shadow view hierarchy ( all non - layout properties ) .
2016-03-24 16:18:39 +03:00
* /
2020-07-17 01:58:49 +03:00
const SUPPORTED _STYLES = {
2017-01-27 05:14:40 +03:00
opacity : true ,
transform : true ,
2018-03-28 01:04:07 +03:00
borderRadius : true ,
borderBottomEndRadius : true ,
borderBottomLeftRadius : true ,
borderBottomRightRadius : true ,
borderBottomStartRadius : true ,
borderTopEndRadius : true ,
borderTopLeftRadius : true ,
borderTopRightRadius : true ,
borderTopStartRadius : true ,
2018-08-22 23:33:14 +03:00
elevation : true ,
2020-01-31 09:13:26 +03:00
zIndex : true ,
2017-10-31 15:59:47 +03:00
/* ios styles */
shadowOpacity : true ,
shadowRadius : true ,
2017-01-27 05:14:40 +03:00
/* legacy android transform properties */
scaleX : true ,
scaleY : true ,
translateX : true ,
translateY : true ,
2016-03-24 16:18:39 +03:00
} ;
2020-07-17 01:58:49 +03:00
const SUPPORTED _TRANSFORMS = {
2016-06-09 20:34:41 +03:00
translateX : true ,
translateY : true ,
scale : true ,
2016-08-03 00:23:18 +03:00
scaleX : true ,
scaleY : true ,
2016-06-09 20:34:41 +03:00
rotate : true ,
2016-08-03 00:23:18 +03:00
rotateX : true ,
rotateY : true ,
2019-08-05 12:29:42 +03:00
rotateZ : true ,
2016-08-03 00:23:18 +03:00
perspective : true ,
2016-06-09 20:34:41 +03:00
} ;
2017-12-14 00:34:58 +03:00
const SUPPORTED _INTERPOLATION _PARAMS = {
inputRange : true ,
outputRange : true ,
extrapolate : true ,
extrapolateRight : true ,
extrapolateLeft : true ,
} ;
function addWhitelistedStyleProp ( prop : string ) : void {
2020-07-17 01:58:49 +03:00
SUPPORTED _STYLES [ prop ] = true ;
2017-12-14 00:34:58 +03:00
}
function addWhitelistedTransformProp ( prop : string ) : void {
2020-07-17 01:58:49 +03:00
SUPPORTED _TRANSFORMS [ prop ] = true ;
2017-12-14 00:34:58 +03:00
}
function addWhitelistedInterpolationParam ( param : string ) : void {
SUPPORTED _INTERPOLATION _PARAMS [ param ] = true ;
}
2019-05-23 03:09:00 +03:00
function validateTransform (
configs : Array <
2019-11-21 20:38:13 +03:00
| {
type : 'animated' ,
property : string ,
nodeTag : ? number ,
...
}
| {
type : 'static' ,
property : string ,
value : number | string ,
...
} ,
2019-05-23 03:09:00 +03:00
> ,
) : void {
2020-03-25 07:35:58 +03:00
configs . forEach ( config => {
2020-07-17 01:58:49 +03:00
if ( ! SUPPORTED _TRANSFORMS . hasOwnProperty ( config . property ) ) {
2017-08-23 03:57:38 +03:00
throw new Error (
2020-04-09 20:55:48 +03:00
` Property ' ${ config . property } ' is not supported by native animated module ` ,
2017-08-23 03:57:38 +03:00
) ;
2016-06-09 20:34:41 +03:00
}
2016-08-03 00:23:18 +03:00
} ) ;
2016-06-09 20:34:41 +03:00
}
2019-11-21 20:38:13 +03:00
function validateStyles ( styles : { [ key : string ] : ? number , ... } ) : void {
2018-05-11 01:44:52 +03:00
for ( const key in styles ) {
2020-07-17 01:58:49 +03:00
if ( ! SUPPORTED _STYLES . hasOwnProperty ( key ) ) {
2017-08-23 03:57:38 +03:00
throw new Error (
` Style property ' ${ key } ' is not supported by native animated module ` ,
) ;
2016-03-24 16:18:39 +03:00
}
}
}
2019-05-23 03:09:00 +03:00
function validateInterpolation ( config : InterpolationConfigType ) : void {
2018-05-11 01:44:52 +03:00
for ( const key in config ) {
2016-04-23 12:37:01 +03:00
if ( ! SUPPORTED _INTERPOLATION _PARAMS . hasOwnProperty ( key ) ) {
2017-08-23 03:57:38 +03:00
throw new Error (
` Interpolation property ' ${ key } ' is not supported by native animated module ` ,
) ;
2016-04-23 12:37:01 +03:00
}
}
}
2016-03-24 16:18:39 +03:00
function generateNewNodeTag ( ) : number {
return _ _nativeAnimatedNodeTagCount ++ ;
}
2016-04-22 10:01:55 +03:00
function generateNewAnimationId ( ) : number {
return _ _nativeAnimationIdCount ++ ;
2016-03-24 16:18:39 +03:00
}
function assertNativeAnimatedModule ( ) : void {
invariant ( NativeAnimatedModule , 'Native animated module is not available' ) ;
}
2017-08-23 03:57:38 +03:00
let _warnedMissingNativeAnimated = false ;
2020-02-21 17:51:52 +03:00
function shouldUseNativeDriver (
config : { ... AnimationConfig , ... } | EventConfig ,
) : boolean {
2019-09-25 23:34:22 +03:00
if ( config . useNativeDriver == null ) {
console . warn (
'Animated: `useNativeDriver` was not specified. This is a required ' +
'option and must be explicitly set to `true` or `false`' ,
) ;
}
2019-05-23 03:09:00 +03:00
if ( config . useNativeDriver === true && ! NativeAnimatedModule ) {
2017-08-23 03:57:38 +03:00
if ( ! _warnedMissingNativeAnimated ) {
console . warn (
'Animated: `useNativeDriver` is not supported because the native ' +
'animated module is missing. Falling back to JS-based animation. To ' +
'resolve this, add `RCTAnimation` module to this app, or remove ' +
'`useNativeDriver`. ' +
2021-10-02 07:21:03 +03:00
'Make sure to run `bundle exec pod install` first. Read more about autolinking: https://github.com/react-native-community/cli/blob/master/docs/autolinking.md' ,
2017-08-23 03:57:38 +03:00
) ;
_warnedMissingNativeAnimated = true ;
}
return false ;
}
return config . useNativeDriver || false ;
2016-11-02 09:55:21 +03:00
}
2019-06-06 14:48:32 +03:00
function transformDataType ( value : number | string ) : number | string {
2019-01-31 12:33:38 +03:00
// 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 {
2019-06-06 14:48:32 +03:00
return value ;
2019-01-31 12:33:38 +03:00
}
}
2016-03-24 16:18:39 +03:00
module . exports = {
API ,
2017-12-14 00:34:58 +03:00
addWhitelistedStyleProp ,
addWhitelistedTransformProp ,
addWhitelistedInterpolationParam ,
2016-03-24 16:18:39 +03:00
validateStyles ,
2016-06-09 20:34:41 +03:00
validateTransform ,
2016-04-23 12:37:01 +03:00
validateInterpolation ,
2016-03-24 16:18:39 +03:00
generateNewNodeTag ,
2016-04-22 10:01:55 +03:00
generateNewAnimationId ,
2016-03-24 16:18:39 +03:00
assertNativeAnimatedModule ,
2017-08-23 03:57:38 +03:00
shouldUseNativeDriver ,
2019-01-31 12:33:38 +03:00
transformDataType ,
2021-04-01 04:19:54 +03:00
// $FlowExpectedError[unsafe-getters-setters] - unsafe getter lint suppresion
// $FlowExpectedError[missing-type-arg] - unsafe getter lint suppresion
2019-08-09 20:06:53 +03:00
get nativeEventEmitter ( ) : NativeEventEmitter {
2016-08-12 04:10:16 +03:00
if ( ! nativeEventEmitter ) {
2021-04-12 16:23:53 +03:00
nativeEventEmitter = new NativeEventEmitter (
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
// If you want to use the native module on other platforms, please remove this condition and test its behavior
Platform . OS !== 'ios' ? null : NativeAnimatedModule ,
) ;
2016-08-12 04:10:16 +03:00
}
return nativeEventEmitter ;
} ,
2016-03-24 16:18:39 +03:00
} ;