This repository has been archived by the owner on Feb 8, 2020. It is now read-only.
generated from satya164/typescript-template
-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new implementation with reanimated
- Loading branch information
Showing
33 changed files
with
2,592 additions
and
0 deletions.
There are no files selected for viewing
140 changes: 140 additions & 0 deletions
140
packages/stack/src/TransitionConfigs/CardStyleInterpolators.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import Animated from 'react-native-reanimated'; | ||
import { CardInterpolationProps, CardInterpolatedStyle } from '../types'; | ||
|
||
const { cond, multiply, interpolate } = Animated; | ||
|
||
/** | ||
* Standard iOS-style slide in from the right. | ||
*/ | ||
export function forHorizontalIOS({ | ||
progress: { current, next }, | ||
layouts: { screen }, | ||
}: CardInterpolationProps): CardInterpolatedStyle { | ||
const translateFocused = interpolate(current, { | ||
inputRange: [0, 1], | ||
outputRange: [screen.width, 0], | ||
}); | ||
const translateUnfocused = next | ||
? interpolate(next, { | ||
inputRange: [0, 1], | ||
outputRange: [0, multiply(screen.width, -0.3)], | ||
}) | ||
: 0; | ||
|
||
const opacity = interpolate(current, { | ||
inputRange: [0, 1], | ||
outputRange: [0, 0.07], | ||
}); | ||
|
||
const shadowOpacity = interpolate(current, { | ||
inputRange: [0, 1], | ||
outputRange: [0, 0.3], | ||
}); | ||
|
||
return { | ||
cardStyle: { | ||
backgroundColor: '#eee', | ||
transform: [ | ||
// Translation for the animation of the current card | ||
{ translateX: translateFocused }, | ||
// Translation for the animation of the card on top of this | ||
{ translateX: translateUnfocused }, | ||
], | ||
shadowOpacity, | ||
}, | ||
overlayStyle: { opacity }, | ||
}; | ||
} | ||
|
||
/** | ||
* Standard iOS-style slide in from the bottom (used for modals). | ||
*/ | ||
export function forVerticalIOS({ | ||
progress: { current }, | ||
layouts: { screen }, | ||
}: CardInterpolationProps): CardInterpolatedStyle { | ||
const translateY = interpolate(current, { | ||
inputRange: [0, 1], | ||
outputRange: [screen.height, 0], | ||
}); | ||
|
||
return { | ||
cardStyle: { | ||
backgroundColor: '#eee', | ||
transform: [ | ||
// Translation for the animation of the current card | ||
{ translateY }, | ||
], | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* Standard Android-style fade in from the bottom for Android Oreo. | ||
*/ | ||
export function forFadeFromBottomAndroid({ | ||
progress: { current }, | ||
layouts: { screen }, | ||
closing, | ||
}: CardInterpolationProps): CardInterpolatedStyle { | ||
const translateY = interpolate(current, { | ||
inputRange: [0, 1], | ||
outputRange: [multiply(screen.height, 0.08), 0], | ||
}); | ||
|
||
const opacity = cond( | ||
closing, | ||
current, | ||
interpolate(current, { | ||
inputRange: [0, 0.5, 0.9, 1], | ||
outputRange: [0, 0.25, 0.7, 1], | ||
}) | ||
); | ||
|
||
return { | ||
cardStyle: { | ||
opacity, | ||
transform: [{ translateY }], | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* Standard Android-style wipe from the bottom for Android Pie. | ||
*/ | ||
export function forWipeFromBottomAndroid({ | ||
progress: { current, next }, | ||
layouts: { screen }, | ||
}: CardInterpolationProps): CardInterpolatedStyle { | ||
const containerTranslateY = interpolate(current, { | ||
inputRange: [0, 1], | ||
outputRange: [screen.height, 0], | ||
}); | ||
const cardTranslateYFocused = interpolate(current, { | ||
inputRange: [0, 1], | ||
outputRange: [multiply(screen.height, 95.9 / 100, -1), 0], | ||
}); | ||
const cardTranslateYUnfocused = next | ||
? interpolate(next, { | ||
inputRange: [0, 1], | ||
outputRange: [0, multiply(screen.height, 2 / 100, -1)], | ||
}) | ||
: 0; | ||
const overlayOpacity = interpolate(current, { | ||
inputRange: [0, 0.36, 1], | ||
outputRange: [0, 0.1, 0.1], | ||
}); | ||
|
||
return { | ||
containerStyle: { | ||
transform: [{ translateY: containerTranslateY }], | ||
}, | ||
cardStyle: { | ||
transform: [ | ||
{ translateY: cardTranslateYFocused }, | ||
{ translateY: cardTranslateYUnfocused }, | ||
], | ||
}, | ||
overlayStyle: { opacity: overlayOpacity }, | ||
}; | ||
} |
100 changes: 100 additions & 0 deletions
100
packages/stack/src/TransitionConfigs/HeaderStyleInterpolators.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import Animated from 'react-native-reanimated'; | ||
import { HeaderInterpolationProps, HeaderInterpolatedStyle } from '../types'; | ||
|
||
const { interpolate, add } = Animated; | ||
|
||
export function forUIKit({ | ||
progress: { current, next }, | ||
layouts, | ||
}: HeaderInterpolationProps): HeaderInterpolatedStyle { | ||
const defaultOffset = 100; | ||
const leftSpacing = 27; | ||
|
||
// The title and back button title should cross-fade to each other | ||
// When screen is fully open, the title should be in center, and back title should be on left | ||
// When screen is closing, the previous title will animate to back title's position | ||
// And back title will animate to title's position | ||
// We achieve this by calculating the offsets needed to translate title to back title's position and vice-versa | ||
const leftLabelOffset = layouts.leftLabel | ||
? (layouts.screen.width - layouts.leftLabel.width) / 2 - leftSpacing | ||
: defaultOffset; | ||
const titleLeftOffset = layouts.title | ||
? (layouts.screen.width - layouts.title.width) / 2 - leftSpacing | ||
: defaultOffset; | ||
|
||
// When the current title is animating to right, it is centered in the right half of screen in middle of transition | ||
// The back title also animates in from this position | ||
const rightOffset = layouts.screen.width / 4; | ||
|
||
const progress = add(current, next ? next : 0); | ||
|
||
return { | ||
leftButtonStyle: { | ||
opacity: interpolate(progress, { | ||
inputRange: [0.3, 1, 1.5], | ||
outputRange: [0, 1, 0], | ||
}), | ||
}, | ||
leftLabelStyle: { | ||
transform: [ | ||
{ | ||
translateX: interpolate(progress, { | ||
inputRange: [0, 1, 2], | ||
outputRange: [leftLabelOffset, 0, -rightOffset], | ||
}), | ||
}, | ||
], | ||
}, | ||
rightButtonStyle: { | ||
opacity: interpolate(progress, { | ||
inputRange: [0.3, 1, 1.5], | ||
outputRange: [0, 1, 0], | ||
}), | ||
}, | ||
titleStyle: { | ||
opacity: interpolate(progress, { | ||
inputRange: [0, 0.4, 1, 1.5], | ||
outputRange: [0, 0.1, 1, 0], | ||
}), | ||
transform: [ | ||
{ | ||
translateX: interpolate(progress, { | ||
inputRange: [0.5, 1, 2], | ||
outputRange: [rightOffset, 0, -titleLeftOffset], | ||
}), | ||
}, | ||
], | ||
}, | ||
backgroundStyle: { | ||
transform: [ | ||
{ | ||
translateX: interpolate(progress, { | ||
inputRange: [0, 1, 2], | ||
outputRange: [layouts.screen.width, 0, -layouts.screen.width], | ||
}), | ||
}, | ||
], | ||
}, | ||
}; | ||
} | ||
|
||
export function forFade({ | ||
progress: { current, next }, | ||
}: HeaderInterpolationProps): HeaderInterpolatedStyle { | ||
const progress = add(current, next ? next : 0); | ||
const opacity = interpolate(progress, { | ||
inputRange: [0, 1, 2], | ||
outputRange: [0, 1, 0], | ||
}); | ||
|
||
return { | ||
leftButtonStyle: { opacity }, | ||
rightButtonStyle: { opacity }, | ||
titleStyle: { opacity }, | ||
backgroundStyle: { opacity }, | ||
}; | ||
} | ||
|
||
export function forNoAnimation(): HeaderInterpolatedStyle { | ||
return {}; | ||
} |
69 changes: 69 additions & 0 deletions
69
packages/stack/src/TransitionConfigs/TransitionPresets.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { | ||
forHorizontalIOS, | ||
forVerticalIOS, | ||
forWipeFromBottomAndroid, | ||
forFadeFromBottomAndroid, | ||
} from './CardStyleInterpolators'; | ||
import { forUIKit, forNoAnimation } from './HeaderStyleInterpolators'; | ||
import { | ||
TransitionIOSSpec, | ||
WipeFromBottomAndroidSpec, | ||
FadeOutToBottomAndroidSpec, | ||
FadeInFromBottomAndroidSpec, | ||
} from './TransitionSpecs'; | ||
import { TransitionPreset } from '../types'; | ||
import { Platform } from 'react-native'; | ||
|
||
const ANDROID_VERSION_PIE = 28; | ||
|
||
// Standard iOS navigation transition | ||
export const SlideFromRightIOS: TransitionPreset = { | ||
direction: 'horizontal', | ||
transitionSpec: { | ||
open: TransitionIOSSpec, | ||
close: TransitionIOSSpec, | ||
}, | ||
cardStyleInterpolator: forHorizontalIOS, | ||
headerStyleInterpolator: forUIKit, | ||
}; | ||
|
||
// Standard iOS navigation transition for modals | ||
export const ModalSlideFromBottomIOS: TransitionPreset = { | ||
direction: 'vertical', | ||
transitionSpec: { | ||
open: TransitionIOSSpec, | ||
close: TransitionIOSSpec, | ||
}, | ||
cardStyleInterpolator: forVerticalIOS, | ||
headerStyleInterpolator: forNoAnimation, | ||
}; | ||
|
||
// Standard Android navigation transition when opening or closing an Activity on Android < 9 | ||
export const FadeFromBottomAndroid: TransitionPreset = { | ||
direction: 'vertical', | ||
transitionSpec: { | ||
open: FadeInFromBottomAndroidSpec, | ||
close: FadeOutToBottomAndroidSpec, | ||
}, | ||
cardStyleInterpolator: forFadeFromBottomAndroid, | ||
headerStyleInterpolator: forNoAnimation, | ||
}; | ||
|
||
// Standard Android navigation transition when opening or closing an Activity on Android >= 9 | ||
export const WipeFromBottomAndroid: TransitionPreset = { | ||
direction: 'vertical', | ||
transitionSpec: { | ||
open: WipeFromBottomAndroidSpec, | ||
close: WipeFromBottomAndroidSpec, | ||
}, | ||
cardStyleInterpolator: forWipeFromBottomAndroid, | ||
headerStyleInterpolator: forNoAnimation, | ||
}; | ||
|
||
export const DefaultTransition = Platform.select({ | ||
ios: SlideFromRightIOS, | ||
default: | ||
Platform.OS === 'android' && Platform.Version < ANDROID_VERSION_PIE | ||
? FadeFromBottomAndroid | ||
: WipeFromBottomAndroid, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { Easing } from 'react-native-reanimated'; | ||
import { TransitionSpec } from '../types'; | ||
|
||
export const TransitionIOSSpec: TransitionSpec = { | ||
timing: 'spring', | ||
config: { | ||
stiffness: 1000, | ||
damping: 500, | ||
mass: 3, | ||
overshootClamping: true, | ||
restDisplacementThreshold: 0.01, | ||
restSpeedThreshold: 0.01, | ||
}, | ||
}; | ||
|
||
// See http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/res/res/anim/activity_open_enter.xml | ||
export const FadeInFromBottomAndroidSpec: TransitionSpec = { | ||
timing: 'timing', | ||
config: { | ||
duration: 350, | ||
easing: Easing.out(Easing.poly(5)), | ||
}, | ||
}; | ||
|
||
// See http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/res/res/anim/activity_close_exit.xml | ||
export const FadeOutToBottomAndroidSpec: TransitionSpec = { | ||
timing: 'timing', | ||
config: { | ||
duration: 150, | ||
easing: Easing.in(Easing.linear), | ||
}, | ||
}; | ||
|
||
// See http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/res/res/anim/activity_open_enter.xml | ||
export const WipeFromBottomAndroidSpec: TransitionSpec = { | ||
timing: 'timing', | ||
config: { | ||
duration: 425, | ||
// This is super rough approximation of the path used for the curve by android | ||
// See http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/res/res/interpolator/fast_out_extra_slow_in.xml | ||
easing: Easing.bezier(0.35, 0.45, 0, 1), | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import * as CardStyleInterpolators from './TransitionConfigs/CardStyleInterpolators'; | ||
import * as HeaderStyleInterpolators from './TransitionConfigs/HeaderStyleInterpolators'; | ||
import * as TransitionPresets from './TransitionConfigs/TransitionPresets'; | ||
|
||
/** | ||
* Navigators | ||
*/ | ||
export { | ||
default as createStackNavigator, | ||
} from './navigators/createStackNavigator'; | ||
|
||
export const Assets = [ | ||
require('./views/assets/back-icon.png'), | ||
require('./views/assets/back-icon-mask.png'), | ||
]; | ||
|
||
/** | ||
* Views | ||
*/ | ||
export { default as Header } from './views/Header/Header'; | ||
export { default as HeaderTitle } from './views/Header/HeaderTitle'; | ||
export { default as HeaderBackButton } from './views/Header/HeaderBackButton'; | ||
|
||
/** | ||
* Transition presets | ||
*/ | ||
export { CardStyleInterpolators, HeaderStyleInterpolators, TransitionPresets }; | ||
|
||
/** | ||
* Utilities | ||
*/ | ||
|
||
export { default as StackGestureContext } from './utils/StackGestureContext'; |
Oops, something went wrong.