Skip to content
This repository was archived by the owner on Feb 25, 2020. It is now read-only.

Commit 3d4b173

Browse files
authored
fix: send events even is stack animation is vain (#270)
1 parent cb8c772 commit 3d4b173

File tree

2 files changed

+152
-89
lines changed

2 files changed

+152
-89
lines changed

example/src/ModalStack.tsx

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import * as React from 'react';
2-
import { Button, View, Text, Dimensions, Switch } from 'react-native';
2+
import {
3+
Button,
4+
View,
5+
Text,
6+
Dimensions,
7+
Switch,
8+
TextInput,
9+
} from 'react-native';
310
import {
411
createStackNavigator,
512
CardStyleInterpolators,
@@ -127,6 +134,10 @@ class DetailsScreen extends React.Component<NavigationStackScreenProps> {
127134
title="Go to Details... again"
128135
onPress={() => this.props.navigation.push('Details')}
129136
/>
137+
<Button
138+
title="Go to inputs..."
139+
onPress={() => this.props.navigation.push('Inputs')}
140+
/>
130141
<Button
131142
title="Go to List"
132143
onPress={() => this.props.navigation.navigate('List')}
@@ -143,12 +154,34 @@ class DetailsScreen extends React.Component<NavigationStackScreenProps> {
143154
);
144155
}
145156
}
157+
function InputsScreen({ navigation }: NavigationStackScreenProps) {
158+
return (
159+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
160+
<Text>Inputs Screen</Text>
161+
<TextInput
162+
defaultValue="sample"
163+
autoFocus
164+
style={{ backgroundColor: 'blue' }}
165+
/>
166+
<TextInput defaultValue="sample" style={{ backgroundColor: 'red' }} />
167+
<TextInput defaultValue="sample" style={{ backgroundColor: 'green' }} />
168+
<Button
169+
title="Go to inputs... again"
170+
onPress={() => navigation.push('Inputs')}
171+
/>
172+
</View>
173+
);
174+
}
146175

147176
export default createStackNavigator(
148177
{
149178
List: ListScreen,
150179
Details: DetailsScreen,
151180
Modal: Modal,
181+
Inputs: {
182+
screen: InputsScreen,
183+
navigationOptions: { gestureDirection: 'vertical' },
184+
},
152185
},
153186
{
154187
initialRouteName: 'List',

src/views/Stack/Card.tsx

Lines changed: 118 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ export default class Card extends React.Component<Props> {
351351
private offset = new Value(0);
352352
private velocityUntraversed = new Value(0);
353353
private velocity = new Value(0);
354+
private didMovementHappen = new Value(0);
354355

355356
private gestureState = new Value(0);
356357

@@ -404,98 +405,126 @@ export default class Card extends React.Component<Props> {
404405
private runTransition = (isVisible: Binary | Animated.Node<number>) => {
405406
const { open: openingSpec, close: closingSpec } = this.props.transitionSpec;
406407

407-
return cond(eq(this.props.current, isVisible), NOOP_NODE, [
408-
cond(clockRunning(this.clock), NOOP_NODE, [
409-
// Animation wasn't running before
410-
// Set the initial values and start the clock
411-
set(this.toValue, isVisible),
412-
// The velocity value is ideal for translating the whole screen
413-
// But since we have 0-1 scale, we need to adjust the velocity
414-
set(
415-
this.transitionVelocity,
416-
multiply(
417-
cond(
418-
this.distance,
419-
divide(this.velocity, this.distance),
420-
FALSE_NODE
421-
),
422-
-1
423-
)
424-
),
425-
set(this.frameTime, FALSE_NODE),
426-
set(this.transitionState.time, FALSE_NODE),
427-
set(this.transitionState.finished, FALSE_NODE),
428-
set(this.isVisible, isVisible),
429-
startClock(this.clock),
430-
call([this.isVisible], ([value]: ReadonlyArray<Binary>) => {
431-
this.handleStartInteraction();
432-
433-
const { onTransitionStart } = this.props;
434-
this.noAnimationStartedSoFar = false;
435-
this.isRunningAnimation = true;
436-
onTransitionStart && onTransitionStart({ closing: !value });
437-
}),
438-
]),
408+
return [
439409
cond(
440-
eq(isVisible, TRUE_NODE),
441-
openingSpec.animation === 'spring'
442-
? memoizedSpring(
443-
this.clock,
444-
{ ...this.transitionState, velocity: this.transitionVelocity },
445-
// @ts-ignore
446-
{
447-
...(this.openingSpecConfig as AnimatedSpringConfig),
448-
toValue: this.toValue,
449-
}
450-
)
451-
: timing(
452-
this.clock,
453-
{ ...this.transitionState, frameTime: this.frameTime },
454-
{
455-
...(this.openingSpecConfig as AnimatedTimingConfig),
456-
toValue: this.toValue,
457-
}
410+
eq(this.props.current, isVisible),
411+
call(
412+
[this.didMovementHappen, this.isVisible],
413+
([didMovementHappen]: ReadonlyArray<Binary>) => {
414+
if (didMovementHappen) {
415+
// if we go back to the same position,
416+
// let's pretend that whole animation happen
417+
// for making the logic consistent
418+
// It's especially vital for having inputs properly focused.
419+
this.handleStartInteraction();
420+
const { onTransitionStart } = this.props;
421+
onTransitionStart && onTransitionStart({ closing: true });
422+
this.handleTransitionEnd();
423+
this.props.onOpen(true);
424+
}
425+
}
426+
),
427+
[
428+
cond(clockRunning(this.clock), NOOP_NODE, [
429+
// Animation wasn't running before
430+
// Set the initial values and start the clock
431+
set(this.toValue, isVisible),
432+
// The velocity value is ideal for translating the whole screen
433+
// But since we have 0-1 scale, we need to adjust the velocity
434+
set(
435+
this.transitionVelocity,
436+
multiply(
437+
cond(
438+
this.distance,
439+
divide(this.velocity, this.distance),
440+
FALSE_NODE
441+
),
442+
-1
443+
)
458444
),
459-
closingSpec.animation === 'spring'
460-
? memoizedSpring(
461-
this.clock,
462-
{ ...this.transitionState, velocity: this.transitionVelocity },
463-
// @ts-ignore
464-
{
465-
...(this.closingSpecConfig as AnimatedSpringConfig),
466-
toValue: this.toValue,
467-
}
468-
)
469-
: timing(
470-
this.clock,
471-
{ ...this.transitionState, frameTime: this.frameTime },
472-
{
473-
...(this.closingSpecConfig as AnimatedTimingConfig),
474-
toValue: this.toValue,
445+
set(this.frameTime, FALSE_NODE),
446+
set(this.transitionState.time, FALSE_NODE),
447+
set(this.transitionState.finished, FALSE_NODE),
448+
set(this.isVisible, isVisible),
449+
startClock(this.clock),
450+
call([this.isVisible], ([value]: ReadonlyArray<Binary>) => {
451+
this.handleStartInteraction();
452+
453+
const { onTransitionStart } = this.props;
454+
this.noAnimationStartedSoFar = false;
455+
this.isRunningAnimation = true;
456+
onTransitionStart && onTransitionStart({ closing: !value });
457+
}),
458+
]),
459+
cond(
460+
eq(isVisible, TRUE_NODE),
461+
openingSpec.animation === 'spring'
462+
? memoizedSpring(
463+
this.clock,
464+
{
465+
...this.transitionState,
466+
velocity: this.transitionVelocity,
467+
},
468+
// @ts-ignore
469+
{
470+
...(this.openingSpecConfig as AnimatedSpringConfig),
471+
toValue: this.toValue,
472+
}
473+
)
474+
: timing(
475+
this.clock,
476+
{ ...this.transitionState, frameTime: this.frameTime },
477+
{
478+
...(this.openingSpecConfig as AnimatedTimingConfig),
479+
toValue: this.toValue,
480+
}
481+
),
482+
closingSpec.animation === 'spring'
483+
? memoizedSpring(
484+
this.clock,
485+
{
486+
...this.transitionState,
487+
velocity: this.transitionVelocity,
488+
},
489+
// @ts-ignore
490+
{
491+
...(this.closingSpecConfig as AnimatedSpringConfig),
492+
toValue: this.toValue,
493+
}
494+
)
495+
: timing(
496+
this.clock,
497+
{ ...this.transitionState, frameTime: this.frameTime },
498+
{
499+
...(this.closingSpecConfig as AnimatedTimingConfig),
500+
toValue: this.toValue,
501+
}
502+
)
503+
),
504+
cond(this.transitionState.finished, [
505+
// Reset values
506+
set(this.isSwipeGesture, FALSE_NODE),
507+
set(this.gesture, FALSE_NODE),
508+
set(this.velocity, FALSE_NODE),
509+
// When the animation finishes, stop the clock
510+
stopClock(this.clock),
511+
call([this.isVisible], ([value]: ReadonlyArray<Binary>) => {
512+
const isOpen = Boolean(value);
513+
const { onOpen, onClose } = this.props;
514+
515+
this.handleTransitionEnd();
516+
517+
if (isOpen) {
518+
onOpen(true);
519+
} else {
520+
onClose(true);
475521
}
476-
)
522+
}),
523+
]),
524+
]
477525
),
478-
cond(this.transitionState.finished, [
479-
// Reset values
480-
set(this.isSwipeGesture, FALSE_NODE),
481-
set(this.gesture, FALSE_NODE),
482-
set(this.velocity, FALSE_NODE),
483-
// When the animation finishes, stop the clock
484-
stopClock(this.clock),
485-
call([this.isVisible], ([value]: ReadonlyArray<Binary>) => {
486-
const isOpen = Boolean(value);
487-
const { onOpen, onClose } = this.props;
488-
489-
this.handleTransitionEnd();
490-
491-
if (isOpen) {
492-
onOpen(true);
493-
} else {
494-
onClose(true);
495-
}
496-
}),
497-
]),
498-
]);
526+
set(this.didMovementHappen, 0),
527+
];
499528
};
500529

501530
private extrapolatedPosition = add(
@@ -576,6 +605,7 @@ export default class Card extends React.Component<Props> {
576605
}
577606
)
578607
),
608+
onChange(this.gestureUntraversed, set(this.didMovementHappen, 1)),
579609
cond(
580610
eq(this.gestureState, GestureState.ACTIVE),
581611
[

0 commit comments

Comments
 (0)