React Native - Unfold collapsed header when user moves this - react-native

I have a header that is collapsed when scrolling in a flatlist, I achieved this with the following code
const positionY = React.useRef(new Animated.Value(0)).current;
const onScroll = Animated.event(
[{ nativeEvent: { contentOffset: { y: positionY } } }],
{
useNativeDriver: true,
}
);
const translateY = positionY.interpolate({
inputRange: [0, headerHeight],
outputRange: [0, -(headerHeight - 64)],
extrapolate: 'clamp',
});
snack whit complete code https://snack.expo.io/#gustperz/collapsible-header
I would like to be able to unfold the header on demand when the user pull down on the sticky header, without scrolling over the list.

I did Double animation
By scrolling + drawer behavior
const beyondLength = 100000
const onScroll = Animated.event(
[{ nativeEvent: { contentOffset: { y: positionY.current } } }],
{ useNativeDriver: true },
);
const translateY = positionY.current.interpolate({
inputRange: [0, headerHeight + 100, beyondLength, beyondLength+1, beyondLength+150],
outputRange: [
0,
-(headerHeight - 64),
-(headerHeight - 64),
-(headerHeight - 65),
0,
],
extrapolate: 'clamp',
});
const panResponder = React.useMemo(
() =>
PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderMove: (event, gestureState) => {
positionY.current.setValue(gestureState.dy + beyondLength);
},
}),
[],
);
expo

Related

React Native Animated If function

const OFFSET = useRef(new Animated.Value(0)).current;
const LOGO_ANIM_INTERPOLATE = OFFSET.interpolate({
inputRange: [-100,0],
outputRange: [ WINDOW_WIDTH/2-35,0],
extrapolate: 'clamp',
})
if (LOGO_ANIM_INTERPOLATE === WINDOW_WIDTH/2-35) {
alert('OK');
} else {
}
-----------
OnScroll in ScrollView
onScroll={
Animated.event(
[{nativeEvent: {
contentOffset: {
y: OFFSET,
},
},
}],
{ useNativeDriver: false },
)}
--------
An object move to the right accordingly Scroll y-offset.
If object get x-position = WINDOW_WIDTH/2-35 I want alert('OK');
IF function not working! Why?

React Native - Animated.spring() isn't called as it should

