mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-09 22:51:57 +08:00
refactor: refactor and perf improvements
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Easing } from 'react-native-reanimated';
|
||||
import { TransitionSpec } from '../types';
|
||||
|
||||
// These are the exact values from UINavigationController's animation configuration
|
||||
export const TransitionIOSSpec: TransitionSpec = {
|
||||
timing: 'spring',
|
||||
config: {
|
||||
|
||||
@@ -69,6 +69,7 @@ export type HeaderOptions = {
|
||||
headerBackTitle?: string;
|
||||
headerBackTitleStyle?: StyleProp<TextStyle>;
|
||||
headerTruncatedBackTitle?: string;
|
||||
headerTitleContainerStyle?: StyleProp<ViewStyle>;
|
||||
headerLeft?: (props: HeaderBackButtonProps) => React.ReactNode;
|
||||
headerLeftContainerStyle?: StyleProp<ViewStyle>;
|
||||
headerRight?: () => React.ReactNode;
|
||||
@@ -118,7 +119,7 @@ export type HeaderBackButtonProps = {
|
||||
disabled?: boolean;
|
||||
onPress?: () => void;
|
||||
pressColorAndroid?: string;
|
||||
backImage?: (props: { tintColor: string; label?: string }) => React.ReactNode;
|
||||
backImage?: (props: { tintColor: string }) => React.ReactNode;
|
||||
tintColor?: string;
|
||||
label?: string;
|
||||
truncatedLabel?: string;
|
||||
|
||||
@@ -18,14 +18,14 @@ export default class Header extends React.PureComponent<HeaderProps> {
|
||||
|
||||
let leftLabel;
|
||||
|
||||
// The label for the left back button shows the title of the previous screen
|
||||
// If a custom label is specified, we use it, otherwise use previous screen's title
|
||||
if (options.headerBackTitle !== undefined) {
|
||||
leftLabel = options.headerBackTitle;
|
||||
} else {
|
||||
if (previous) {
|
||||
const opts = previous.descriptor.options;
|
||||
leftLabel =
|
||||
opts.headerTitle !== undefined ? opts.headerTitle : opts.title;
|
||||
}
|
||||
} else if (previous) {
|
||||
const opts = previous.descriptor.options;
|
||||
leftLabel =
|
||||
opts.headerTitle !== undefined ? opts.headerTitle : opts.title;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -42,6 +42,9 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
return;
|
||||
}
|
||||
|
||||
// This measurement is used to determine if we should truncate the label when it doesn't fit
|
||||
// Only measure it once because `onLayout` will fire again when we render truncated label
|
||||
// and we want the measurement of not-truncated label
|
||||
this.setState({
|
||||
initialLabelWidth: e.nativeEvent.layout.x + e.nativeEvent.layout.width,
|
||||
});
|
||||
@@ -50,17 +53,15 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
private renderBackImage() {
|
||||
const { backImage, labelVisible, tintColor } = this.props;
|
||||
|
||||
let label = this.getLabelText();
|
||||
|
||||
if (backImage) {
|
||||
return backImage({ tintColor, label });
|
||||
return backImage({ tintColor });
|
||||
} else {
|
||||
return (
|
||||
<Image
|
||||
style={[
|
||||
styles.icon,
|
||||
!!labelVisible && styles.iconWithTitle,
|
||||
!!tintColor && { tintColor },
|
||||
Boolean(labelVisible) && styles.iconWithLabel,
|
||||
Boolean(tintColor) && { tintColor },
|
||||
]}
|
||||
source={require('../assets/back-icon.png')}
|
||||
fadeDuration={0}
|
||||
@@ -72,7 +73,7 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
private getLabelText = () => {
|
||||
const { titleLayout, screenLayout, label, truncatedLabel } = this.props;
|
||||
|
||||
let { initialLabelWidth: initialLabelWidth } = this.state;
|
||||
let { initialLabelWidth } = this.state;
|
||||
|
||||
if (!label) {
|
||||
return truncatedLabel;
|
||||
@@ -88,7 +89,7 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
private maybeRenderTitle() {
|
||||
private renderLabel() {
|
||||
const {
|
||||
allowFontScaling,
|
||||
labelVisible,
|
||||
@@ -104,12 +105,12 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
return null;
|
||||
}
|
||||
|
||||
const title = (
|
||||
const label = (
|
||||
<Animated.Text
|
||||
accessible={false}
|
||||
onLayout={this.handleLabelLayout}
|
||||
style={[
|
||||
styles.title,
|
||||
styles.label,
|
||||
screenLayout ? { marginRight: screenLayout.width / 2 } : null,
|
||||
tintColor ? { color: tintColor } : null,
|
||||
labelStyle,
|
||||
@@ -122,7 +123,9 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
);
|
||||
|
||||
if (backImage) {
|
||||
return title;
|
||||
// When a custom backimage is specified, we can't mask the label
|
||||
// Otherwise there might be weird effect due to our mask not being the same as the image
|
||||
return label;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -137,7 +140,7 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
</View>
|
||||
}
|
||||
>
|
||||
{title}
|
||||
{label}
|
||||
</MaskedViewIOS>
|
||||
);
|
||||
}
|
||||
@@ -171,7 +174,7 @@ class HeaderBackButton extends React.Component<Props, State> {
|
||||
>
|
||||
<React.Fragment>
|
||||
{this.renderBackImage()}
|
||||
{this.maybeRenderTitle()}
|
||||
{this.renderLabel()}
|
||||
</React.Fragment>
|
||||
</TouchableItem>
|
||||
);
|
||||
@@ -193,9 +196,9 @@ const styles = StyleSheet.create({
|
||||
disabled: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
title: {
|
||||
label: {
|
||||
fontSize: 17,
|
||||
// Title and back title are a bit different width due to title being bold
|
||||
// Title and back label are a bit different width due to title being bold
|
||||
// Adjusting the letterSpacing makes them coincide better
|
||||
letterSpacing: 0.35,
|
||||
},
|
||||
@@ -217,7 +220,7 @@ const styles = StyleSheet.create({
|
||||
transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
|
||||
},
|
||||
}),
|
||||
iconWithTitle:
|
||||
iconWithLabel:
|
||||
Platform.OS === 'ios'
|
||||
? {
|
||||
marginRight: 6,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { View, StyleSheet, Platform, ViewProps } from 'react-native';
|
||||
|
||||
type Props = ViewProps;
|
||||
|
||||
export default function HeaderBackground({ style, ...rest }: Props) {
|
||||
export default function HeaderBackground({ style, ...rest }: ViewProps) {
|
||||
return <View style={[styles.container, style]} {...rest} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function HeaderContainer({
|
||||
<View
|
||||
key={scene.route.key}
|
||||
onLayout={onLayout}
|
||||
pointerEvents="box-none"
|
||||
pointerEvents={isFocused ? 'box-none' : 'none'}
|
||||
accessibilityElementsHidden={!isFocused}
|
||||
importantForAccessibility={
|
||||
isFocused ? 'auto' : 'no-hide-descendants'
|
||||
|
||||
@@ -137,6 +137,7 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
headerBackTitleStyle: customLeftLabelStyle,
|
||||
headerLeftContainerStyle: leftContainerStyle,
|
||||
headerRightContainerStyle: rightContainerStyle,
|
||||
headerTitleContainerStyle: titleContainerStyle,
|
||||
styleInterpolator,
|
||||
} = this.props;
|
||||
|
||||
@@ -196,21 +197,25 @@ export default class HeaderSegment extends React.Component<Props, State> {
|
||||
</Animated.View>
|
||||
) : null}
|
||||
{currentTitle ? (
|
||||
<HeaderTitle
|
||||
allowFontScaling={titleAllowFontScaling}
|
||||
onLayout={this.handleTitleLayout}
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.title,
|
||||
Platform.select({
|
||||
ios: null,
|
||||
default: { left: onGoBack ? 72 : 16 },
|
||||
}),
|
||||
styles.title,
|
||||
titleStyle,
|
||||
customTitleStyle,
|
||||
titleContainerStyle,
|
||||
]}
|
||||
>
|
||||
{currentTitle}
|
||||
</HeaderTitle>
|
||||
<HeaderTitle
|
||||
onLayout={this.handleTitleLayout}
|
||||
allowFontScaling={titleAllowFontScaling}
|
||||
style={customTitleStyle}
|
||||
>
|
||||
{currentTitle}
|
||||
</HeaderTitle>
|
||||
</Animated.View>
|
||||
) : null}
|
||||
{right ? (
|
||||
<Animated.View
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import { StyleSheet, Platform } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { Text, StyleSheet, Platform, TextProps } from 'react-native';
|
||||
|
||||
type Props = React.ComponentProps<typeof Animated.Text> & {
|
||||
type Props = TextProps & {
|
||||
children: string;
|
||||
};
|
||||
|
||||
export default function HeaderTitle({ style, ...rest }: Props) {
|
||||
return <Animated.Text {...rest} style={[styles.title, style]} />;
|
||||
return <Text {...rest} style={[styles.title, style]} />;
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
@@ -23,7 +23,6 @@ type Props = ViewProps & {
|
||||
onGestureCanceled?: () => void;
|
||||
onGestureEnd?: () => void;
|
||||
children: React.ReactNode;
|
||||
animateIn: boolean;
|
||||
gesturesEnabled: boolean;
|
||||
gestureResponseDistance?: {
|
||||
vertical?: number;
|
||||
@@ -58,54 +57,46 @@ const GESTURE_RESPONSE_DISTANCE_HORIZONTAL = 50;
|
||||
const GESTURE_RESPONSE_DISTANCE_VERTICAL = 135;
|
||||
|
||||
const {
|
||||
cond,
|
||||
eq,
|
||||
neq,
|
||||
set,
|
||||
abs,
|
||||
and,
|
||||
or,
|
||||
block,
|
||||
call,
|
||||
cond,
|
||||
divide,
|
||||
eq,
|
||||
greaterThan,
|
||||
lessThan,
|
||||
abs,
|
||||
add,
|
||||
max,
|
||||
block,
|
||||
stopClock,
|
||||
startClock,
|
||||
clockRunning,
|
||||
min,
|
||||
neq,
|
||||
onChange,
|
||||
Value,
|
||||
Clock,
|
||||
call,
|
||||
or,
|
||||
set,
|
||||
spring,
|
||||
sub,
|
||||
timing,
|
||||
interpolate,
|
||||
startClock,
|
||||
stopClock,
|
||||
clockRunning,
|
||||
Clock,
|
||||
Value,
|
||||
} = Animated;
|
||||
|
||||
export default class Card extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
animateIn: true,
|
||||
gesturesEnabled: true,
|
||||
};
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const { layout, direction, closing, animateIn } = this.props;
|
||||
const { layout, direction, closing } = this.props;
|
||||
const { width, height } = layout;
|
||||
|
||||
if (
|
||||
width !== prevProps.layout.width ||
|
||||
height !== prevProps.layout.height
|
||||
) {
|
||||
if (width !== prevProps.layout.width) {
|
||||
this.layout.width.setValue(width);
|
||||
this.layout.height.setValue(height);
|
||||
}
|
||||
|
||||
this.position.setValue(
|
||||
animateIn
|
||||
? direction === 'vertical'
|
||||
? layout.height
|
||||
: layout.width
|
||||
: 0
|
||||
);
|
||||
if (height !== prevProps.layout.height) {
|
||||
this.layout.height.setValue(height);
|
||||
}
|
||||
|
||||
if (direction !== prevProps.direction) {
|
||||
@@ -143,14 +134,6 @@ export default class Card extends React.Component<Props> {
|
||||
this.layout.width
|
||||
);
|
||||
|
||||
private position = new Value(
|
||||
this.props.animateIn
|
||||
? this.props.direction === 'vertical'
|
||||
? this.props.layout.height
|
||||
: this.props.layout.width
|
||||
: 0
|
||||
);
|
||||
|
||||
private gesture = new Value(0);
|
||||
private offset = new Value(0);
|
||||
private velocity = new Value(0);
|
||||
@@ -164,8 +147,10 @@ export default class Card extends React.Component<Props> {
|
||||
private toValue = new Value(0);
|
||||
private frameTime = new Value(0);
|
||||
|
||||
private transitionVelocity = new Value(0);
|
||||
|
||||
private transitionState = {
|
||||
position: this.position,
|
||||
position: this.props.current,
|
||||
time: new Value(0),
|
||||
finished: new Value(FALSE),
|
||||
};
|
||||
@@ -173,13 +158,17 @@ export default class Card extends React.Component<Props> {
|
||||
private runTransition = (isVisible: Binary | Animated.Node<number>) => {
|
||||
const { open: openingSpec, close: closingSpec } = this.props.transitionSpec;
|
||||
|
||||
const toValue = cond(isVisible, 0, this.distance);
|
||||
|
||||
return cond(eq(this.position, toValue), NOOP, [
|
||||
return cond(eq(this.props.current, isVisible), NOOP, [
|
||||
cond(clockRunning(this.clock), NOOP, [
|
||||
// Animation wasn't running before
|
||||
// Set the initial values and start the clock
|
||||
set(this.toValue, toValue),
|
||||
set(this.toValue, isVisible),
|
||||
// The velocity value is ideal for translating the whole screen
|
||||
// But since we have 0-1 scale, we need to adjust the velocity
|
||||
set(
|
||||
this.transitionVelocity,
|
||||
cond(this.distance, divide(this.velocity, this.distance), 0)
|
||||
),
|
||||
set(this.frameTime, 0),
|
||||
set(this.transitionState.time, 0),
|
||||
set(this.transitionState.finished, FALSE),
|
||||
@@ -192,11 +181,11 @@ export default class Card extends React.Component<Props> {
|
||||
}),
|
||||
]),
|
||||
cond(
|
||||
eq(toValue, 0),
|
||||
eq(isVisible, 1),
|
||||
openingSpec.timing === 'spring'
|
||||
? spring(
|
||||
this.clock,
|
||||
{ ...this.transitionState, velocity: this.velocity },
|
||||
{ ...this.transitionState, velocity: this.transitionVelocity },
|
||||
{ ...openingSpec.config, toValue: this.toValue }
|
||||
)
|
||||
: timing(
|
||||
@@ -207,7 +196,7 @@ export default class Card extends React.Component<Props> {
|
||||
closingSpec.timing === 'spring'
|
||||
? spring(
|
||||
this.clock,
|
||||
{ ...this.transitionState, velocity: this.velocity },
|
||||
{ ...this.transitionState, velocity: this.transitionVelocity },
|
||||
{ ...closingSpec.config, toValue: this.toValue }
|
||||
)
|
||||
: timing(
|
||||
@@ -237,7 +226,7 @@ export default class Card extends React.Component<Props> {
|
||||
]);
|
||||
};
|
||||
|
||||
private translate = block([
|
||||
private exec = block([
|
||||
onChange(
|
||||
this.isClosing,
|
||||
cond(this.isClosing, set(this.nextIsVisible, FALSE))
|
||||
@@ -276,18 +265,6 @@ export default class Card extends React.Component<Props> {
|
||||
}
|
||||
)
|
||||
),
|
||||
// Synchronize the translation with the animated value representing the progress
|
||||
set(
|
||||
this.props.current,
|
||||
cond(
|
||||
or(eq(this.layout.width, 0), eq(this.layout.height, 0)),
|
||||
this.isVisible,
|
||||
interpolate(this.position, {
|
||||
inputRange: [0, this.distance],
|
||||
outputRange: [1, 0],
|
||||
})
|
||||
)
|
||||
),
|
||||
cond(
|
||||
eq(this.gestureState, GestureState.ACTIVE),
|
||||
[
|
||||
@@ -297,10 +274,22 @@ export default class Card extends React.Component<Props> {
|
||||
set(this.isSwiping, TRUE),
|
||||
set(this.isSwipeGesture, TRUE),
|
||||
// Also update the drag offset to the last position
|
||||
set(this.offset, this.position),
|
||||
set(this.offset, this.props.current),
|
||||
]),
|
||||
// Update position with next offset + gesture distance
|
||||
set(this.position, max(add(this.offset, this.gesture), 0)),
|
||||
set(
|
||||
this.props.current,
|
||||
min(
|
||||
max(
|
||||
sub(
|
||||
this.offset,
|
||||
cond(this.distance, divide(this.gesture, this.distance), 1)
|
||||
),
|
||||
0
|
||||
),
|
||||
1
|
||||
)
|
||||
),
|
||||
// Stop animations while we're dragging
|
||||
stopClock(this.clock),
|
||||
],
|
||||
@@ -342,7 +331,6 @@ export default class Card extends React.Component<Props> {
|
||||
),
|
||||
]
|
||||
),
|
||||
this.position,
|
||||
]);
|
||||
|
||||
private handleGestureEventHorizontal = Animated.event([
|
||||
@@ -442,7 +430,7 @@ export default class Card extends React.Component<Props> {
|
||||
return (
|
||||
<StackGestureContext.Provider value={this.gestureRef}>
|
||||
<View pointerEvents="box-none" {...rest}>
|
||||
<Animated.Code exec={this.translate} />
|
||||
<Animated.Code exec={this.exec} />
|
||||
{overlayStyle ? (
|
||||
<Animated.View
|
||||
pointerEvents="none"
|
||||
|
||||
@@ -160,7 +160,6 @@ export default class Stack extends React.Component<Props, State> {
|
||||
descriptors,
|
||||
navigation,
|
||||
routes,
|
||||
openingRoutes,
|
||||
closingRoutes,
|
||||
onOpenRoute,
|
||||
onCloseRoute,
|
||||
@@ -206,7 +205,6 @@ export default class Stack extends React.Component<Props, State> {
|
||||
closing={closingRoutes.includes(route.key)}
|
||||
onOpen={() => onOpenRoute({ route })}
|
||||
onClose={() => onCloseRoute({ route })}
|
||||
animateIn={openingRoutes.includes(route.key)}
|
||||
gesturesEnabled={getGesturesEnabled({ route })}
|
||||
onTransitionStart={({ closing }) => {
|
||||
onTransitionStart &&
|
||||
|
||||
@@ -94,9 +94,9 @@ class StackView extends React.Component<Props, State> {
|
||||
const { routes } = this.props.navigation.state;
|
||||
|
||||
const isFirst = routes[0].key === route.key;
|
||||
const isLast = routes[routes.length - 1].key === route.key;
|
||||
|
||||
if (isFirst || !isLast) {
|
||||
if (isFirst) {
|
||||
// The user shouldn't be able to close the first screen
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -109,29 +109,26 @@ class StackView extends React.Component<Props, State> {
|
||||
|
||||
private renderScene = ({ route }: { route: Route }) => {
|
||||
const descriptor = this.state.descriptors[route.key];
|
||||
|
||||
if (!descriptor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { navigation, getComponent } = descriptor;
|
||||
const SceneComponent = getComponent();
|
||||
|
||||
const { screenProps } = this.props;
|
||||
|
||||
return (
|
||||
<SceneView
|
||||
screenProps={screenProps}
|
||||
screenProps={this.props.screenProps}
|
||||
navigation={navigation}
|
||||
component={SceneComponent}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
private handleGoBack = ({ route }: { route: Route }) =>
|
||||
private handleGoBack = ({ route }: { route: Route }) => {
|
||||
// This event will trigger when a gesture ends
|
||||
// We need to perform the transition before popping the route completely
|
||||
this.props.navigation.dispatch(StackActions.pop({ key: route.key }));
|
||||
};
|
||||
|
||||
private handleTransitionComplete = ({ route }: { route: Route }) => {
|
||||
// When transition completes, we need to remove the item from the pushing/popping arrays
|
||||
this.props.navigation.dispatch(
|
||||
StackActions.completeTransition({ toChildKey: route.key })
|
||||
);
|
||||
@@ -142,6 +139,9 @@ class StackView extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
private handleCloseRoute = ({ route }: { route: Route }) => {
|
||||
// This event will trigger when the transition for closing the route ends
|
||||
// In this case, we need to clean up any state tracking the route and pop it immediately
|
||||
|
||||
// @ts-ignore
|
||||
this.setState(state => ({
|
||||
routes: state.routes.filter(r => r.key !== route.key),
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { ViewProps } from 'react-native';
|
||||
import Animated from 'react-native-reanimated';
|
||||
import { PanGestureHandler } from 'react-native-gesture-handler';
|
||||
import StackGestureContext from '../../utils/StackGestureContext';
|
||||
import { Layout } from '../../types';
|
||||
|
||||
type Props = ViewProps & {
|
||||
gesture: Animated.Value<number>;
|
||||
velocity: Animated.Value<number>;
|
||||
gestureState: Animated.Value<number>;
|
||||
layout: Layout;
|
||||
direction: 'horizontal' | 'vertical';
|
||||
gesturesEnabled: boolean;
|
||||
gestureResponseDistance?: {
|
||||
vertical?: number;
|
||||
horizontal?: number;
|
||||
};
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* The distance of touch start from the edge of the screen where the gesture will be recognized
|
||||
*/
|
||||
const GESTURE_RESPONSE_DISTANCE_HORIZONTAL = 50;
|
||||
const GESTURE_RESPONSE_DISTANCE_VERTICAL = 135;
|
||||
|
||||
export default class Swipeable extends React.Component<Props> {
|
||||
private handleGestureEventHorizontal = Animated.event([
|
||||
{
|
||||
nativeEvent: {
|
||||
translationX: this.props.gesture,
|
||||
velocityX: this.props.velocity,
|
||||
state: this.props.gestureState,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
private handleGestureEventVertical = Animated.event([
|
||||
{
|
||||
nativeEvent: {
|
||||
translationY: this.props.gesture,
|
||||
velocityY: this.props.velocity,
|
||||
state: this.props.gestureState,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
private gestureActivationCriteria() {
|
||||
const { layout, direction, gestureResponseDistance } = this.props;
|
||||
|
||||
// Doesn't make sense for a response distance of 0, so this works fine
|
||||
const distance =
|
||||
direction === 'vertical'
|
||||
? (gestureResponseDistance && gestureResponseDistance.vertical) ||
|
||||
GESTURE_RESPONSE_DISTANCE_VERTICAL
|
||||
: (gestureResponseDistance && gestureResponseDistance.horizontal) ||
|
||||
GESTURE_RESPONSE_DISTANCE_HORIZONTAL;
|
||||
|
||||
if (direction === 'vertical') {
|
||||
return {
|
||||
maxDeltaX: 15,
|
||||
minOffsetY: 5,
|
||||
hitSlop: { bottom: -layout.height + distance },
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
minOffsetX: 5,
|
||||
maxDeltaY: 20,
|
||||
hitSlop: { right: -layout.width + distance },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private gestureRef: React.Ref<PanGestureHandler> = React.createRef();
|
||||
|
||||
render() {
|
||||
const {
|
||||
layout,
|
||||
direction,
|
||||
gesturesEnabled,
|
||||
children,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
const handleGestureEvent =
|
||||
direction === 'vertical'
|
||||
? this.handleGestureEventVertical
|
||||
: this.handleGestureEventHorizontal;
|
||||
|
||||
return (
|
||||
<StackGestureContext.Provider value={this.gestureRef}>
|
||||
<PanGestureHandler
|
||||
ref={this.gestureRef}
|
||||
enabled={layout.width !== 0 && gesturesEnabled}
|
||||
onGestureEvent={handleGestureEvent}
|
||||
onHandlerStateChange={handleGestureEvent}
|
||||
{...this.gestureActivationCriteria()}
|
||||
>
|
||||
<Animated.View {...rest}>{children}</Animated.View>
|
||||
</PanGestureHandler>
|
||||
</StackGestureContext.Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user