amis-rpc-design/node_modules/react-native/Libraries/LayoutAnimation/LayoutAnimation.js
2023-10-07 19:42:30 +08:00

202 lines
5.7 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
*/
'use strict';
import type {Spec as FabricUIManagerSpec} from '../ReactNative/FabricUIManager';
import type {
LayoutAnimationConfig as LayoutAnimationConfig_,
LayoutAnimationProperty,
LayoutAnimationType,
} from '../Renderer/shims/ReactNativeTypes';
import {getFabricUIManager} from '../ReactNative/FabricUIManager';
import ReactNativeFeatureFlags from '../ReactNative/ReactNativeFeatureFlags';
import Platform from '../Utilities/Platform';
const UIManager = require('../ReactNative/UIManager');
// Reexport type
export type LayoutAnimationConfig = LayoutAnimationConfig_;
type OnAnimationDidEndCallback = () => void;
type OnAnimationDidFailCallback = () => void;
let isLayoutAnimationEnabled: boolean =
ReactNativeFeatureFlags.isLayoutAnimationEnabled();
function setEnabled(value: boolean) {
isLayoutAnimationEnabled = isLayoutAnimationEnabled;
}
/**
* Configures the next commit to be animated.
*
* onAnimationDidEnd is guaranteed to be called when the animation completes.
* onAnimationDidFail is *never* called in the classic, pre-Fabric renderer,
* and never has been. In the new renderer (Fabric) it is called only if configuration
* parsing fails.
*/
function configureNext(
config: LayoutAnimationConfig,
onAnimationDidEnd?: OnAnimationDidEndCallback,
onAnimationDidFail?: OnAnimationDidFailCallback,
) {
if (Platform.isTesting) {
return;
}
if (!isLayoutAnimationEnabled) {
return;
}
// Since LayoutAnimations may possibly be disabled for now on iOS (Fabric),
// or Android (non-Fabric) we race a setTimeout with animation completion,
// in case onComplete is never called
// from native. Once LayoutAnimations+Fabric unconditionally ship everywhere, we can
// delete this mechanism at least in the Fabric branch.
let animationCompletionHasRun = false;
const onAnimationComplete = () => {
if (animationCompletionHasRun) {
return;
}
animationCompletionHasRun = true;
clearTimeout(raceWithAnimationId);
onAnimationDidEnd?.();
};
const raceWithAnimationId = setTimeout(
onAnimationComplete,
(config.duration ?? 0) + 17 /* one frame + 1ms */,
);
// In Fabric, LayoutAnimations are unconditionally enabled for Android, and
// conditionally enabled on iOS (pending fully shipping; this is a temporary state).
const FabricUIManager = getFabricUIManager();
if (FabricUIManager?.configureNextLayoutAnimation) {
global?.nativeFabricUIManager?.configureNextLayoutAnimation(
config,
onAnimationComplete,
onAnimationDidFail ??
function () {} /* this will only be called if configuration parsing fails */,
);
return;
}
// This will only run if Fabric is *not* installed.
// If you have Fabric + non-Fabric running in the same VM, non-Fabric LayoutAnimations
// will not work.
if (UIManager?.configureNextLayoutAnimation) {
UIManager.configureNextLayoutAnimation(
config,
onAnimationComplete ?? function () {},
onAnimationDidFail ??
function () {} /* this should never be called in Non-Fabric */,
);
}
}
function create(
duration: number,
type: LayoutAnimationType,
property: LayoutAnimationProperty,
): LayoutAnimationConfig {
return {
duration,
create: {type, property},
update: {type},
delete: {type, property},
};
}
const Presets = {
easeInEaseOut: (create(
300,
'easeInEaseOut',
'opacity',
): LayoutAnimationConfig),
linear: (create(500, 'linear', 'opacity'): LayoutAnimationConfig),
spring: {
duration: 700,
create: {
type: 'linear',
property: 'opacity',
},
update: {
type: 'spring',
springDamping: 0.4,
},
delete: {
type: 'linear',
property: 'opacity',
},
},
};
/**
* Automatically animates views to their new positions when the
* next layout happens.
*
* A common way to use this API is to call it before calling `setState`.
*
* Note that in order to get this to work on **Android** you need to set the following flags via `UIManager`:
*
* UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
*/
const LayoutAnimation = {
/**
* Schedules an animation to happen on the next layout.
*
* @param config Specifies animation properties:
*
* - `duration` in milliseconds
* - `create`, `AnimationConfig` for animating in new views
* - `update`, `AnimationConfig` for animating views that have been updated
*
* @param onAnimationDidEnd Called when the animation finished.
* Only supported on iOS.
* @param onError Called on error. Only supported on iOS.
*/
configureNext,
/**
* Helper for creating a config for `configureNext`.
*/
create,
Types: Object.freeze({
spring: 'spring',
linear: 'linear',
easeInEaseOut: 'easeInEaseOut',
easeIn: 'easeIn',
easeOut: 'easeOut',
keyboard: 'keyboard',
}),
Properties: Object.freeze({
opacity: 'opacity',
scaleX: 'scaleX',
scaleY: 'scaleY',
scaleXY: 'scaleXY',
}),
checkConfig(...args: Array<mixed>) {
console.error('LayoutAnimation.checkConfig(...) has been disabled.');
},
Presets,
easeInEaseOut: (configureNext.bind(null, Presets.easeInEaseOut): (
onAnimationDidEnd?: OnAnimationDidEndCallback,
) => void),
linear: (configureNext.bind(null, Presets.linear): (
onAnimationDidEnd?: OnAnimationDidEndCallback,
) => void),
spring: (configureNext.bind(null, Presets.spring): (
onAnimationDidEnd?: OnAnimationDidEndCallback,
) => void),
setEnabled,
};
module.exports = LayoutAnimation;