mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-02 09:21:44 +08:00
Summary: AnimatedImplementation.js is getting pretty hard to navigate and reason about so I split it up into different modules. Also took the opportunity to run prettier on that code and do some minor const/let refactorings. This doesn't change any logic, mostly just moves code around and add proper imports / exports. This opens the door for further cleanup and flow type improvements but want to keep this already big PR as small as possible. Discussion points: - Should I use haste for this? Animated is pretty much a standalone module, it still uses a few haste imports but for new modules I used commonjs imports to avoid polluting the haste global namespace too much. The new modules are all internal to Animated and should not be imported externally. - We're using `requestAnimationFrame` from fbjs instead of the one available globally in RN and browsers is there a reason for that? - Should we even support web in this implementation? There is a standalone repo that exist for Animated web. Is this implementation of Animated web used internally at facebook? - Probably still related to web, we used some weird Set polyfill is that still needed? Notes: There is a small regression for docs where the type of some classes that are exported like AnimatedValue show up as CallExpression instead if Class. <img width="655" alt="screen shot 2017-08-14 at 3 19 18 am" src="https://user-images.githubusercontent.com/2677334/29261820-8f243036-809f-11e7-8bf0-0fe9f93939ca.png"> **Test plan** Tested that all Animated related examples still work in RNTester on iOS and Android. Tested that the doc is still working Ran unit tests Closes https://github.com/facebook/react-native/pull/15485 Differential Revision: D5679886 Pulled By: sahrens fbshipit-source-id: d3e9b6987ab5ff2cd20108c3b9d860c7536be8a0
254 lines
7.1 KiB
JavaScript
254 lines
7.1 KiB
JavaScript
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @providesModule NativeAnimatedHelper
|
|
* @flow
|
|
* @format
|
|
*/
|
|
'use strict';
|
|
|
|
const NativeAnimatedModule = require('NativeModules').NativeAnimatedModule;
|
|
const NativeEventEmitter = require('NativeEventEmitter');
|
|
|
|
const invariant = require('fbjs/lib/invariant');
|
|
|
|
import type {AnimationConfig} from './animations/Animation';
|
|
import type {EventConfig} from './AnimatedEvent';
|
|
|
|
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<string>,
|
|
animatedValueTag: ?number,
|
|
};
|
|
|
|
let nativeEventEmitter;
|
|
|
|
/**
|
|
* 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();
|
|
NativeAnimatedModule.createAnimatedNode(tag, config);
|
|
},
|
|
startListeningToAnimatedNodeValue: function(tag: ?number) {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.startListeningToAnimatedNodeValue(tag);
|
|
},
|
|
stopListeningToAnimatedNodeValue: function(tag: ?number) {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.stopListeningToAnimatedNodeValue(tag);
|
|
},
|
|
connectAnimatedNodes: function(parentTag: ?number, childTag: ?number): void {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.connectAnimatedNodes(parentTag, childTag);
|
|
},
|
|
disconnectAnimatedNodes: function(
|
|
parentTag: ?number,
|
|
childTag: ?number,
|
|
): void {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.disconnectAnimatedNodes(parentTag, childTag);
|
|
},
|
|
startAnimatingNode: function(
|
|
animationId: ?number,
|
|
nodeTag: ?number,
|
|
config: Object,
|
|
endCallback: EndCallback,
|
|
): void {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.startAnimatingNode(
|
|
animationId,
|
|
nodeTag,
|
|
config,
|
|
endCallback,
|
|
);
|
|
},
|
|
stopAnimation: function(animationId: ?number) {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.stopAnimation(animationId);
|
|
},
|
|
setAnimatedNodeValue: function(nodeTag: ?number, value: ?number): void {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.setAnimatedNodeValue(nodeTag, value);
|
|
},
|
|
setAnimatedNodeOffset: function(nodeTag: ?number, offset: ?number): void {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.setAnimatedNodeOffset(nodeTag, offset);
|
|
},
|
|
flattenAnimatedNodeOffset: function(nodeTag: ?number): void {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.flattenAnimatedNodeOffset(nodeTag);
|
|
},
|
|
extractAnimatedNodeOffset: function(nodeTag: ?number): void {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.extractAnimatedNodeOffset(nodeTag);
|
|
},
|
|
connectAnimatedNodeToView: function(
|
|
nodeTag: ?number,
|
|
viewTag: ?number,
|
|
): void {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.connectAnimatedNodeToView(nodeTag, viewTag);
|
|
},
|
|
disconnectAnimatedNodeFromView: function(
|
|
nodeTag: ?number,
|
|
viewTag: ?number,
|
|
): void {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.disconnectAnimatedNodeFromView(nodeTag, viewTag);
|
|
},
|
|
dropAnimatedNode: function(tag: ?number): void {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.dropAnimatedNode(tag);
|
|
},
|
|
addAnimatedEventToView: function(
|
|
viewTag: ?number,
|
|
eventName: string,
|
|
eventMapping: EventMapping,
|
|
) {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.addAnimatedEventToView(
|
|
viewTag,
|
|
eventName,
|
|
eventMapping,
|
|
);
|
|
},
|
|
removeAnimatedEventFromView(
|
|
viewTag: ?number,
|
|
eventName: string,
|
|
animatedNodeTag: ?number,
|
|
) {
|
|
assertNativeAnimatedModule();
|
|
NativeAnimatedModule.removeAnimatedEventFromView(
|
|
viewTag,
|
|
eventName,
|
|
animatedNodeTag,
|
|
);
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Styles allowed by the native animated implementation.
|
|
*
|
|
* In general native animated implementation should support any numeric property that doesn't need
|
|
* to be updated through the shadow view hierarchy (all non-layout properties).
|
|
*/
|
|
const STYLES_WHITELIST = {
|
|
opacity: true,
|
|
transform: true,
|
|
/* legacy android transform properties */
|
|
scaleX: true,
|
|
scaleY: true,
|
|
translateX: true,
|
|
translateY: true,
|
|
};
|
|
|
|
const TRANSFORM_WHITELIST = {
|
|
translateX: true,
|
|
translateY: true,
|
|
scale: true,
|
|
scaleX: true,
|
|
scaleY: true,
|
|
rotate: true,
|
|
rotateX: true,
|
|
rotateY: true,
|
|
perspective: true,
|
|
};
|
|
|
|
function validateTransform(configs: Array<Object>): void {
|
|
configs.forEach(config => {
|
|
if (!TRANSFORM_WHITELIST.hasOwnProperty(config.property)) {
|
|
throw new Error(
|
|
`Property '${config.property}' is not supported by native animated module`,
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
function validateStyles(styles: Object): void {
|
|
for (var key in styles) {
|
|
if (!STYLES_WHITELIST.hasOwnProperty(key)) {
|
|
throw new Error(
|
|
`Style property '${key}' is not supported by native animated module`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function validateInterpolation(config: Object): void {
|
|
var SUPPORTED_INTERPOLATION_PARAMS = {
|
|
inputRange: true,
|
|
outputRange: true,
|
|
extrapolate: true,
|
|
extrapolateRight: true,
|
|
extrapolateLeft: true,
|
|
};
|
|
for (var key in config) {
|
|
if (!SUPPORTED_INTERPOLATION_PARAMS.hasOwnProperty(key)) {
|
|
throw new Error(
|
|
`Interpolation property '${key}' is not supported by native animated module`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function generateNewNodeTag(): number {
|
|
return __nativeAnimatedNodeTagCount++;
|
|
}
|
|
|
|
function generateNewAnimationId(): number {
|
|
return __nativeAnimationIdCount++;
|
|
}
|
|
|
|
function assertNativeAnimatedModule(): void {
|
|
invariant(NativeAnimatedModule, 'Native animated module is not available');
|
|
}
|
|
|
|
let _warnedMissingNativeAnimated = false;
|
|
|
|
function shouldUseNativeDriver(config: AnimationConfig | EventConfig): boolean {
|
|
if (config.useNativeDriver && !NativeAnimatedModule) {
|
|
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`. ' +
|
|
'More info: https://github.com/facebook/react-native/issues/11094#issuecomment-263240420',
|
|
);
|
|
_warnedMissingNativeAnimated = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return config.useNativeDriver || false;
|
|
}
|
|
|
|
module.exports = {
|
|
API,
|
|
validateStyles,
|
|
validateTransform,
|
|
validateInterpolation,
|
|
generateNewNodeTag,
|
|
generateNewAnimationId,
|
|
assertNativeAnimatedModule,
|
|
shouldUseNativeDriver,
|
|
get nativeEventEmitter() {
|
|
if (!nativeEventEmitter) {
|
|
nativeEventEmitter = new NativeEventEmitter(NativeAnimatedModule);
|
|
}
|
|
return nativeEventEmitter;
|
|
},
|
|
};
|