How corretly debounce react native animation - react-native

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

Related

React native confirmation code field values

Hi I'm using this package as component it shwos and get vlaues but how can I get input values from root component.
in root page
import AnimatedVerification from '../../components/AnimatedVerification/AnimatedVerification';
inside this code is all comes from this example
I just want to get this 4 number values from root component how to make it. Thank you
Expo
/*
*/
import {Animated, Image, SafeAreaView, Text, View} from 'react-native';
import React, {useState} from 'react';
import {
CodeField,
Cursor,
useBlurOnFulfill,
useClearByFocusCell,
} from 'react-native-confirmation-code-field';
import styles, {
ACTIVE_CELL_BG_COLOR,
CELL_BORDER_RADIUS,
CELL_SIZE,
DEFAULT_CELL_BG_COLOR,
NOT_EMPTY_CELL_BG_COLOR,
} from './styles';
const {Value, Text: AnimatedText} = Animated;
const CELL_COUNT = 4;
const source = {
uri:
'https://user-images.gitx.com/4xxxxxxx.png',
};
const animationsColor = [...new Array(CELL_COUNT)].map(() => new Value(0));
const animationsScale = [...new Array(CELL_COUNT)].map(() => new Value(1));
const animateCell = ({hasValue, index, isFocused}) => {
Animated.parallel([
Animated.timing(animationsColor[index], {
useNativeDriver: false,
toValue: isFocused ? 1 : 0,
duration: 250,
}),
Animated.spring(animationsScale[index], {
useNativeDriver: false,
toValue: hasValue ? 0 : 1,
duration: hasValue ? 300 : 250,
}),
]).start();
};
const AnimatedVerification = () => {
const [value, setValue] = useState('');
const [props, getCellOnLayoutHandler] = useClearByFocusCell({
value,
setValue,
});
const renderCell = ({index, symbol, isFocused}) => {
const hasValue = Boolean(symbol);
const animatedCellStyle = {
backgroundColor: hasValue
? animationsScale[index].interpolate({
inputRange: [0, 1],
outputRange: [NOT_EMPTY_CELL_BG_COLOR, ACTIVE_CELL_BG_COLOR],
})
: animationsColor[index].interpolate({
inputRange: [0, 1],
outputRange: [DEFAULT_CELL_BG_COLOR, ACTIVE_CELL_BG_COLOR],
}),
borderRadius: animationsScale[index].interpolate({
inputRange: [0, 1],
outputRange: [CELL_SIZE, CELL_BORDER_RADIUS],
}),
transform: [
{
scale: animationsScale[index].interpolate({
inputRange: [0, 1],
outputRange: [0.2, 1],
}),
},
],
};
// Run animation on next event loop tik
// Because we need first return new style prop and then animate this value
setTimeout(() => {
animateCell({hasValue, index, isFocused});
}, 0);
return (
<AnimatedText
key={index}
style={[styles.cell, animatedCellStyle]}
onLayout={getCellOnLayoutHandler(index)}>
{symbol || (isFocused ? <Cursor /> : null)}
</AnimatedText>
);
};
return (
<SafeAreaView style={styles.root}>
<CodeField
ref={props.ref}
{...props}
value={value}
onChangeText={setValue}
cellCount={CELL_COUNT}
rootStyle={styles.codeFiledRoot}
keyboardType="number-pad"
textContentType="oneTimeCode"
renderCell={renderCell}
/>
</SafeAreaView>
);
};
export default AnimatedVerification;
If I understand, you want to get the value of the child component in parent component ?
Your CodeField component has a ref in the parent component ref={props.ref}.
In your CodeField component define a method who return the inputs values. And in the parent you can call the method with the ref.

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

Onscroll native event update all same type wrapper components

