jiuyiUniapp/service/node_modules/react-native/Libraries/Animated/nodes/AnimatedNode.js

201 lines
5.6 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
* @format
*/
import type {EventSubscription} from '../../vendor/emitter/EventEmitter';
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
import invariant from 'invariant';
const {startListeningToAnimatedNodeValue, stopListeningToAnimatedNodeValue} =
NativeAnimatedHelper.API;
type ValueListenerCallback = (state: {value: number, ...}) => mixed;
let _uniqueId = 1;
let _assertNativeAnimatedModule: ?() => void = () => {
NativeAnimatedHelper.assertNativeAnimatedModule();
// We only have to assert that the module exists once. After we've asserted
// this, clear out the function so we know to skip it in the future.
_assertNativeAnimatedModule = null;
};
export default class AnimatedNode {
#listeners: Map<string, ValueListenerCallback> = new Map();
#updateSubscription: ?EventSubscription = null;
_platformConfig: ?PlatformConfig = undefined;
__attach(): void {}
__detach(): void {
this.removeAllListeners();
if (this.__isNative && this.__nativeTag != null) {
NativeAnimatedHelper.API.dropAnimatedNode(this.__nativeTag);
this.__nativeTag = undefined;
}
}
__getValue(): any {}
__getAnimatedValue(): any {
return this.__getValue();
}
__addChild(child: AnimatedNode) {}
__removeChild(child: AnimatedNode) {}
__getChildren(): $ReadOnlyArray<AnimatedNode> {
return [];
}
/* Methods and props used by native Animated impl */
__isNative: boolean = false;
__nativeTag: ?number = undefined;
__makeNative(platformConfig: ?PlatformConfig): void {
// Subclasses are expected to set `__isNative` to true before this.
invariant(
this.__isNative,
'This node cannot be made a "native" animated node',
);
this._platformConfig = platformConfig;
if (this.#listeners.size > 0) {
this.#ensureUpdateSubscriptionExists();
}
}
/**
* 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 https://reactnative.dev/docs/animatedvalue#addlistener
*/
addListener(callback: (value: any) => mixed): string {
const id = String(_uniqueId++);
this.#listeners.set(id, callback);
if (this.__isNative) {
this.#ensureUpdateSubscriptionExists();
}
return id;
}
/**
* Unregister a listener. The `id` param shall match the identifier
* previously returned by `addListener()`.
*
* See https://reactnative.dev/docs/animatedvalue#removelistener
*/
removeListener(id: string): void {
this.#listeners.delete(id);
if (this.__isNative && this.#listeners.size === 0) {
this.#updateSubscription?.remove();
}
}
/**
* Remove all registered listeners.
*
* See https://reactnative.dev/docs/animatedvalue#removealllisteners
*/
removeAllListeners(): void {
this.#listeners.clear();
if (this.__isNative) {
this.#updateSubscription?.remove();
}
}
hasListeners(): boolean {
return this.#listeners.size > 0;
}
#ensureUpdateSubscriptionExists(): void {
if (this.#updateSubscription != null) {
return;
}
const nativeTag = this.__getNativeTag();
startListeningToAnimatedNodeValue(nativeTag);
const subscription: EventSubscription =
NativeAnimatedHelper.nativeEventEmitter.addListener(
'onAnimatedValueUpdate',
data => {
if (data.tag === nativeTag) {
this.__onAnimatedValueUpdateReceived(data.value);
}
},
);
this.#updateSubscription = {
remove: () => {
// Only this function assigns to `this.#updateSubscription`.
if (this.#updateSubscription == null) {
return;
}
this.#updateSubscription = null;
subscription.remove();
stopListeningToAnimatedNodeValue(nativeTag);
},
};
}
__onAnimatedValueUpdateReceived(value: number): void {
this.__callListeners(value);
}
__callListeners(value: number): void {
const event = {value};
this.#listeners.forEach(listener => {
listener(event);
});
}
__getNativeTag(): number {
let nativeTag = this.__nativeTag;
if (nativeTag == null) {
_assertNativeAnimatedModule?.();
// `__isNative` is initialized as false and only ever set to true. So we
// only need to check it once here when initializing `__nativeTag`.
invariant(
this.__isNative,
'Attempt to get native tag from node not marked as "native"',
);
nativeTag = NativeAnimatedHelper.generateNewNodeTag();
this.__nativeTag = nativeTag;
const config = this.__getNativeConfig();
if (this._platformConfig) {
config.platformConfig = this._platformConfig;
}
NativeAnimatedHelper.API.createAnimatedNode(nativeTag, config);
}
return nativeTag;
}
__getNativeConfig(): Object {
throw new Error(
'This JS animated node type cannot be used as native animated node',
);
}
__getPlatformConfig(): ?PlatformConfig {
return this._platformConfig;
}
__setPlatformConfig(platformConfig: ?PlatformConfig) {
this._platformConfig = platformConfig;
}
/**
* NOTE: This is intended to prevent `JSON.stringify` from throwing "cyclic
* structure" errors in React DevTools. Avoid depending on this!
*/
toJSON(): mixed {
return this.__getValue();
}
}