Skip to content

Commit

Permalink
feat(animations): merge pull request #60 from CursedWizard/master
Browse files Browse the repository at this point in the history
feat(animations): use transforms with translateX/translateY instead of dynamic margin.
  • Loading branch information
Luke Brandon Farrell authored Aug 30, 2021
2 parents 84824f9 + 7c4b4cf commit d26d5ec
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 80 deletions.
201 changes: 123 additions & 78 deletions src/RNNDrawer.tsx
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,14 @@ export enum DirectionType {
top = 'top',
}

interface Point {
moveX: number;
moveY: number;
}

interface IState {
sideMenuOpenValue: any;
sideMenuOverlayOpacity: any;
sideMenuOpenValue: Animated.Value;
sideMenuOverlayOpacity: Animated.Value;
sideMenuSwipingStarted: boolean;
sideMenuIsDismissing: boolean;
screenHeight: number;
Expand All @@ -97,6 +102,7 @@ interface IProps {
fadeOpacity: number;
drawerScreenWidth: number | string;
drawerScreenHeight: number | string;
animateDrawerExpanding?: boolean;
style: any;
}

Expand Down Expand Up @@ -124,7 +130,7 @@ interface DrawerReverseDirectionInterface {
}

interface SwipeMoveInterface {
value: number;
value: Point;
direction: string;
}

Expand All @@ -142,13 +148,16 @@ class RNNDrawer {
private readonly drawerWidth: number;
private readonly drawerHeight: number;
private readonly drawerOpenedValues: DrawerDirectionValuesInterface;
private readonly initialValues: DrawerDirectionValuesInterface;
private panResponder: PanResponderInstance;
private animatedDrawer: any;
private animatedOpacity: any;
private unsubscribeSwipeStart: any;
private unsubscribeSwipeMove: any;
private unsubscribeSwipeEnd: any;
private unsubscribeDismissDrawer: any;
private animatedDrawer!: Animated.CompositeAnimation;
private animatedOpacity!: Animated.CompositeAnimation;
private unsubscribeSwipeStart!: () => void;
private unsubscribeSwipeMove!: () => void;
private unsubscribeSwipeEnd!: () => void;
private unsubscribeDismissDrawer!: () => void;
private panningStartedPoint: Point = { moveX: 0 , moveY: 0 };
private startedFromSideMenu: boolean = false;

static defaultProps = {
animationOpenTime: 300,
Expand All @@ -158,6 +167,7 @@ class RNNDrawer {
fadeOpacity: 0.6,
drawerScreenWidth: '80%',
drawerScreenHeight: '100%',
animateDrawerExpanding: true
};

/**
Expand Down Expand Up @@ -186,9 +196,12 @@ class RNNDrawer {
_evt: GestureResponderEvent,
_gestureState: PanResponderGestureState,
) => {
const { dx } = _gestureState;
const { dx, dy } = _gestureState;

return Math.abs(dx) > 5;
if (this.props.direction === DirectionType.left || this.props.direction === DirectionType.right)
return Math.abs(dx) > 5;
else
return Math.abs(dy) > 5;
},
onMoveShouldSetPanResponderCapture: (
_evt: GestureResponderEvent,
Expand All @@ -198,7 +211,9 @@ class RNNDrawer {
_evt: GestureResponderEvent,
_gestureState: PanResponderGestureState,
) => {
dispatch('SWIPE_START');
const { moveX, moveY } = _gestureState;

dispatch('SWIPE_START', {moveX, moveY});
},
onPanResponderRelease: (
_evt: GestureResponderEvent,
Expand All @@ -221,10 +236,10 @@ class RNNDrawer {
_evt: GestureResponderEvent,
_gestureState: PanResponderGestureState,
) => {
const { moveX } = _gestureState;
const { moveX, moveY } = _gestureState;
const direction = this.props.direction || 'left';

dispatch('SWIPE_MOVE', { value: moveX, direction });
dispatch('SWIPE_MOVE', { value: { moveX, moveY }, direction });
},
});

Expand Down Expand Up @@ -263,20 +278,20 @@ class RNNDrawer {
this.drawerOpenedValues = {
left: 0,
right: this.screenWidth - this.drawerWidth,
top: 0,
top: this.drawerHeight - this.screenHeight,
bottom: this.screenHeight - this.drawerHeight,
};

const initialValues: DrawerDirectionValuesInterface = {
this.initialValues = {
left: -this.drawerWidth,
right: this.screenWidth,
top: -this.drawerHeight,
top: -this.screenHeight,
bottom: this.screenHeight,
};

/** Component State */
this.state = {
sideMenuOpenValue: new Animated.Value(initialValues[props.direction]),
sideMenuOpenValue: new Animated.Value(this.initialValues[props.direction]),
sideMenuOverlayOpacity: new Animated.Value(0),
sideMenuSwipingStarted: false,
sideMenuIsDismissing: false,
Expand Down Expand Up @@ -310,13 +325,16 @@ class RNNDrawer {
*/
componentDidMount() {
/** Props */
const { direction, fadeOpacity } = this.props;
const { direction, fadeOpacity, animateDrawerExpanding } = this.props;

if (typeof animateDrawerExpanding !== 'undefined' && !animateDrawerExpanding)
this.startedFromSideMenu = true;

// Animate side menu open
this.animatedDrawer = Animated.timing(this.state.sideMenuOpenValue, {
toValue: this.drawerOpenedValues[direction],
duration: this.props.animationOpenTime,
useNativeDriver: false,
useNativeDriver: true,
});

// Animate outside side menu opacity
Expand All @@ -325,7 +343,7 @@ class RNNDrawer {
{
toValue: fadeOpacity,
duration: this.props.animationOpenTime,
useNativeDriver: false,
useNativeDriver: true,
},
);
}
Expand All @@ -339,7 +357,7 @@ class RNNDrawer {
this.registerListeners();

// If there has been no Swiping, and this component appears, then just start the open animations
if (!this.state.sideMenuSwipingStarted) {
if (!this.state.sideMenuSwipingStarted && this.props.animateDrawerExpanding) {
this.animatedDrawer.start();
this.animatedOpacity.start();
}
Expand All @@ -356,6 +374,20 @@ class RNNDrawer {
dispatch('DRAWER_CLOSED');
}

onOrientationChange = ({window}: any) => {
const screenHeight = window.height;

this.setState({ screenHeight });

// Apply correct position if opened from right
if (this.props.direction === 'right') {
// Calculates the position of the drawer from the left side of the screen
const alignedMovementValue = window.width - this.drawerWidth;

this.state.sideMenuOpenValue.setValue(alignedMovementValue);
}
}

/**
* Registers all the listenrs for this component
*/
Expand All @@ -364,22 +396,13 @@ class RNNDrawer {
const { direction, fadeOpacity } = this.props;

// Adapt the drawer's size on orientation change
Dimensions.addEventListener('change', ({ window }) => {
const screenHeight = window.height;

this.setState({ screenHeight });

// Apply correct position if opened from right
if (this.props.direction === 'right') {
// Calculates the position of the drawer from the left side of the screen
const alignedMovementValue = window.width - this.drawerWidth;

this.state.sideMenuOpenValue.setValue(alignedMovementValue);
}
});
Dimensions.addEventListener('change', this.onOrientationChange);

// Executes when the side of the screen interaction starts
this.unsubscribeSwipeStart = listen('SWIPE_START', () => {
this.unsubscribeSwipeStart = listen('SWIPE_START', (value: Point) => {
this.panningStartedPoint.moveX = value.moveX;
this.panningStartedPoint.moveY = value.moveY;

this.setState({
sideMenuSwipingStarted: true,
});
Expand All @@ -389,46 +412,62 @@ class RNNDrawer {
this.unsubscribeSwipeMove = listen(
'SWIPE_MOVE',
({ value, direction: swipeDirection }: SwipeMoveInterface) => {
if (swipeDirection === 'left') {
// Calculates the position of the drawer from the left side of the screen
const alignedMovementValue = value - this.drawerWidth;
// Calculates the percetage 0 - 100 of which the drawer is open
const openedPercentage = Math.abs(
(Math.abs(alignedMovementValue) / this.drawerWidth) * 100 - 100,
);
// Calculates the opacity to set of the screen based on the percentage the drawer is open
const normalizedOpacity = Math.min(
openedPercentage / 100,
fadeOpacity,
);

// Does allow the drawer to go further than the maximum width
if (this.drawerOpenedValues[direction] > alignedMovementValue) {
// Sets the animation values, we use this so we can resume animation from any point
this.state.sideMenuOpenValue.setValue(alignedMovementValue);
// Cover special case when we are swiping from the edge of the screen
if (this.startedFromSideMenu) {
if (direction === "left" && value.moveX < this.drawerWidth) {
this.state.sideMenuOpenValue.setValue(value.moveX - this.drawerWidth);
const normalizedOpacity = Math.min(
(value.moveX / this.drawerWidth) * fadeOpacity,
fadeOpacity,
);
this.state.sideMenuOverlayOpacity.setValue(normalizedOpacity);
}
if (direction === "right" && (this.screenWidth - value.moveX) < this.drawerWidth) {
this.state.sideMenuOpenValue.setValue(value.moveX);
const normalizedOpacity = Math.min(
((this.screenWidth - value.moveX) / this.drawerWidth) * fadeOpacity,
fadeOpacity,
);
this.state.sideMenuOverlayOpacity.setValue(normalizedOpacity);
}

return;
}

// Calculates the translateX / translateY value
let alignedMovementValue = 0;
// To swap the direction if needed
let directionModifier = 1;
// Whether we use the height of the drawer or the width
let drawerDimension = this.drawerWidth;

if (swipeDirection === 'left') {
alignedMovementValue = value.moveX - this.panningStartedPoint.moveX;
} else if (swipeDirection === 'right') {
// Works out the distance from right of screen to the finger position
const normalizedValue = this.screenWidth - value;
// Calculates the position of the drawer from the left side of the screen
const alignedMovementValue = this.screenWidth - normalizedValue;
// Calculates the percetage 0 - 100 of which the drawer is open
const openedPercentage = Math.abs(
(Math.abs(normalizedValue) / this.drawerWidth) * 100,
);
// Calculates the opacity to set of the screen based on the percentage the drawer is open
const normalizedOpacity = Math.min(
openedPercentage / 100,
alignedMovementValue = this.panningStartedPoint.moveX - value.moveX;
directionModifier = -1;
} else if (swipeDirection === 'bottom') {
alignedMovementValue = this.panningStartedPoint.moveY - value.moveY;
directionModifier = -1;
drawerDimension = this.drawerHeight;
} else if (swipeDirection === 'top') {
alignedMovementValue = value.moveY - this.panningStartedPoint.moveY;
drawerDimension = this.drawerHeight;
}

// Calculates the percentage 0 - 1 of which the drawer is open
const openedPercentage = Math.abs(drawerDimension + alignedMovementValue) / drawerDimension;
// Calculates the opacity to set of the screen based on the percentage the drawer is open
const normalizedOpacity = Math.min(
openedPercentage * fadeOpacity,
fadeOpacity,
);
);

// Does allow the drawer to go further than the maximum width
if (this.drawerOpenedValues[direction] < alignedMovementValue) {
// Sets the animation values, we use this so we can resume animation from any point
this.state.sideMenuOpenValue.setValue(alignedMovementValue);
this.state.sideMenuOverlayOpacity.setValue(normalizedOpacity);
}
// Does not allow the drawer to go further than the maximum width / height
if (0 > alignedMovementValue) {
// Sets the animation values, we use this so we can resume animation from any point
this.state.sideMenuOpenValue.setValue(this.drawerOpenedValues[direction] + alignedMovementValue * directionModifier);
this.state.sideMenuOverlayOpacity.setValue(normalizedOpacity);
}
},
);
Expand All @@ -440,7 +479,11 @@ class RNNDrawer {
const reverseDirection: DrawerReverseDirectionInterface = {
right: 'left',
left: 'right',
top: 'bottom',
bottom: 'top'
};
// In case the drawer started by dragging the edge of the screen reset the flag
this.startedFromSideMenu = false;

if (swipeDirection === reverseDirection[direction]) {
this.animatedDrawer.start();
Expand Down Expand Up @@ -472,7 +515,7 @@ class RNNDrawer {
* Removes all the listenrs from this component
*/
removeListeners() {
Dimensions.removeEventListener('change', () => {});
Dimensions.removeEventListener('change', this.onOrientationChange);
if (this.unsubscribeSwipeStart) this.unsubscribeSwipeStart();
if (this.unsubscribeSwipeMove) this.unsubscribeSwipeMove();
if (this.unsubscribeSwipeEnd) this.unsubscribeSwipeEnd();
Expand All @@ -494,8 +537,8 @@ class RNNDrawer {
/** Variables */
const animatedValue =
direction === DirectionType.left || direction === DirectionType.right
? { marginLeft: sideMenuOpenValue }
: { marginTop: sideMenuOpenValue };
? { translateX: sideMenuOpenValue }
: { translateY: sideMenuOpenValue };

return (
<View
Expand All @@ -517,7 +560,9 @@ class RNNDrawer {
{
height: this.state.screenHeight,
width: this.drawerWidth,
...animatedValue,
transform:[
animatedValue
]
},
]}
>
Expand Down Expand Up @@ -547,15 +592,15 @@ class RNNDrawer {
const closeValues: DrawerDirectionValuesInterface = {
left: -this.drawerWidth,
right: this.screenWidth,
top: -this.drawerHeight,
top: -this.screenHeight,
bottom: this.screenHeight,
};

// Animate side menu close
Animated.timing(this.state.sideMenuOpenValue, {
toValue: closeValues[direction],
duration: this.props.animationCloseTime,
useNativeDriver: false,
useNativeDriver: true,
}).start(() => {
Navigation.dismissOverlay(this.props.componentId);
this.setState({ sideMenuIsDismissing: false });
Expand All @@ -565,7 +610,7 @@ class RNNDrawer {
Animated.timing(this.state.sideMenuOverlayOpacity, {
toValue: 0,
duration: this.props.animationCloseTime,
useNativeDriver: false,
useNativeDriver: true,
}).start();
}
}
Expand Down
Loading

0 comments on commit d26d5ec

Please sign in to comment.