Set reanimated values back to original after click - react-native

I have made a bit of re-usable animation code. It works great but I an unsure if it is possible to get the animations back to their original values after a button is pressed easily.
The only way I can think of it to use useShared Values to store all the before and after values and set them as required but this would involve alot of values but because the animations have already ran, there must be a way to just take them back to their original start?
The code I am using for the animations is : -
EntryAnimation.js
import React, { useEffect } from 'react';
import Animated, {
useAnimatedStyle,
useSharedValue,
useDerivedValue,
interpolate,
withDelay,
withTiming,
withSpring,
Easing,
} from 'react-native-reanimated';
export const EntryAnimation = ({
children,
index,
rotateV,
scaleV,
offsetXV,
offsetYX,
}) => {
const play = useSharedValue(play);
const progress = useDerivedValue(() => {
return play.value
? withDelay(50 * (index ?? 0), withSpring(1, { duration: 350 }))
: 0;
});
useEffect(() => {
play.value = play;
}, []);
const animatedStyle = useAnimatedStyle(() => {
// const opacity = interpolate(progress.value, [0, 1], [0, 1]);
const translateY = interpolate(progress.value, [0, 1], [0, offsetYX]);
const translateX = interpolate(progress.value, [0, 1], [0, offsetXV]);
const rotate = interpolate(progress.value, [0, 1], [0, rotateV]);
const scale = interpolate(progress.value, [0, 1], [1, scaleV]);
return {
transform: [{ translateY }, { translateX }, { rotate }, { scale }],
};
});
return <Animated.View style={animatedStyle}>{children}</Animated.View>;
};
And to use on an element in my main code, I use :-
<EntryAnimation
index={1}
rotateV={0}
scaleV={0.8}
offsetXV={0}
offsetYX={-270}>
<Animated.Image
source={{ uri: item.poster }}
style={[styles.posterImage, { zIndex: 6 }]}
/>
</EntryAnimation>
I have tried using the code below but because its in a ternary statement I am getting errors?
{animStarted ? (
<EntryAnimation
index={1}
rotateV={0}
scaleV={0.8}
offsetXV={0}
offsetYX={-270}
>
) : (
<EntryAnimation
index={1}
rotateV={0}
scaleV={1}
offsetXV={0}
offsetYX={0}
>
)}
Any ideas?

Related

react-native-reanimated interpolate width percentage

I am converting some of my default Animated stuff to react-native-reanimated v2 and can't seem to figure out how to interpolate a value to a string percentage. I also can't find anything related to this in the docs.
Using Animated from react-native I could just do:
width: widthAnim.interpolate({
inputRange: [0, 1],
outputRange: ['0%', '100%'],
})
But the interpolate() function from reanimated only takes number params, so how can I animate the width then?
interpolate(widthAnim.value, [0, 1], ['0%', '100%'])
// err: Type 'string' is not assignable to type 'number'.
You can simply use template literal
width: `${widthAnim.value * 100}%`
Check out more official example
A full example to complete the previous response:
const searchViewAnimatedWidth = useSharedValue(0);
const searchBoxAnimatedStyle = useAnimatedStyle(() => {
const interpolatedWidth = interpolate(
searchViewAnimatedWidth.value,
[0, 1],
[40, 100],
{ extrapolateRight: Extrapolation.CLAMP }
);
return {
width: withTiming(`${interpolatedWidth}%`, {
duration: 500,
useNativeDriver: false,
}),
};
});
<Animated.View
style={{...otherStyles,...searchBoxAnimatedStyle}}>
...
</Animated.View>
And in your trigger
onPress={() => {
searchViewAnimatedWidth.value = searchViewAnimatedWidth.value == 0 ? 1 : 0;
}}

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: Error while updating property 'transform' of a view managed by: RCTView in Android

I am developing some application with React Native. First, I was checking Android and IOS at the same time. After that, I made a mistake. I continued with only IOS. After a while, I had run in Android Emulator then I saw the app is crashed (Error is attached). I doubt the animations. I will not add all the codes of the project to not waste anyone's time. I have added the codes of animated CardFlip. It gives the error when only I opened the page which has the animated component. I hope that somebody might help me with this.
CardFlip Component
import React, { ReactNode, useEffect, useState } from 'react';
import { Animated, StyleSheet, ViewStyle, View, TouchableWithoutFeedback } from 'react-native';
import { isAndroid } from 'Utils/Device';
import { Helpers } from 'Theme/index';
interface CardFlipProps {
condition?: boolean
children: [ReactNode, ReactNode]
containerStyle?: ViewStyle|Array<ViewStyle>
}
const CardFlip: React.FunctionComponent<CardFlipProps> = ({ children, condition = true, containerStyle = {} }): JSX.Element => {
const [flip, setFlip] = useState(false);
const [rotate] = useState(new Animated.Value(0));
const startAnimation = ({ toRotate }) => {
Animated.parallel([
Animated.spring(rotate, {
toValue: toRotate,
friction: 8,
tension: 1,
}),
]).start();
};
useEffect(() => {
if (flip) {
startAnimation({ toRotate: 1 });
} else {
startAnimation({ toRotate: 0 });
}
}, [flip]);
useEffect(() => {
if (!condition) {
setFlip(false);
}
}, [condition]);
const interpolatedFrontRotate = rotate.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '180deg'],
});
const interpolatedBackRotate = rotate.interpolate({
inputRange: [0, 1],
outputRange: ['180deg', '0deg'],
});
const frontOpacity = rotate.interpolate({
inputRange: [0, 1],
outputRange: [1, 0.7],
});
const backOpacity = rotate.interpolate({
inputRange: [0, 1],
outputRange: [0.7, 1],
});
const perspective = isAndroid() ? undefined : 1000;
return (
<TouchableWithoutFeedback onPress={ () => condition && setFlip(!flip) }>
<View style={ containerStyle }>
<Animated.View style={ [
style.card,
{ transform: [{ rotateY: interpolatedFrontRotate }, { perspective }] },
{ opacity: frontOpacity },
] }>
{ children[0] }
</Animated.View>
<Animated.View style={ [
style.card,
{ transform: [{ rotateY: interpolatedBackRotate }, { perspective }] },
{ opacity: backOpacity },
] }>
{ children[1] }
</Animated.View>
</View>
</TouchableWithoutFeedback>
);
};
interface Styles {
card: ViewStyle
}
const style = StyleSheet.create<Styles>({
card: {
...StyleSheet.absoluteFillObject,
width: '100%',
height: '100%',
backfaceVisibility: 'hidden',
...Helpers.center,
},
});
export default CardFlip;
Just moving my comment as answer as requested. It seems that the perspective value could be null or a bug in Android so either removing it or using it conditionally should work.
in the lines
{ transform: [{ rotateY: interpolatedFrontRotate }, { perspective }] }, and
{ transform: [{ rotateY: interpolatedBackRotate }, { perspective }] }, : removing perspective or implementing it as { transform: [{ rotateY: interpolatedFrontRotate }, { perspective: perspective }] }, may solve your problem. In addition, you can rename the const perspective to avoid confusion.

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

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?