The output I want :
When I click on the card, it is flipped and it set isValidated to true. When I click a second time, is it flipped back to its original position.
I don't understand why I need to trigger onPressCard two times in order for the card to flip.
Console output are real strange too :
'rotationValue 0', when pressed 1 time
'rotationValue3 0 rotationValue 0' when pressed a second time
Here is the code :
interface RotativeCardProps {
card: RotativeCardType,
index: number
}
const Rotative = ({ card, index }: RotativeCardProps) => {
const [isValidated, setIsValidated] = useState<boolean>(false);
let rotationValue = 0;
const animatedValue = new Animated.Value(0);
animatedValue.addListener(({ value }) => { rotationValue = value; });
const onPressCard = () => {
if (!isValidated) { setIsValidated(true); }
flipCard();
};
const flipCard = () => {
console.log('rotationValue', rotationValue);
if (rotationValue > 89) {
console.log('rotationValue2', rotationValue);
Animated.spring(animatedValue, {
toValue: 0,
friction: 8,
tension: 10,
useNativeDriver: true,
}).start();
} else {
console.log('rotationValue3', rotationValue);
Animated.spring(animatedValue, {
toValue: 180,
friction: 8,
tension: 10,
useNativeDriver: true,
}).start();
}
};
const frontInterpolate = animatedValue.interpolate({ inputRange: [0, 180], outputRange: ['0deg', '180deg'] });
const backInterpolate = animatedValue.interpolate({ inputRange: [0, 180], outputRange: ['180deg', '360deg'] });
const frontAnimatedStyle = { transform: [{ rotateY: frontInterpolate }] };
const backAnimatedStyle = { transform: [{ rotateY: backInterpolate }] };
```
flipCard();
calling this function onPressCard should be removed and moved to useEffect
useEffect(()=>{
flipCard();
},[isValidated])
this fill fix the issue as set state is blocking the animation

How corretly debounce react native animation

I'm building an translateY e opacity component to animate my submit button that appears at the end of the form. The problem is that everytime the prop changes too fast the button stops working
[...]
class ShyButton extends PureComponent {
constructor(props) {
super(props)
this.state = { height: 0, visible: props.isVisible }
this.animatedValue = new Animated.Value(props.isVisible ? 1 : 0)
}
componentDidUpdate(prevProps, prevState) {
const { isVisible } = this.props
if (prevProps.isVisible !== isVisible) {
if (isVisible) { this.toggleVisibility(prevState, true) }
Animated.timing(this.animatedValue,
{ toValue: isVisible ? 1 : 0, duration: 800, useNativeDriver: true }
).start(() => { if (!isVisible) { this.toggleVisibility(prevState, false) } })
}
}
toggleVisibility = (prevState, visible) => this.setState({ ...prevState, visible })
onLayout = event => this.setState({ height: event.nativeEvent.layout.height })
render() {
const { isVisible, style, children, ...props } = this.props
const { height, visible } = this.state
const animatedStyle = {
opacity: this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
}),
transform: [
{
translateY: this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [height, 0],
extrapolate: 'clamp'
})
}
]
}
const combinedStyle = StyleSheet.flatten([style, { opacity: 0, transform: [{ translateY: 0 }] }])
const animatingStyle = StyleSheet.flatten([combinedStyle, animatedStyle])
return (
<Animated.View onLayout={this.onLayout} style={visible ? animatingStyle : combinedStyle} {...props}>
{visible ? children : null}
</Animated.View>
)
}
}
[...]
The biggest problem is when auto-correct is used on the first word the text length goes to zero (test used to show or hide it) and back too fast, blocking the animation
Imgur

Transition Animation for react native(react-navigation and redux)

I followed the below code to implement Screen transition animation but it doesn't seem to be working. I am using react-navigation(1.0.0-beta.26) with redux.
const transitionConfig = () => {
return {
transitionSpec: {
duration: 750,
easing: Easing.out(Easing.poly(4)),
timing: Animated.timing,
useNativeDriver: true,
},
screenInterpolator: sceneProps => {
const { position, layout, scene, index, scenes } = sceneProps
const toIndex = index
const thisSceneIndex = scene.index
const height = layout.initHeight
const width = layout.initWidth
const translateX = position.interpolate({
inputRange: [thisSceneIndex - 1, thisSceneIndex, thisSceneIndex + 1],
outputRange: [width, 0, 0]
})
const translateY = position.interpolate({
inputRange: [0, thisSceneIndex],
outputRange: [height, 0]
})
const slideFromRight = { transform: [{ translateX }] }
const slideFromBottom = { transform: [{ translateY }] }
const lastSceneIndex = scenes[scenes.length - 1].index
if (lastSceneIndex - toIndex > 1) {
if (scene.index === toIndex) return
if (scene.index !== lastSceneIndex) return { opacity: 0 }
return slideFromBottom
}
return slideFromRight
},
}}
export const AppNavigator = StackNavigator({
Screen1: {screen: Screen1, navigationOptions:{header:null}},
Screen2: {screen: Screen2, navigationOptions:{header:null}},
}, {
initialRouteName: 'Screen1',
transitionConfig,
});
class App extends Component {
return (
<View>
<AppNavigator />
</View>
)
}
I am navigating using the redux dispatch as mentioned below
this.props.navigation.dispatch({ type: 'Screen2' })

React Native: react-native-maps. Animate active marker depending on ScrollView offset

I am using react-native-maps and want to gradually animate scale of active and inactive markers depending on the ScrollView x offset. (the below image describes it better)
My approach is to create new Animated.Value for each marker and Interpolate ScrollView offset for each marker:
//Container
constructor(props) {
super(props);
this.state = {
...
scale: [],
opacity: []
}
}
componentWillReceiveProps(props) {
if (props.places.length) {
const scale = [];
const opacity = [];
props.places.forEach((xx, ii) => {
this.state[`scale${ii}`] = new Animated.Value(0.5);
this.state[`scale${ii}`] = this._scrollX.interpolate({
inputRange: [vw * (ii - 1), vw * ii, vw * (ii + 1)],
outputRange: [0.5, 1, 0.5],
extrapolate: 'clamp'
});
scale.push(this.state[`scale${ii}`]);
this.state[`opacity${ii}`] = new Animated.Value(0);
this.state[`opacity${ii}`] = this._scrollX.interpolate({
inputRange: [vw * (ii - 1), vw * ii, vw * (ii + 1)],
outputRange: [0, 1, 0],
extrapolate: 'clamp'
});
opacity.push(this.state[`opacity${ii}`]);
});
this.setState({
scale,
opacity,
});
}
render() {
return (
<PlacesMap scale={this.state.scale} opacity={this.state.opacity} />
<ScrollView onScroll={this._onScroll} ... />
)
}
_onScroll = Animated.event(
[{ nativeEvent: { contentOffset: { x: this._scrollX }}}],
{ useNativeDriver: true }
);
//PlacesMap
....
render {
return (
<MapView.Animated>
{ places &&
places.map((place, ii) => {
return (
<PlaceMarker
key={place.id.toString()}
place={place}
scale={scale[ii]}
opacity={opacity[ii]}
/>
)
})
}
</MapView.Animated>
)}
Maybe there is a more elegant (performant) approach that I don't know, where I don't need to create new Animated.Value and Interpolators for each marker and with which I can get the same result?