I have a wrapper flat list component used in react navigation library.
This component is in different stacknavigation tab to handle the header's animation.
import React, { Component } from "react";
import { Constants } from 'expo';
// import PropTypes from "prop-types";
import {
Animated,
Dimensions,
// PanResponder,
// Platform,
// ScrollView,
StyleSheet,
FlatList,
// ScrollView,
// StatusBar,
// Text,
// TouchableWithoutFeedback,
// View
} from "react-native";
// import Icon from "react-native-vector-icons/Ionicons";
// Get screen dimensions
const { width, height } = Dimensions.get("window");
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
const HEADER_HEIGHT= 40;
const FILTER_HEIGHT= 50;
const STATUS_BAR_HEIGHT = Constants.statusBarHeight;
const NAVBAR_HEIGHT = HEADER_HEIGHT+FILTER_HEIGHT-2;
const scrollAnim = new Animated.Value(0);
const offsetAnim = new Animated.Value(0);
export default class AnimatedFlatListComp extends React.PureComponent {
// Define state
state = {
scrollAnim,
offsetAnim,
clampedScroll: Animated.diffClamp(
Animated.add(
scrollAnim.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolateLeft: 'clamp',
}),
offsetAnim,
),
0,
// NAVBAR_HEIGHT - STATUS_BAR_HEIGHT,
HEADER_HEIGHT //i mede this one cuz the code abode not work is the value 40
),
};
componentWillUnmount() {
console.log('smontoooo');
// this._isMounted = false;
// Don't forget to remove the listeners!
// this.state.scrollAnim.removeAllListeners();
// this.state.offsetAnim.removeAllListeners();
this._disableListener();
}
componentDidMount() {
this._clampedScrollValue = 0;
this._offsetValue = 0;
this._scrollValue = 0;
this._enableLister()
this._handleScroll()
}
_onMomentumScrollBegin = () => {
console.log('_onMomentumScrollBegin');
clearTimeout(this._scrollEndTimer);
}
_onScrollEndDrag = () => {
this._scrollEndTimer = setTimeout(this._onMomentumScrollEnd, 250);
}
_onMomentumScrollEnd = () => {
console.log('_onMomentumScrollEnd');
console.log(this._scrollValue, NAVBAR_HEIGHT, this._clampedScrollValue, (NAVBAR_HEIGHT - STATUS_BAR_HEIGHT) / 2);
const toValue = this._scrollValue > NAVBAR_HEIGHT &&
this._clampedScrollValue > (NAVBAR_HEIGHT - STATUS_BAR_HEIGHT) / 2
? this._offsetValue + NAVBAR_HEIGHT
: this._offsetValue - NAVBAR_HEIGHT;
Animated.timing(this.state.offsetAnim, {
toValue,
duration: 350,
useNativeDriver: true,
}).start();
}
_handleScroll = () => this.props._handleScroll(this.state.clampedScroll)
// _handleScroll = event => {
// const { y } = event.nativeEvent.contentOffset;
// // // console.log(y);
// this.setState({ scrollOffset: y }, () => {
// this.props._handleScroll(this.state.clampedScroll)
// });
//
// };
_scrollToTop = () => {
console.log('_scrollToTop');
if (!!this.flatListRef) {
// this.flatListRef.getNode().scrollTo({ y: 0, animated: true });
this.flatListRef.getNode().scrollToOffset({ offset: 0, animated: true });
}
};
_enableLister = () => {
// this._firstMountFunction();
this.state.scrollAnim.addListener(({ value }) => {
// This is the same calculations that diffClamp does.
const diff = value - this._scrollValue;
this._scrollValue = value;
this._clampedScrollValue = Math.min(
Math.max(this._clampedScrollValue + diff, 0),
NAVBAR_HEIGHT - STATUS_BAR_HEIGHT,
);
});
this.state.offsetAnim.addListener(({ value }) => {
this._offsetValue = value;
});
}
_disableListener = () => {
this.state.scrollAnim.removeAllListeners();
this.state.offsetAnim.removeAllListeners();
}
_keyExtractor = (item, index) => index.toString();
// _onScroll = event => {
//
// }
render() {
return (
<AnimatedFlatList
{...this.props}
ref={(ref) => { this.flatListRef = ref; }}
showsVerticalScrollIndicator={false}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: this.state.scrollAnim}}}],
{
useNativeDriver: true,
// listener: this._handleScroll
},
)}
// onScroll={this._onScroll}
removeClippedSubviews={true}
keyExtractor={this._keyExtractor}
onMomentumScrollBegin={this._onMomentumScrollBegin}
onMomentumScrollEnd={this._onMomentumScrollEnd}
onScrollEndDrag={this._onScrollEndDrag}
scrollEventThrottle={1}
/>
);
}
}
this is the parent
_handleScroll = clampedScroll => this.setState({ clampedScroll: clampedScroll })
render(){
const { clampedScroll } = this.state;
//
const navbarTranslate = clampedScroll.interpolate({
inputRange: [0, NAVBAR_HEIGHT - STATUS_BAR_HEIGHT],
outputRange: [0, -(NAVBAR_HEIGHT - STATUS_BAR_HEIGHT)],
extrapolate: 'clamp',
});
return (
<AnimatedFlatList
// debug={true}
ref={(ref) => { this.flatListRef = ref; }}
maxToRenderPerBatch={4}
contentContainerStyle={{
paddingTop: NAVBAR_HEIGHT+STATUS_BAR_HEIGHT,
}}
data={this.state.dataSource}
renderItem={
({item, index}) =>
<CardAgenda
item={JSON.parse(item.JSON)}
ChangeSelectedEvent={this.ChangeSelectedEvent}
colorTrail={JSON.parse(item.colorTrail)}
// _sendBackdata={this._getChildrenCategoryData}
searchData={JSON.parse(item.searchData)}
NumAncillary={item.NumAncillary}
indexItinerary={item.id}
index={index}
/>
}
ListEmptyComponent={this._emptyList}
ItemSeparatorComponent={() => <View style={{width: width-40, backgroundColor: 'rgba(0,0,0,0.1)', height: 1, marginTop: 20, marginLeft: 20, marginRight: 20}}/>}
_handleScroll={this._handleScroll}
/>
)}
Its working fine but onscroll event triggers the this.state.scrollAnim variable of ALL wrappers.
I mean if i scroll up the first AnimatedFlatList the header goes up but also the different one header in new navigation page goes up.
The correct behavior must be that all header must be independent to the own flatlist.
Thanks in advance
This is because you are setting up a reference to the state when creating animated Values obj. You should not keep them as constants outside your class boundary.
Try remove your following constants.
const scrollAnim = new Animated.Value(0);
const offsetAnim = new Animated.Value(0);
Then define them inside the constructor.
export default class AnimatedFlatListComp extends React.PureComponent {
constructor(props){
super(props);
this.scrollAnim = new Animated.Value(0);
this.offsetAnim = new Animated.Value(0);
// Define state
state = {
scrollAnim: this.scrollAnim,
offsetAnim: this.offsetAnim,
clampedScroll: Animated.diffClamp(
Animated.add(
this.scrollAnim.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
extrapolateLeft: 'clamp',
}),
this.offsetAnim,
),
0,
// NAVBAR_HEIGHT - STATUS_BAR_HEIGHT,
HEADER_HEIGHT //i mede this one cuz the code abode not work is
the value 40
),
};
}

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?