react-native-reanimated
Version:
More powerful alternative to Animated library for React Native.
159 lines (136 loc) • 4.32 kB
text/typescript
import { LayoutAnimationType } from '../../../commonTypes';
import type { ReanimatedHTMLElement } from '../../../ReanimatedModule/js-reanimated';
import type { TransitionData } from '../animationParser';
import type { AnimationConfig } from '../config';
import type { WebEasingsNames } from '../Easing.web';
import { getEasingByName } from '../Easing.web';
function resetStyle(component: HTMLElement) {
component.style.animationName = ''; // This line prevents unwanted entering animation
component.style.position = 'absolute';
component.style.top = '0px';
component.style.left = '0px';
component.style.margin = '0px';
component.style.width = '100%';
component.style.height = '100%';
}
function showChildren(
parent: HTMLElement,
childrenDisplayProperty: Map<HTMLElement, string>,
shouldShow: boolean
) {
for (let i = 0; i < parent.children.length; ++i) {
const child = parent.children[i] as HTMLElement;
if (shouldShow) {
child.style.display = childrenDisplayProperty.get(child)!;
} else {
childrenDisplayProperty.set(child, child.style.display);
child.style.display = 'none';
}
}
}
function prepareParent(
element: ReanimatedHTMLElement,
dummy: ReanimatedHTMLElement,
animationConfig: AnimationConfig,
transitionData: TransitionData
) {
// Adjust configs for `CurvedTransition` and create config object for dummy
animationConfig.easing = getEasingByName(
transitionData.easingX as WebEasingsNames
);
const childrenDisplayProperty = new Map<HTMLElement, string>();
showChildren(element, childrenDisplayProperty, false);
const originalBackgroundColor = element.style.backgroundColor;
element.style.backgroundColor = 'transparent';
const onFinalize = () => {
if (element.contains(dummy)) {
element.removeChild(dummy);
}
showChildren(element, childrenDisplayProperty, true);
element.style.backgroundColor = originalBackgroundColor;
};
const animationCancelCallback = () => {
onFinalize();
element.removeEventListener('animationcancel', animationCancelCallback);
};
const animationEndCallback = () => {
onFinalize();
element.removeEventListener('animationend', animationEndCallback);
};
element.addEventListener('animationend', animationEndCallback);
element.addEventListener('animationcancel', animationCancelCallback);
element.appendChild(dummy);
}
function prepareDummy(
element: ReanimatedHTMLElement,
animationConfig: AnimationConfig,
transitionData: TransitionData,
dummyTransitionKeyframeName: string
) {
const dummyAnimationConfig: AnimationConfig = {
animationName: dummyTransitionKeyframeName,
animationType: LayoutAnimationType.LAYOUT,
duration: animationConfig.duration,
delay: animationConfig.delay,
easing: getEasingByName(transitionData.easingY as WebEasingsNames),
callback: null,
reversed: false,
};
const dummy = element.cloneNode(true) as ReanimatedHTMLElement;
resetStyle(dummy);
return { dummy, dummyAnimationConfig };
}
export function prepareCurvedTransition(
element: ReanimatedHTMLElement,
animationConfig: AnimationConfig,
transitionData: TransitionData,
dummyTransitionKeyframeName: string
) {
const { dummy, dummyAnimationConfig } = prepareDummy(
element,
animationConfig,
transitionData,
dummyTransitionKeyframeName
);
prepareParent(element, dummy, animationConfig, transitionData);
return { dummy, dummyAnimationConfig };
}
export function CurvedTransition(
keyframeXName: string,
keyframeYName: string,
transitionData: TransitionData
) {
const keyframeXObj = {
name: keyframeXName,
style: {
0: {
transform: [
{
translateX: `${transitionData.translateX}px`,
scale: `${transitionData.scaleX},${transitionData.scaleY}`,
},
],
},
},
duration: 300,
};
const keyframeYObj = {
name: keyframeYName,
style: {
0: {
transform: [
{
translateY: `${transitionData.translateY}px`,
scale: `${transitionData.scaleX},${transitionData.scaleY}`,
},
],
},
},
duration: 300,
};
return {
firstKeyframeObj: keyframeXObj,
secondKeyframeObj: keyframeYObj,
};
}
;