react-native#I18nManager TypeScript Examples
The following examples show how to use
react-native#I18nManager.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts From react-native-range-slider-expo with MIT License | 6 votes |
osRtl = I18nManager.isRTL
Example #2
Source File: DatePicker.tsx From react-native-jigsaw with MIT License | 6 votes |
styles = StyleSheet.create({
container: {
alignSelf: "stretch",
},
picker: {
position: "absolute",
bottom: 0,
left: 0,
right: 0,
flexDirection: "row",
justifyContent: "center",
},
underline: {
position: "absolute",
left: 0,
right: 0,
bottom: 0,
height: 2,
},
input: {
flexGrow: 1,
justifyContent: "center",
textAlignVertical: "center",
margin: 0,
textAlign: I18nManager.isRTL ? "right" : "left",
},
placeholder: {
position: "absolute",
left: 0,
},
pickerContainer: { flexDirection: "column", width: "100%", zIndex: 100 },
closeButton: {
alignSelf: "flex-end",
},
})
Example #3
Source File: Text.tsx From react-native-jigsaw with MIT License | 6 votes |
render() {
const { style, ...rest } = this.props;
const writingDirection = I18nManager.isRTL ? "rtl" : "ltr";
return (
<NativeText
{...rest}
ref={(c) => {
this._root = c;
}}
style={[
{
textAlign: "left",
writingDirection,
},
style,
]}
/>
);
}
Example #4
Source File: TextField.tsx From react-native-jigsaw with MIT License | 6 votes |
styles = StyleSheet.create({
container: {
alignSelf: "stretch",
},
placeholder: {
position: "absolute",
left: 0,
},
underline: {
position: "absolute",
left: 0,
right: 0,
bottom: 0,
height: 2,
},
input: {
flexGrow: 1,
justifyContent: "center",
textAlignVertical: "center",
margin: 0,
textAlign: I18nManager.isRTL ? "right" : "left",
},
})
Example #5
Source File: i18n.ts From nyxo-app with GNU General Public License v3.0 | 6 votes |
setI18nConfig = (): void => {
// fallback if no available language fits
const fallback = { languageTag: 'en', isRTL: false }
const { languageTag, isRTL } =
findBestAvailableLanguage(Object.keys(translationGetters)) || fallback
// clear translation cache
if (translate?.cache.clear) translate.cache.clear()
// update layout direction
I18nManager.forceRTL(isRTL)
// set i18n-js config
I18n.translations = { [languageTag]: translationGetters[languageTag]() }
I18n.locale = languageTag
}
Example #6
Source File: HeaderStyleInterpolators.tsx From nlw2-proffy with MIT License | 6 votes |
/**
* Simple translate animation to translate the header to left.
*/
export function forSlideLeft({
current,
next,
layouts: { screen },
}: StackHeaderInterpolationProps): StackHeaderInterpolatedStyle {
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 translateX = progress.interpolate({
inputRange: [0, 1, 2],
outputRange: I18nManager.isRTL
? [-screen.width, 0, screen.width]
: [screen.width, 0, -screen.width],
});
const transform = [{ translateX }];
return {
leftButtonStyle: { transform },
rightButtonStyle: { transform },
titleStyle: { transform },
backgroundStyle: { transform },
};
}
Example #7
Source File: HeaderStyleInterpolators.tsx From nlw2-proffy with MIT License | 6 votes |
/**
* Simple translate animation to translate the header to right.
*/
export function forSlideRight({
current,
next,
layouts: { screen },
}: StackHeaderInterpolationProps): StackHeaderInterpolatedStyle {
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 translateX = progress.interpolate({
inputRange: [0, 1, 2],
outputRange: I18nManager.isRTL
? [screen.width, 0, -screen.width]
: [-screen.width, 0, screen.width],
});
const transform = [{ translateX }];
return {
leftButtonStyle: { transform },
rightButtonStyle: { transform },
titleStyle: { transform },
backgroundStyle: { transform },
};
}
Example #8
Source File: getInvertedMultiplier.tsx From nlw2-proffy with MIT License | 6 votes |
export default function getInvertedMultiplier(
gestureDirection: GestureDirection
): 1 | -1 {
switch (gestureDirection) {
case 'vertical':
return 1;
case 'vertical-inverted':
return -1;
case 'horizontal':
return I18nManager.isRTL ? -1 : 1;
case 'horizontal-inverted':
return I18nManager.isRTL ? 1 : -1;
}
}
Example #9
Source File: App.tsx From sellflow with MIT License | 5 votes |
I18nManager.forceRTL(CustomTheme.isRTL);
Example #10
Source File: Slider.tsx From react-native-range-slider-expo with MIT License | 5 votes |
osRtl = I18nManager.isRTL
Example #11
Source File: TextSlider.tsx From react-native-range-slider-expo with MIT License | 5 votes |
osRtl = I18nManager.isRTL
Example #12
Source File: ProgressBar.tsx From react-native-jigsaw with MIT License | 5 votes |
render() {
const {
borderColor,
borderRadius = 4,
borderWidth = 1,
children,
color = "rgba(0, 122, 255, 1)",
style,
unfilledColor,
width = 150,
...restProps
} = this.props;
const innerWidth = Math.max(0, width || this.state.width) - borderWidth * 2;
const containerStyle: StyleProp<ViewStyle> = {
width,
borderWidth,
borderColor: borderColor || color,
borderRadius,
overflow: "hidden",
backgroundColor: unfilledColor,
};
const progressStyle = {
backgroundColor: color,
// Always take up full height of container.
height: "100%",
transform: [
{
translateX: this.state.animationValue.interpolate({
inputRange: [0, 1],
outputRange: [innerWidth * -INDETERMINATE_WIDTH_FACTOR, innerWidth],
}),
},
{
translateX: this.state.progress.interpolate({
inputRange: [0, 1],
outputRange: [innerWidth / (I18nManager.isRTL ? 2 : -2), 0],
}),
},
{
// Interpolation a temp workaround for https://github.com/facebook/react-native/issues/6278
scaleX: this.state.progress.interpolate({
inputRange: [0, 1],
outputRange: [0.0001, 1],
}),
},
],
};
return (
<View
style={[containerStyle, style]}
onLayout={this.handleLayout}
{...restProps}
>
<Animated.View style={progressStyle} />
{children}
</View>
);
}
Example #13
Source File: HeaderBackButton.tsx From nlw2-proffy with MIT License | 5 votes |
styles = StyleSheet.create({
container: {
alignItems: 'center',
flexDirection: 'row',
...Platform.select({
ios: null,
default: {
marginVertical: 3,
marginHorizontal: 11,
},
}),
},
disabled: {
opacity: 0.5,
},
label: {
fontSize: 17,
// Title and back label are a bit different width due to title being bold
// Adjusting the letterSpacing makes them coincide better
letterSpacing: 0.35,
},
labelWrapper: {
// These styles will make sure that the label doesn't fill the available space
// Otherwise it messes with the measurement of the label
flexDirection: 'row',
alignItems: 'flex-start',
},
icon: Platform.select({
ios: {
height: 21,
width: 13,
marginLeft: 8,
marginRight: 22,
marginVertical: 12,
resizeMode: 'contain',
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
},
default: {
height: 24,
width: 24,
margin: 3,
resizeMode: 'contain',
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
},
}),
iconWithLabel:
Platform.OS === 'ios'
? {
marginRight: 6,
}
: {},
iconMaskContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
},
iconMaskFillerRect: {
flex: 1,
backgroundColor: '#000',
},
iconMask: {
height: 21,
width: 13,
marginLeft: -14.5,
marginVertical: 12,
alignSelf: 'center',
resizeMode: 'contain',
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
},
})
Example #14
Source File: BackgroundCircle.tsx From react-native-paper-onboarding with MIT License | 5 votes |
BackgroundCircleComponent = ({
index,
animatedIndex,
color,
extendedSize,
bottomPosition,
screenDimensions,
indicatorSize,
animatedIndicatorsContainerPosition,
}: BackgroundCircleProps) => {
//#region variables
//#endregion
//#region animations
const animatedFocus = useMemo(
() =>
interpolate(animatedIndex, {
inputRange: [index - 1, index, index + 1],
outputRange: [0, 1, 2],
extrapolate: Extrapolate.CLAMP,
}),
[animatedIndex, index]
);
const animatedRadius = useMemo(
() =>
interpolate(animatedFocus, {
inputRange: [0, 1],
outputRange: [0, extendedSize],
extrapolate: Extrapolate.CLAMP,
}),
[animatedFocus, extendedSize]
);
const animatedLeftPosition = useMemo(
() =>
add(
animatedIndicatorsContainerPosition,
indicatorSize / 2,
I18nManager.isRTL
? -((index + 1) * indicatorSize)
: index * indicatorSize,
I18nManager.isRTL ? screenDimensions.width : 0
),
[
animatedIndicatorsContainerPosition,
index,
indicatorSize,
screenDimensions.width,
]
);
//#endregion
// render
return (
<AnimatedCircle
r={animatedRadius}
cy={bottomPosition}
cx={animatedLeftPosition}
fill={color}
/>
);
}
Example #15
Source File: HeaderStyleInterpolators.tsx From nlw2-proffy with MIT License | 4 votes |
/**
* Standard UIKit style animation for the header where the title fades into the back button label.
*/
export function forUIKit({
current,
next,
layouts,
}: StackHeaderInterpolationProps): StackHeaderInterpolatedStyle {
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.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'clamp',
}),
next
? next.progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolate: 'clamp',
})
: 0
);
return {
leftButtonStyle: {
opacity: progress.interpolate({
inputRange: [0.3, 1, 1.5],
outputRange: [0, 1, 0],
}),
},
leftLabelStyle: {
transform: [
{
translateX: progress.interpolate({
inputRange: [0, 1, 2],
outputRange: I18nManager.isRTL
? [-rightOffset, 0, leftLabelOffset]
: [leftLabelOffset, 0, -rightOffset],
}),
},
],
},
rightButtonStyle: {
opacity: progress.interpolate({
inputRange: [0.3, 1, 1.5],
outputRange: [0, 1, 0],
}),
},
titleStyle: {
opacity: progress.interpolate({
inputRange: [0, 0.4, 1, 1.5],
outputRange: [0, 0.1, 1, 0],
}),
transform: [
{
translateX: progress.interpolate({
inputRange: [0.5, 1, 2],
outputRange: I18nManager.isRTL
? [-titleLeftOffset, 0, rightOffset]
: [rightOffset, 0, -titleLeftOffset],
}),
},
],
},
backgroundStyle: {
transform: [
{
translateX: progress.interpolate({
inputRange: [0, 1, 2],
outputRange: I18nManager.isRTL
? [-layouts.screen.width, 0, layouts.screen.width]
: [layouts.screen.width, 0, -layouts.screen.width],
}),
},
],
},
};
}
Example #16
Source File: useTiming.ts From react-native-paper-onboarding with MIT License | 4 votes |
useTiming = ({
animatedStaticIndex,
animatedOverrideIndex,
value,
velocity,
state,
size,
screenWidth,
}: useTimingProps) => {
const clock = useClock();
const isManuallyAnimated = useValue(0);
const config = useMemo(
() => ({
toValue: new Animated.Value(0),
duration: 500,
easing: Easing.out(Easing.exp),
}),
[]
);
const animationState = useMemo(
() => ({
finished: new Animated.Value(0),
position: new Animated.Value(0),
frameTime: new Animated.Value(0),
time: new Animated.Value(0),
}),
[]
);
const valueClamp = useMemo(
() =>
interpolate(value, {
inputRange: [screenWidth * -1, 0, screenWidth],
outputRange: I18nManager.isRTL ? [-1, 0, 1] : [1, 0, -1],
extrapolate: Animated.Extrapolate.CLAMP,
}),
[value, screenWidth]
);
const velocityClamp = useMemo(
() =>
interpolate(velocity, {
inputRange: [screenWidth * -2, 0, screenWidth * 2],
outputRange: I18nManager.isRTL ? [-0.5, 0, 0.5] : [0.5, 0, -0.5],
extrapolate: Animated.Extrapolate.CLAMP,
}),
[screenWidth, velocity]
);
const isTimingInterrupted = and(eq(state, State.BEGAN), clockRunning(clock));
const finishTiming = useMemo(
() => [
set(animatedStaticIndex, config.toValue),
set(animatedOverrideIndex, config.toValue),
set(animationState.frameTime, 0),
set(animationState.time, 0),
set(state, State.UNDETERMINED),
set(isManuallyAnimated, 0),
stopClock(clock),
],
[
state,
animatedOverrideIndex,
animatedStaticIndex,
animationState.frameTime,
animationState.time,
clock,
config.toValue,
isManuallyAnimated,
]
);
const shouldAnimate = useMemo(
() =>
and(
not(and(eq(animatedStaticIndex, 0), lessThan(valueClamp, 0))),
not(and(eq(animatedStaticIndex, size - 1), greaterThan(valueClamp, 0)))
),
[animatedStaticIndex, size, valueClamp]
);
const shouldReset = useMemo(
() => not(greaterThan(add(abs(valueClamp), abs(velocityClamp)), 0.5)),
[valueClamp, velocityClamp]
);
const shouldAnimateNext = useMemo(
() =>
greaterThan(
add(animationState.position, velocityClamp),
animatedStaticIndex
),
[animatedStaticIndex, animationState.position, velocityClamp]
);
const animatedPosition = useMemo(
() =>
block([
cond(isTimingInterrupted, finishTiming),
cond(
eq(state, State.ACTIVE),
cond(
and(
not(and(eq(animatedStaticIndex, 0), lessThan(valueClamp, 0))),
not(
and(
eq(animatedStaticIndex, size - 1),
greaterThan(valueClamp, 0)
)
)
),
[
set(animationState.finished, 0),
set(
animationState.position,
add(animatedStaticIndex, valueClamp)
),
]
)
),
onChange(animatedOverrideIndex, [
set(isManuallyAnimated, 1),
set(animationState.finished, 0),
]),
cond(or(eq(state, State.END), isManuallyAnimated), [
cond(and(not(clockRunning(clock)), not(animationState.finished)), [
cond(
isManuallyAnimated,
set(config.toValue, animatedOverrideIndex),
cond(
or(shouldReset, not(shouldAnimate)),
set(config.toValue, animatedStaticIndex),
cond(
shouldAnimateNext,
set(config.toValue, add(animatedStaticIndex, 1)),
set(config.toValue, sub(animatedStaticIndex, 1))
)
)
),
set(animationState.finished, 0),
set(animationState.frameTime, 0),
set(animationState.time, 0),
startClock(clock),
]),
timing(clock, animationState, config),
cond(animationState.finished, finishTiming),
]),
animationState.position,
]),
[
size,
state,
animatedOverrideIndex,
animatedStaticIndex,
animationState,
clock,
config,
finishTiming,
isManuallyAnimated,
isTimingInterrupted,
shouldAnimate,
shouldAnimateNext,
shouldReset,
valueClamp,
]
);
return animatedPosition;
}
Example #17
Source File: PaperOnboarding.tsx From react-native-paper-onboarding with MIT License | 4 votes |
PaperOnboardingComponent = forwardRef<
PaperOnboarding,
PaperOnboardingProps
>(
(
{
data,
safeInsets: _safeInsets,
direction = DEFAULT_DIRECTION,
// indicator config
indicatorSize = DEFAULT_INDICATOR_SIZE,
indicatorBackgroundColor = DEFAULT_INDICATOR_BACKGROUND_COLOR,
indicatorBorderColor = DEFAULT_INDICATOR_BORDER_COLOR,
// override styles
titleStyle,
descriptionStyle,
// close button config
closeButton,
closeButtonTextStyle,
closeButtonText = DEFAULT_CLOSE_BUTTON_TEXT,
onCloseButtonPress = DEFAULT_CLOSE_BUTTON_CALLBACK,
onIndexChange,
},
ref
) => {
// state
const [dimensions, setDimensions] =
useState<PaperOnboardingScreenDimensions>({
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
});
// refs
const indexRef = useRef<number>(0);
const pagesRef = useRef<Array<Animated.View | null>>(data.map(() => null));
//#region variables
const safeInsets = useMemo<Required<Insets>>(() => {
return {
top: _safeInsets?.top ?? DEFAULT_SAFE_INSET,
bottom: _safeInsets?.bottom ?? DEFAULT_SAFE_INSET,
left: _safeInsets?.left ?? DEFAULT_SAFE_INSET,
right: _safeInsets?.right ?? DEFAULT_SAFE_INSET,
};
}, [_safeInsets]);
const indicatorsContainerLeftPadding = useMemo(() => {
const containerLeftPadding = dimensions.width / 2 - indicatorSize / 2;
return I18nManager.isRTL
? -containerLeftPadding + indicatorSize * (data.length - 1)
: containerLeftPadding;
}, [dimensions.width, indicatorSize, data.length]);
//#endregion
//#region animated variables
const { gestureHandler, state, translation, velocity } =
usePanGestureHandler();
const animatedStaticIndex = useValue<number>(0);
const animatedOverrideIndex = useValue<number>(0);
const animatedIndex = useTiming({
animatedStaticIndex,
animatedOverrideIndex,
value: direction === 'horizontal' ? translation.x : translation.y,
velocity: direction === 'horizontal' ? velocity.x : velocity.y,
state: state,
size: data.length,
screenWidth: dimensions.width,
});
const indicatorsContainerPosition = useMemo(
() => data.map((_, index) => index * indicatorSize * -1),
[data, indicatorSize]
);
const animatedIndicatorsContainerPosition = useMemo(
() =>
add(
interpolate(animatedIndex, {
inputRange: data.map((_, index) => index),
outputRange: I18nManager.isRTL
? indicatorsContainerPosition.reverse()
: indicatorsContainerPosition,
extrapolate: Animated.Extrapolate.CLAMP,
}),
indicatorsContainerLeftPadding
),
[
data,
animatedIndex,
indicatorsContainerLeftPadding,
indicatorsContainerPosition,
]
);
//#endregion
//#region callbacks
const handlePageRef = useCallback((pageRef, index) => {
pagesRef.current[index] = pageRef;
}, []);
const handleOnLayout = useCallback(
({
nativeEvent: {
layout: { width, height },
},
}: LayoutChangeEvent) => {
setDimensions({
width,
height,
});
},
[]
);
//#endregion
//#region public methods
const handleNavigateToNextPage = useCallback(() => {
const currentIndex = indexRef.current;
if (currentIndex === data.length - 1) {
return;
}
animatedOverrideIndex.setValue(currentIndex + 1);
}, [data, animatedOverrideIndex]);
const handleNavigateToPreviousPage = useCallback(() => {
const currentIndex = indexRef.current;
if (currentIndex === 0) {
return;
}
animatedOverrideIndex.setValue(currentIndex - 1);
}, [animatedOverrideIndex]);
useImperativeHandle(
ref,
() => ({
next: handleNavigateToNextPage,
previous: handleNavigateToPreviousPage,
}),
[handleNavigateToNextPage, handleNavigateToPreviousPage]
);
//#endregion
//#region effects
useCode(
() =>
onChange(
animatedStaticIndex,
call([animatedStaticIndex], args => {
indexRef.current = args[0];
/**
* @DEV
* here we directly manipulate pages native props by setting `pointerEvents`
* to `auto` for current page and `none` for others.
*/
pagesRef.current.map((pageRef, _index) => {
// @ts-ignore
pageRef.setNativeProps({
pointerEvents: _index === args[0] ? 'auto' : 'none',
});
});
if (onIndexChange) {
onIndexChange(args[0]);
}
})
),
[]
);
//#endregion
// renders
return (
<PanGestureHandler {...gestureHandler}>
<Animated.View onLayout={handleOnLayout} style={styles.container}>
<Background
animatedIndex={animatedIndex}
data={data}
safeInsets={safeInsets}
screenDimensions={dimensions}
indicatorSize={indicatorSize}
animatedIndicatorsContainerPosition={
animatedIndicatorsContainerPosition
}
/>
{data.map((item, index) => (
<Page
key={`page-${index}`}
index={index}
item={item}
animatedIndex={animatedIndex}
indicatorSize={indicatorSize}
titleStyle={titleStyle}
descriptionStyle={descriptionStyle}
safeInsets={safeInsets}
screenDimensions={dimensions}
handleRef={handlePageRef}
/>
))}
<IndicatorsContainer
data={data}
animatedIndex={animatedIndex}
animatedIndicatorsContainerPosition={
animatedIndicatorsContainerPosition
}
indicatorSize={indicatorSize}
indicatorBackgroundColor={indicatorBackgroundColor}
indicatorBorderColor={indicatorBorderColor}
safeInsets={safeInsets}
/>
<CloseButton
data={data}
animatedIndex={animatedIndex}
safeInsets={safeInsets}
closeButtonText={closeButtonText}
closeButtonTextStyle={closeButtonTextStyle}
closeButton={closeButton}
onCloseButtonPress={onCloseButtonPress}
/>
</Animated.View>
</PanGestureHandler>
);
}
)
Example #18
Source File: Ripple.tsx From mobile with Apache License 2.0 | 4 votes |
Ripple = ({
children,
disabled,
rippleColor = 'rgb(0, 0, 0)',
rippleCentered = false,
rippleOpacity = 0.3,
rippleExpandDuration = 500,
rippleFadeDuration = 200,
rippleContainerBorderRadius = 0,
rippleSize = 0,
...touchableWithoutFeedbackProps
}: RippleProps) => {
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const uuid = useRef(0);
const [ripples, setRipples] = useState<AnimatedRipple[]>([]);
const [fadings, setFadings] = useState<number[]>([]);
const startFade = useCallback(
(ripple: AnimatedRipple, duration: number) => {
if (fadings.indexOf(ripple.id) >= 0) {
return;
}
setFadings([...fadings, ripple.id]);
Animated.timing(ripple.fadeAnimatedValue, {
toValue: 1,
easing: Easing.out(Easing.ease),
duration,
useNativeDriver: true,
}).start(() => {
setRipples(ripples.filter(item => item !== ripple));
});
},
[fadings, ripples],
);
const startExpand = useCallback(
(event: GestureResponderEvent) => {
if (!width || !height) {
return;
}
const timestamp = Date.now();
if (ripples.length > 0 && timestamp < ripples[ripples.length - 1].timestamp + DEBOUNCE) {
return;
}
const w2 = 0.5 * width;
const h2 = 0.5 * height;
const {locationX, locationY} = rippleCentered ? {locationX: w2, locationY: h2} : event.nativeEvent;
const offsetX = Math.abs(w2 - locationX);
const offsetY = Math.abs(h2 - locationY);
const radius = rippleSize > 0 ? 0.5 * rippleSize : Math.sqrt((w2 + offsetX) ** 2 + (h2 + offsetY) ** 2);
const id = uuid.current;
uuid.current += 1;
const ripple: AnimatedRipple = {
id,
locationX,
locationY,
radius,
timestamp,
expandAnimatedValue: new Animated.Value(0),
fadeAnimatedValue: new Animated.Value(0),
};
Animated.timing(ripple.expandAnimatedValue, {
toValue: 1,
easing: Easing.out(Easing.ease),
duration: rippleExpandDuration,
useNativeDriver: true,
}).start();
setRipples(ripples.concat(ripple));
},
[height, rippleCentered, rippleExpandDuration, rippleSize, ripples, width],
);
const onLayout = useCallback(
(event: LayoutChangeEvent) => {
const {
nativeEvent: {
layout: {height, width},
},
} = event;
setWidth(width);
setHeight(height);
touchableWithoutFeedbackProps.onLayout?.(event);
},
[touchableWithoutFeedbackProps.onLayout],
);
const onPressIn = useCallback(
(event: GestureResponderEvent) => {
startExpand(event);
touchableWithoutFeedbackProps.onPressIn?.(event);
},
[startExpand, touchableWithoutFeedbackProps.onPressIn],
);
const onPressOut = useCallback(
(event: GestureResponderEvent) => {
ripples.forEach(ripple => startFade(ripple, rippleFadeDuration + rippleExpandDuration / 2));
touchableWithoutFeedbackProps.onPressOut?.(event);
},
[rippleExpandDuration, rippleFadeDuration, ripples, startFade, touchableWithoutFeedbackProps.onPressOut],
);
const onPress = useCallback(
(event: GestureResponderEvent) => {
requestAnimationFrame(() => {
touchableWithoutFeedbackProps.onPress?.(event);
});
},
[touchableWithoutFeedbackProps.onPress],
);
const renderRipple = useCallback(
({locationX, locationY, radius, id, expandAnimatedValue, fadeAnimatedValue}: AnimatedRipple) => {
const rippleStyle = {
top: locationY - RADIUS,
[I18nManager.isRTL ? 'right' : 'left']: locationX - RADIUS,
backgroundColor: rippleColor,
transform: [
{
scale: expandAnimatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0.5 / RADIUS, radius / RADIUS],
}),
},
],
opacity: fadeAnimatedValue.interpolate({
inputRange: [0, 1],
outputRange: [rippleOpacity, 0],
}),
};
return <Animated.View style={[styles.ripple, rippleStyle]} key={id} />;
},
[rippleColor, rippleOpacity],
);
const style = useMemo(
() => [
styles.container,
{
borderRadius: rippleContainerBorderRadius,
},
],
[rippleContainerBorderRadius],
);
return (
<TouchableWithoutFeedback
{...touchableWithoutFeedbackProps}
disabled={disabled}
onPressIn={onPressIn}
onPressOut={onPressOut}
onPress={onPress}
onLayout={onLayout}
>
<Animated.View pointerEvents="box-only">
<View style={style}>{ripples.map(renderRipple)}</View>
{children}
</Animated.View>
</TouchableWithoutFeedback>
);
}
Example #19
Source File: AnimatedTabBar.tsx From curved-bottom-navigation-bar with MIT License | 4 votes |
AnimatedTabBarComponent = (props: AnimatedTabBarProps) => {
// props
const {
navigation,
tabs,
descriptors,
state,
duration = DEFAULT_ITEM_ANIMATION_DURATION,
barColor = TAB_BAR_COLOR,
dotSize = SIZE_DOT,
barHeight = TAB_BAR_HEIGHT,
dotColor = TAB_BAR_COLOR,
titleShown = false,
barWidth,
} = props;
// variables
const {
routes,
index: navigationIndex,
key: navigationKey,
} = useMemo(() => {
return state;
}, [state]);
// reanimated
const selectedIndex = useSharedValue(0);
// callbacks
const getRouteTitle = useCallback(
(route: Route<string>) => {
const { options } = descriptors[route.key];
// eslint-disable-next-line no-nested-ternary
return options.tabBarLabel !== undefined &&
typeof options.tabBarLabel === 'string'
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
},
[descriptors]
);
const getRouteTabConfigs = useCallback(
(route: Route<string>) => {
return tabs[route.name];
},
[tabs]
);
const getRoutes = useCallback(() => {
return routes.map((route) => ({
key: route.key,
title: getRouteTitle(route),
...getRouteTabConfigs(route),
}));
}, [routes, getRouteTitle, getRouteTabConfigs]);
const handleSelectedIndexChange = useCallback(
(index: number) => {
const { key, name } = routes[index];
const event = navigation.emit({
type: 'tabPress',
target: key,
canPreventDefault: true,
});
if (!event.defaultPrevented) {
navigation.dispatch({
...CommonActions.navigate(name),
target: navigationKey,
});
}
},
[routes, navigation, navigationKey]
);
// effects
useEffect(() => {
selectedIndex.value = navigationIndex;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [navigationIndex]);
useAnimatedReaction(
() => selectedIndex.value,
(nextSelected, prevSelected) => {
if (nextSelected !== prevSelected) {
runOnJS(handleSelectedIndexChange)(nextSelected);
}
},
[selectedIndex, handleSelectedIndexChange]
);
// render
return (
<CurvedTabBar
isRtl={I18nManager.isRTL}
barWidth={barWidth}
titleShown={titleShown}
dotColor={dotColor}
barHeight={barHeight}
dotSize={dotSize}
tabBarColor={barColor}
selectedIndex={selectedIndex}
navigationIndex={navigationIndex}
routes={getRoutes()}
duration={duration}
/>
);
}