react-native-macos/Libraries/Animated/nodes/AnimatedValueXY.js

251 строка
6.3 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
*/
'use strict';
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import AnimatedValue from './AnimatedValue';
import AnimatedWithChildren from './AnimatedWithChildren';
import invariant from 'invariant';
export type AnimatedValueXYConfig = $ReadOnly<{
useNativeDriver: boolean,
}>;
type ValueXYListenerCallback = (value: {
x: number,
y: number,
...
}) => mixed;
let _uniqueId = 1;
/**
* 2D Value for driving 2D animations, such as pan gestures. Almost identical
* API to normal `Animated.Value`, but multiplexed.
*
* See https://reactnative.dev/docs/animatedvaluexy
*/
export default class AnimatedValueXY extends AnimatedWithChildren {
x: AnimatedValue;
y: AnimatedValue;
_listeners: {
[key: string]: {
x: string,
y: string,
...
},
...
};
constructor(
valueIn?: ?{
+x: number | AnimatedValue,
+y: number | AnimatedValue,
...
},
config?: ?AnimatedValueXYConfig,
) {
super();
const value: any = valueIn || {x: 0, y: 0}; // @flowfixme: shouldn't need `: any`
if (typeof value.x === 'number' && typeof value.y === 'number') {
this.x = new AnimatedValue(value.x);
this.y = new AnimatedValue(value.y);
} else {
invariant(
value.x instanceof AnimatedValue && value.y instanceof AnimatedValue,
'AnimatedValueXY must be initialized with an object of numbers or ' +
'AnimatedValues.',
);
this.x = value.x;
this.y = value.y;
}
this._listeners = {};
if (config && config.useNativeDriver) {
this.__makeNative();
}
}
/**
* Directly set the value. This will stop any animations running on the value
* and update all the bound properties.
*
* See https://reactnative.dev/docs/animatedvaluexy#setvalue
*/
setValue(value: {x: number, y: number, ...}) {
this.x.setValue(value.x);
this.y.setValue(value.y);
}
/**
* Sets an offset that is applied on top of whatever value is set, whether
* via `setValue`, an animation, or `Animated.event`. Useful for compensating
* things like the start of a pan gesture.
*
* See https://reactnative.dev/docs/animatedvaluexy#setoffset
*/
setOffset(offset: {x: number, y: number, ...}) {
this.x.setOffset(offset.x);
this.y.setOffset(offset.y);
}
/**
* Merges the offset value into the base value and resets the offset to zero.
* The final output of the value is unchanged.
*
* See https://reactnative.dev/docs/animatedvaluexy#flattenoffset
*/
flattenOffset(): void {
this.x.flattenOffset();
this.y.flattenOffset();
}
/**
* Sets the offset value to the base value, and resets the base value to
* zero. The final output of the value is unchanged.
*
* See https://reactnative.dev/docs/animatedvaluexy#extractoffset
*/
extractOffset(): void {
this.x.extractOffset();
this.y.extractOffset();
}
__getValue(): {
x: number,
y: number,
...
} {
return {
x: this.x.__getValue(),
y: this.y.__getValue(),
};
}
/**
* Stops any animation and resets the value to its original.
*
* See https://reactnative.dev/docs/animatedvaluexy#resetanimation
*/
resetAnimation(
callback?: (value: {
x: number,
y: number,
...
}) => void,
): void {
this.x.resetAnimation();
this.y.resetAnimation();
callback && callback(this.__getValue());
}
/**
* Stops any running animation or tracking. `callback` is invoked with the
* final value after stopping the animation, which is useful for updating
* state to match the animation position with layout.
*
* See https://reactnative.dev/docs/animatedvaluexy#stopanimation
*/
stopAnimation(
callback?: (value: {
x: number,
y: number,
...
}) => void,
): void {
this.x.stopAnimation();
this.y.stopAnimation();
callback && callback(this.__getValue());
}
/**
* 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.
*
* Returns a string that serves as an identifier for the listener.
*
* See https://reactnative.dev/docs/animatedvaluexy#addlistener
*/
addListener(callback: ValueXYListenerCallback): string {
const id = String(_uniqueId++);
const jointCallback = ({value: number}: any) => {
callback(this.__getValue());
};
this._listeners[id] = {
x: this.x.addListener(jointCallback),
y: this.y.addListener(jointCallback),
};
return id;
}
/**
* Unregister a listener. The `id` param shall match the identifier
* previously returned by `addListener()`.
*
* See https://reactnative.dev/docs/animatedvaluexy#removelistener
*/
removeListener(id: string): void {
this.x.removeListener(this._listeners[id].x);
this.y.removeListener(this._listeners[id].y);
delete this._listeners[id];
}
/**
* Remove all registered listeners.
*
* See https://reactnative.dev/docs/animatedvaluexy#removealllisteners
*/
removeAllListeners(): void {
this.x.removeAllListeners();
this.y.removeAllListeners();
this._listeners = {};
}
/**
* Converts `{x, y}` into `{left, top}` for use in style.
*
* See https://reactnative.dev/docs/animatedvaluexy#getlayout
*/
getLayout(): {[key: string]: AnimatedValue, ...} {
return {
left: this.x,
top: this.y,
};
}
/**
* Converts `{x, y}` into a useable translation transform.
*
* See https://reactnative.dev/docs/animatedvaluexy#gettranslatetransform
*/
getTranslateTransform(): Array<{[key: string]: AnimatedValue, ...}> {
return [{translateX: this.x}, {translateY: this.y}];
}
__attach(): void {
this.x.__addChild(this);
this.y.__addChild(this);
super.__attach();
}
__detach(): void {
this.x.__removeChild(this);
this.y.__removeChild(this);
super.__detach();
}
__makeNative(platformConfig: ?PlatformConfig) {
this.x.__makeNative(platformConfig);
this.y.__makeNative(platformConfig);
super.__makeNative(platformConfig);
}
}