mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-15 09:17:26 +08:00
318 lines
6.9 KiB
TypeScript
318 lines
6.9 KiB
TypeScript
import { Animated } from 'react-native';
|
|
import conditional from '../utils/conditional';
|
|
import {
|
|
StackCardInterpolationProps,
|
|
StackCardInterpolatedStyle,
|
|
} from '../types';
|
|
|
|
const { add, multiply } = Animated;
|
|
|
|
/**
|
|
* Standard iOS-style slide in from the right.
|
|
*/
|
|
export function forHorizontalIOS({
|
|
current,
|
|
next,
|
|
inverted,
|
|
layouts: { screen },
|
|
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
|
const translateFocused = multiply(
|
|
current.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [screen.width, 0],
|
|
extrapolate: 'clamp',
|
|
}),
|
|
inverted
|
|
);
|
|
|
|
const translateUnfocused = next
|
|
? multiply(
|
|
next.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [0, screen.width * -0.3],
|
|
extrapolate: 'clamp',
|
|
}),
|
|
inverted
|
|
)
|
|
: 0;
|
|
|
|
const overlayOpacity = current.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [0, 0.07],
|
|
extrapolate: 'clamp',
|
|
});
|
|
|
|
const shadowOpacity = current.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [0, 0.3],
|
|
extrapolate: 'clamp',
|
|
});
|
|
|
|
return {
|
|
cardStyle: {
|
|
transform: [
|
|
// Translation for the animation of the current card
|
|
{ translateX: translateFocused },
|
|
// Translation for the animation of the card on top of this
|
|
{ translateX: translateUnfocused },
|
|
],
|
|
},
|
|
overlayStyle: { opacity: overlayOpacity },
|
|
shadowStyle: { shadowOpacity },
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Standard iOS-style slide in from the bottom (used for modals).
|
|
*/
|
|
export function forVerticalIOS({
|
|
current,
|
|
inverted,
|
|
layouts: { screen },
|
|
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
|
const translateY = multiply(
|
|
current.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [screen.height, 0],
|
|
extrapolate: 'clamp',
|
|
}),
|
|
inverted
|
|
);
|
|
|
|
return {
|
|
cardStyle: {
|
|
transform: [
|
|
// Translation for the animation of the current card
|
|
{ translateY },
|
|
],
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Standard iOS-style modal animation in iOS 13.
|
|
*/
|
|
export function forModalPresentationIOS({
|
|
index,
|
|
current,
|
|
next,
|
|
inverted,
|
|
layouts: { screen },
|
|
insets,
|
|
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
|
const isLandscape = screen.width > screen.height;
|
|
const topOffset = isLandscape ? 0 : 10;
|
|
const statusBarHeight = insets.top;
|
|
const aspectRatio = screen.height / screen.width;
|
|
|
|
const progress = add(
|
|
current.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [0, 1],
|
|
extrapolate: 'clamp',
|
|
}),
|
|
next
|
|
? next.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [0, 1],
|
|
extrapolate: 'clamp',
|
|
})
|
|
: 0
|
|
);
|
|
|
|
const translateY = multiply(
|
|
progress.interpolate({
|
|
inputRange: [0, 1, 2],
|
|
outputRange: [
|
|
screen.height,
|
|
index === 0 ? 0 : topOffset,
|
|
(index === 0 ? statusBarHeight : 0) - topOffset * aspectRatio,
|
|
],
|
|
}),
|
|
inverted
|
|
);
|
|
|
|
const overlayOpacity = progress.interpolate({
|
|
inputRange: [0, 1, 1.0001, 2],
|
|
outputRange: [0, 0.3, 1, 1],
|
|
});
|
|
|
|
const scale = isLandscape
|
|
? 1
|
|
: progress.interpolate({
|
|
inputRange: [0, 1, 2],
|
|
outputRange: [
|
|
1,
|
|
1,
|
|
screen.width ? 1 - (topOffset * 2) / screen.width : 1,
|
|
],
|
|
});
|
|
|
|
const borderRadius = isLandscape
|
|
? 0
|
|
: index === 0
|
|
? progress.interpolate({
|
|
inputRange: [0, 1, 2],
|
|
outputRange: [0, 0, 10],
|
|
})
|
|
: 10;
|
|
|
|
return {
|
|
cardStyle: {
|
|
overflow: 'hidden',
|
|
borderTopLeftRadius: borderRadius,
|
|
borderTopRightRadius: borderRadius,
|
|
marginTop: index === 0 ? 0 : statusBarHeight,
|
|
transform: [{ translateY }, { scale }],
|
|
},
|
|
overlayStyle: { opacity: overlayOpacity },
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Standard Android-style fade in from the bottom for Android Oreo.
|
|
*/
|
|
export function forFadeFromBottomAndroid({
|
|
current,
|
|
inverted,
|
|
layouts: { screen },
|
|
closing,
|
|
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
|
const translateY = multiply(
|
|
current.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [screen.height * 0.08, 0],
|
|
extrapolate: 'clamp',
|
|
}),
|
|
inverted
|
|
);
|
|
|
|
const opacity = conditional(
|
|
closing,
|
|
current.progress,
|
|
current.progress.interpolate({
|
|
inputRange: [0, 0.5, 0.9, 1],
|
|
outputRange: [0, 0.25, 0.7, 1],
|
|
})
|
|
);
|
|
|
|
return {
|
|
cardStyle: {
|
|
opacity,
|
|
transform: [{ translateY }],
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Standard Android-style reveal from the bottom for Android Pie.
|
|
*/
|
|
export function forRevealFromBottomAndroid({
|
|
current,
|
|
next,
|
|
inverted,
|
|
layouts: { screen },
|
|
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
|
const containerTranslateY = multiply(
|
|
current.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [screen.height, 0],
|
|
extrapolate: 'clamp',
|
|
}),
|
|
inverted
|
|
);
|
|
|
|
const cardTranslateYFocused = multiply(
|
|
current.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [screen.height * (95.9 / 100) * -1, 0],
|
|
extrapolate: 'clamp',
|
|
}),
|
|
inverted
|
|
);
|
|
|
|
const cardTranslateYUnfocused = next
|
|
? multiply(
|
|
next.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [0, screen.height * (2 / 100) * -1],
|
|
extrapolate: 'clamp',
|
|
}),
|
|
inverted
|
|
)
|
|
: 0;
|
|
|
|
const overlayOpacity = current.progress.interpolate({
|
|
inputRange: [0, 0.36, 1],
|
|
outputRange: [0, 0.1, 0.1],
|
|
extrapolate: 'clamp',
|
|
});
|
|
|
|
return {
|
|
containerStyle: {
|
|
overflow: 'hidden',
|
|
transform: [{ translateY: containerTranslateY }],
|
|
},
|
|
cardStyle: {
|
|
transform: [
|
|
{ translateY: cardTranslateYFocused },
|
|
{ translateY: cardTranslateYUnfocused },
|
|
],
|
|
},
|
|
overlayStyle: { opacity: overlayOpacity },
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Standard Android-style reveal from the bottom for Android Q.
|
|
*/
|
|
export function forScaleFromCenterAndroid({
|
|
current,
|
|
next,
|
|
closing,
|
|
}: StackCardInterpolationProps): StackCardInterpolatedStyle {
|
|
const progress = add(
|
|
current.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [0, 1],
|
|
extrapolate: 'clamp',
|
|
}),
|
|
next
|
|
? next.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [0, 1],
|
|
extrapolate: 'clamp',
|
|
})
|
|
: 0
|
|
);
|
|
|
|
const opacity = progress.interpolate({
|
|
inputRange: [0, 0.8, 1, 1.2, 2],
|
|
outputRange: [0, 0.5, 1, 0.33, 0],
|
|
});
|
|
|
|
const scale = conditional(
|
|
closing,
|
|
current.progress.interpolate({
|
|
inputRange: [0, 1],
|
|
outputRange: [0.9, 1],
|
|
extrapolate: 'clamp',
|
|
}),
|
|
progress.interpolate({
|
|
inputRange: [0, 1, 2],
|
|
outputRange: [0.85, 1, 1.1],
|
|
})
|
|
);
|
|
|
|
return {
|
|
containerStyle: {
|
|
opacity,
|
|
transform: [{ scale }],
|
|
},
|
|
};
|
|
}
|
|
|
|
export function forNoAnimation(): StackCardInterpolatedStyle {
|
|
return {};
|
|
}
|