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

fix: send events even is stack animation is vain #270

Merged
merged 3 commits into from
Oct 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion example/src/ModalStack.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import * as React from 'react';
import { Button, View, Text, Dimensions, Switch } from 'react-native';
import {
Button,
View,
Text,
Dimensions,
Switch,
TextInput,
} from 'react-native';
import {
createStackNavigator,
CardStyleInterpolators,
Expand Down Expand Up @@ -127,6 +134,10 @@ class DetailsScreen extends React.Component<NavigationStackScreenProps> {
title="Go to Details... again"
onPress={() => this.props.navigation.push('Details')}
/>
<Button
title="Go to inputs..."
onPress={() => this.props.navigation.push('Inputs')}
/>
<Button
title="Go to List"
onPress={() => this.props.navigation.navigate('List')}
Expand All @@ -143,12 +154,34 @@ class DetailsScreen extends React.Component<NavigationStackScreenProps> {
);
}
}
function InputsScreen({ navigation }: NavigationStackScreenProps) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Inputs Screen</Text>
<TextInput
defaultValue="sample"
autoFocus
style={{ backgroundColor: 'blue' }}
/>
<TextInput defaultValue="sample" style={{ backgroundColor: 'red' }} />
<TextInput defaultValue="sample" style={{ backgroundColor: 'green' }} />
<Button
title="Go to inputs... again"
onPress={() => navigation.push('Inputs')}
/>
</View>
);
}

