Skip to content

Instantly share code, notes, and snippets.

@jennmueng
Created February 3, 2019 15:30
Show Gist options
  • Save jennmueng/7af7fc8c2855d0c57b2531c9ab1f0589 to your computer and use it in GitHub Desktop.
Save jennmueng/7af7fc8c2855d0c57b2531c9ab1f0589 to your computer and use it in GitHub Desktop.
Snippet for the dragger animations.
// @flow
...
/*
this.animationKey is transitioned between 3 values: 0, 1, and 2.
0 is when dragger is not active.
1 is when a touch is active on dragger but not attached to a dropzone.
2 is when a touch is active on dragger and attached to dropzone.
*/
class Dragger extends PureComponent<{
deviceProps: DeviceProps // A static object that I store in the redux store that contains basic device info along with the screen dimensions.
}> {
animationKey = new Animated.Value(0);
componentWillMount() {
this.panResponder = PanResponder.create({
onPanResponderGrant: () => {
...
this.transitionState(1).start(); // Transition the 'state' to 1.
},
onPanResponderMove: (evt, gestureState) => {
if (!this.attached) {
// If not attached.
return Animated.event([
{
nativeEvent: {
pageY: this.draggerY // y coordinate of where the dragger currently is on the screen.
}
},
{
dy: this.gesturePos.y, // broadcast y pos to place/animate the component on screen.
dx: this.gesturePos.x // broadcast x pos to place/animate the component on screen.
}
])(evt, gestureState);
}
...
// is attached, still broadcast the yPos but don't animate.
return Animated.event([
{
nativeEvent: {
pageY: this.draggerY
}
}
])(evt, gestureState);
},
onPanResponderRelease: () => {
...
this.transitionState(0).start(); // transition back to 0.
...
}
});
}
transitionState = (state: 0 | 1 | 2) =>
Animated.spring(this.animationKey, {
toValue: state,
speed: 36
});
attach = (y: number, dontExpandHeight: boolean) => {
// This function is called by the 'dropzone' that is listening to the y position of the PanResponder when it is in its 'zone'
if (!this.attached) {
this.attached = true;
...
const { height, profile } = this.props.deviceProps; // profile === 1 means it's an iPhone X (XS, XR etc too)
setTimeout(() => {
const yPos = y - height + (profile === 1 ? 110 : 90) + (dontExpandHeight ? 0 : 5); // 100 and 80 for actual positions, offset is for the 5 extra dropzone area, and 10 for padding.
Animated.parallel([
Animated.spring(this.gesturePos, {
toValue: { x: 0, y: yPos }, // set the dragger's y position to the y position of the dropzone, so it 'attaches'
speed: 16
}),
this.transitionState(2) // transition the 'state' to 2.
]).start();
}, 20); // delay for less lag.
}
};
detach = () => {
if (this.attached) {
this.attached = false;
...
this.transitionState(1).start(); // 'detach' by changing the state back to 1.
}
};
panResponder: any;
render() {
const { deviceProps } = this.props;
return (
<Positioner
style={{
transform: [
{
translateY: this.positionKey.interpolate({
inputRange: [0, 1],
outputRange: [deviceProps.profile === 1 ? 100 : 80, 0]
})
}
]
}}
pointerEvents="box-none"
deviceProps={deviceProps}
>
<Container
{...this.panResponder.panHandlers}
style={{
width: this.animationKey.interpolate({
inputRange: [0, 1, 2],
outputRange: [133, 180, deviceProps.width - 40]
}),
height: this.animationKey.interpolate({
inputRange: [1.2, 2],
outputRange: [60, 80],
extrapolateLeft: 'clamp'
}),
borderRadius: this.animationKey.interpolate({
inputRange: [0, 1, 2],
outputRange: [30, 30, 14]
}),
transform: [
{
translateY: this.gesturePos.y
},
{
translateX: this.gesturePos.x
}
]
}}
>
...
</Container>
</Positioner>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment