jiuyiUniapp/service/node_modules/react-native/Libraries/Animated/animations/Animation.js

172 lines
5.0 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and 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-local
* @format
*/
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import type AnimatedNode from '../nodes/AnimatedNode';
import type AnimatedValue from '../nodes/AnimatedValue';
import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
import AnimatedProps from '../nodes/AnimatedProps';
export type EndResult = {finished: boolean, value?: number, ...};
export type EndCallback = (result: EndResult) => void;
export type AnimationConfig = $ReadOnly<{
isInteraction?: boolean,
useNativeDriver: boolean,
platformConfig?: PlatformConfig,
onComplete?: ?EndCallback,
iterations?: number,
isLooping?: boolean,
...
}>;
let startNativeAnimationNextId = 1;
// Important note: start() and stop() will only be called at most once.
// Once an animation has been stopped or finished its course, it will
// not be reused.
export default class Animation {
#nativeID: ?number;
#onEnd: ?EndCallback;
#useNativeDriver: boolean;
__active: boolean;
__isInteraction: boolean;
__isLooping: ?boolean;
__iterations: number;
constructor(config: AnimationConfig) {
this.#useNativeDriver = NativeAnimatedHelper.shouldUseNativeDriver(config);
this.__active = false;
this.__isInteraction = config.isInteraction ?? !this.#useNativeDriver;
this.__isLooping = config.isLooping;
this.__iterations = config.iterations ?? 1;
}
start(
fromValue: number,
onUpdate: (value: number) => void,
onEnd: ?EndCallback,
previousAnimation: ?Animation,
animatedValue: AnimatedValue,
): void {
if (!this.#useNativeDriver && animatedValue.__isNative === true) {
throw new Error(
'Attempting to run JS driven animation on animated node ' +
'that has been moved to "native" earlier by starting an ' +
'animation with `useNativeDriver: true`',
);
}
this.#onEnd = onEnd;
this.__active = true;
}
stop(): void {
if (this.#nativeID != null) {
NativeAnimatedHelper.API.stopAnimation(this.#nativeID);
}
this.__active = false;
}
__getNativeAnimationConfig(): $ReadOnly<{
platformConfig: ?PlatformConfig,
...
}> {
// Subclasses that have corresponding animation implementation done in native
// should override this method
throw new Error('This animation type cannot be offloaded to native');
}
__findAnimatedPropsNodes(node: AnimatedNode): Array<AnimatedProps> {
const result = [];
if (node instanceof AnimatedProps) {
result.push(node);
return result;
}
for (const child of node.__getChildren()) {
result.push(...this.__findAnimatedPropsNodes(child));
}
return result;
}
__startAnimationIfNative(animatedValue: AnimatedValue): boolean {
if (!this.#useNativeDriver) {
return false;
}
const startNativeAnimationWaitId = `${startNativeAnimationNextId}:startAnimation`;
startNativeAnimationNextId += 1;
NativeAnimatedHelper.API.setWaitingForIdentifier(
startNativeAnimationWaitId,
);
try {
const config = this.__getNativeAnimationConfig();
animatedValue.__makeNative(config.platformConfig);
this.#nativeID = NativeAnimatedHelper.generateNewAnimationId();
NativeAnimatedHelper.API.startAnimatingNode(
this.#nativeID,
animatedValue.__getNativeTag(),
config,
result => {
this.__notifyAnimationEnd(result);
// When using natively driven animations, once the animation completes,
// we need to ensure that the JS side nodes are synced with the updated
// values.
const {value} = result;
if (value != null) {
animatedValue.__onAnimatedValueUpdateReceived(value);
if (
ReactNativeFeatureFlags.shouldSkipStateUpdatesForLoopingAnimations() &&
this.__isLooping === true
) {
return;
}
// Once the JS side node is synced with the updated values, trigger an
// update on the AnimatedProps nodes to call any registered callbacks.
this.__findAnimatedPropsNodes(animatedValue).forEach(node =>
node.update(),
);
}
},
);
return true;
} catch (e) {
throw e;
} finally {
NativeAnimatedHelper.API.unsetWaitingForIdentifier(
startNativeAnimationWaitId,
);
}
}
/**
* Notify the completion callback that the animation has ended. The completion
* callback will never be called more than once.
*/
__notifyAnimationEnd(result: EndResult): void {
const callback = this.#onEnd;
if (callback != null) {
this.#onEnd = null;
callback(result);
}
}
}