export default createStackNavigator(
{
List: ListScreen,
Details: DetailsScreen,
Modal: Modal,
Inputs: {
screen: InputsScreen,
navigationOptions: { gestureDirection: 'vertical' },
},
},
{
initialRouteName: 'List',
Expand Down
206 changes: 118 additions & 88 deletions src/views/Stack/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ export default class Card extends React.Component<Props> {
private offset = new Value(0);
private velocityUntraversed = new Value(0);
private velocity = new Value(0);
private didMovementHappen = new Value(0);

private gestureState = new Value(0);

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

return cond(eq(this.props.current, isVisible), NOOP_NODE, [
cond(clockRunning(this.clock), NOOP_NODE, [
// Animation wasn't running before
// Set the initial values and start the clock
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,
multiply(
cond(
this.distance,
divide(this.velocity, this.distance),
FALSE_NODE
),
-1
)
),
set(this.frameTime, FALSE_NODE),
set(this.transitionState.time, FALSE_NODE),
set(this.transitionState.finished, FALSE_NODE),
set(this.isVisible, isVisible),
startClock(this.clock),
call([this.isVisible], ([value]: ReadonlyArray<Binary>) => {
this.handleStartInteraction();

const { onTransitionStart } = this.props;
this.noAnimationStartedSoFar = false;
this.isRunningAnimation = true;
onTransitionStart && onTransitionStart({ closing: !value });
}),
]),
return [
cond(
eq(isVisible, TRUE_NODE),
openingSpec.animation === 'spring'
? memoizedSpring(
this.clock,
{ ...this.transitionState, velocity: this.transitionVelocity },
// @ts-ignore
{
...(this.openingSpecConfig as AnimatedSpringConfig),
toValue: this.toValue,
}
)
: timing(
this.clock,
{ ...this.transitionState, frameTime: this.frameTime },
{
...(this.openingSpecConfig as AnimatedTimingConfig),
toValue: this.toValue,
}
eq(this.props.current, isVisible),
call(
[this.didMovementHappen, this.isVisible],
([didMovementHappen]: ReadonlyArray<Binary>) => {
if (didMovementHappen) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's the only difference. If this.props.current and isVisible are equal we need to predict a case when component was dragging but there was no movement as a result

// if we go back to the same position,
// let's pretend that whole animation happen
// for making the logic consistent
// It's especially vital for having inputs properly focused.
this.handleStartInteraction();
const { onTransitionStart } = this.props;
onTransitionStart && onTransitionStart({ closing: true });
this.handleTransitionEnd();
this.props.onOpen(true);
}
}
),
[
cond(clockRunning(this.clock), NOOP_NODE, [
// Animation wasn't running before
// Set the initial values and start the clock
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,
multiply(
cond(
this.distance,
divide(this.velocity, this.distance),
FALSE_NODE
),
-1
)
),
closingSpec.animation === 'spring'
? memoizedSpring(
this.clock,
{ ...this.transitionState, velocity: this.transitionVelocity },
// @ts-ignore
{
...(this.closingSpecConfig as AnimatedSpringConfig),
toValue: this.toValue,
}
)
: timing(
this.clock,
{ ...this.transitionState, frameTime: this.frameTime },
{
...(this.closingSpecConfig as AnimatedTimingConfig),
toValue: this.toValue,
set(this.frameTime, FALSE_NODE),
set(this.transitionState.time, FALSE_NODE),
set(this.transitionState.finished, FALSE_NODE),
set(this.isVisible, isVisible),
startClock(this.clock),
call([this.isVisible], ([value]: ReadonlyArray<Binary>) => {
this.handleStartInteraction();

const { onTransitionStart } = this.props;
this.noAnimationStartedSoFar = false;
this.isRunningAnimation = true;
onTransitionStart && onTransitionStart({ closing: !value });
}),
]),
cond(
eq(isVisible, TRUE_NODE),
openingSpec.animation === 'spring'
? memoizedSpring(
this.clock,
{
...this.transitionState,
velocity: this.transitionVelocity,
},
// @ts-ignore
{
...(this.openingSpecConfig as AnimatedSpringConfig),
toValue: this.toValue,
}
)
: timing(
this.clock,
{ ...this.transitionState, frameTime: this.frameTime },
{
...(this.openingSpecConfig as AnimatedTimingConfig),
toValue: this.toValue,
}
),
closingSpec.animation === 'spring'
? memoizedSpring(
this.clock,
{
...this.transitionState,
velocity: this.transitionVelocity,
},
// @ts-ignore
{
...(this.closingSpecConfig as AnimatedSpringConfig),
toValue: this.toValue,
}
)
: timing(
this.clock,
{ ...this.transitionState, frameTime: this.frameTime },
{
...(this.closingSpecConfig as AnimatedTimingConfig),
toValue: this.toValue,
}
)
),
cond(this.transitionState.finished, [
// Reset values
set(this.isSwipeGesture, FALSE_NODE),
set(this.gesture, FALSE_NODE),
set(this.velocity, FALSE_NODE),
// When the animation finishes, stop the clock
stopClock(this.clock),
call([this.isVisible], ([value]: ReadonlyArray<Binary>) => {
const isOpen = Boolean(value);
const { onOpen, onClose } = this.props;

this.handleTransitionEnd();

if (isOpen) {
onOpen(true);
} else {
onClose(true);
}
)
}),
]),
]
),
cond(this.transitionState.finished, [
// Reset values
set(this.isSwipeGesture, FALSE_NODE),
set(this.gesture, FALSE_NODE),
set(this.velocity, FALSE_NODE),
// When the animation finishes, stop the clock
stopClock(this.clock),
call([this.isVisible], ([value]: ReadonlyArray<Binary>) => {
const isOpen = Boolean(value);
const { onOpen, onClose } = this.props;

this.handleTransitionEnd();

if (isOpen) {
onOpen(true);
} else {
onClose(true);
}
}),
]),
]);
set(this.didMovementHappen, 0),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

plus this. We handle it only once

];
};

private extrapolatedPosition = add(
Expand Down Expand Up @@ -576,6 +605,7 @@ export default class Card extends React.Component<Props> {
}
)
),
onChange(this.gestureUntraversed, set(this.didMovementHappen, 1)),
cond(
eq(this.gestureState, GestureState.ACTIVE),
[
Expand Down