My code looks something like this currently:
<View>
<TextInput placeholder='PlaceholderText'>
</TextInput>
</View>
I want to make a TextInput component that has an opacity animation on click (exactly like TouchableOpacity changes opacity on click).
I tried wrapping the TextInput inside TouchableOpacity, but it doesn't work since the touchable component surrounds text input. Is there a standard React Native or StyleSheet way of doing this or would I have to manually create an animation to mimic that effect?
Simply wrap your TextInput in a View element and animate the View's opacity color from the onFocs event of the TextInput. TextInput doesn't have the opacity attribute, therefore the additional view is required for your goal.
Following code may give you an idea how to solve it. Have to admit, that I haven't tested the code, it may contain some error, so use it carefully.
// First create an animated value to use for the view's opacity.
const textInputAnimOpacity = useRef(new Animated.Value(1.0)).current;
// create a method to set the opacity back to 1.0
const showTxtInput = () => {
Animated.timing(textInputAnimOpacity, {
toValue: 1.0, // value to reach
duration: 250 // time in ms
}).start();
};
// this animated method differs from the first one by (obviously the value 0.7)
//and the callback part that goes into the start()-method
const blurTxtInput = () => {
Animated.timing(textInputAnimOpacity, {
toValue: 0.7, // value to reach
duration: 250 // time in ms
}).start(({ finished }) => {
showTxtInput(); // Callback after finish, setting opacity back to 1.0
});
};
/*Set the opacity to the value, we want to animate*/
<View style={{opacity:textInputAnimOpacity}}>
/* call blurTxtInput to set the value to 0.7 and again to 1.0 once it reaches 0.7*/
<TextInput onPressIn={blurTxtInput} placeholder='PlaceholderText'>
</TextInput>
</View>
If you just want to set opacity, make your styles change using the onPressIn and onPressOut props:
const [pressed, setPressed] = useState(false);
// in render
<TextInput
onPressIn={() => setPressed(true)}
onPressOut={() => setPressed(false)}
style={pressed ? styles.textInputPressed : styles.textInput}
// ...
/>
If you need the changes to animate, you can do that with the built-in RN Animated component or react-native-reanimated, using the same props to trigger the animations.
Related
I am developing a react-native project.
I have a ScrollView in MyComponent, the content of ScrollView consists of :
a MySubComponent,
a Text
a Image component
All the content/data of above components have dynamic length or height. So, I would like adjust the content height of my ScrollView on the fly at runtime.
To achieve that my plan is to disable automaticallyAdjustContentInsets of the ScrollView as you can see from my below code snippet. Then, have a state variable to hold the latest value of contentInsetBottom. But I am not sure how can I calculate the height of child components so that I can call setContentInsetBottom(totalHeight) to update the content height of my ScrollView .
(I am pretty sure my plan will work if I know how to calculate the height of each child component of my ScrollView.)
Anyone can guide me a bit?
const MyComponent = ({myText, image}) => {
// I have a state of the contentInsetBottom for the ScrollView
const [contentInsetBottom, setContentInsetBottom] = useState(0);
// how can I get the height of each children component, sum them up & call setContentInsetBottom(totalHeight) here ?
return (
<ScrollView
automaticallyAdjustContentInsets={false}
contentInset={{top: 0, bottom: contentInsetBottom}}
style={styles.scrollView}
contentContainerStyle={styles.contentContainer}>
<MySubComponent />
<Text>{myText}</Text>
<Image source={{uri: image?.uri}}>
</ScrollView>)
}
Wrap all content inside the <ScrollView> in a <View>. Then use onLayout to get the height of that parent View.
const handleScrollContentLayout = (e) => {
const { height } = e.nativeEvent.layout
setScrollLayoutHeight(height)
}
...
<View onLayout={handleScrollContentLayout}>
{ /* scrollView content... */ }
</View>
Then you can use the scrollLayoutHeight as per your needs to set the height at runtime.
Hi everyone I just want to animate text and image. This code I wrote is animating at the beginnig.When I update the state, animation is gone just changing image and text. How can I fix it.
//the package I installed from npm
import * as Animatable from 'react-native-animatable';
//the images array changes by state
var arr=[{image:require('./assets/img1.png')},{image:require('./assets/img2.png')},{image:require('./assets/img3.png')}];
//The initial state
state = {
uri: require('./assets/img1.png'),
message:"-",
img_rank:0
};
//In onpress method of TocuhableOpacity
<TouchableOpacity
onPress={() => {
const randomIndex = Math.floor(Math.random() * 3);
//state of message
this.setState({ message: "Act1" });
//state of image rank from the array
this.setState({ img_rank: randomIndex })
...
Showing the text and image
//it's showing the image with animation at the beginning
<Animatable.Image source={arr[this.state.img_rank].image} animation="zoomInUp">
</Animatable.Image>
//the text but it's not animating
<Animatable.Text animation="zoomInUp">
<Text>{this.state.message} </Text>
</Animatable.Text>
Thank you.
Firstly, get your animated component's ref into a variable in your class like animatedImageRef.
<Animatable.Image
ref={r => this.animatedImageRef = r}
source={arr[this.state.img_rank].image}
animation={this.state.img_rank ? 'zoomInUp' : undefined}>
</Animatable.Image>
Then, in your onPress function, use this ref to animate your component again.
this.animatedImageRef.startAnimation(500, 0, () => { })
I am using Flatlist in my rn project and when I push new data into my flatlist, my item 1 will automatically move from position A to position B. But my question is I don't want it to just change the position, I want to use animation to move my item(from position A to position B). How can I implement that? Thank you!
Please check the demo picture and video from the link down below:
https://photos.app.goo.gl/WypswNyA38A2EAPQA
https://photos.app.goo.gl/Ev1RYMduDj7mxrHn7
You can use Animated component to do the animation. As per your attached video, 2 steps animation comes into play, one which pushes the items up in the list and another one which increases the opacity of the list item. A simple approach would be to add the list item with height 0 and increase the height to desired height using animation, this will complete the first step. Once the first step is completed, control the opacity to go from 0 to 1.
Next, you need to start the animation when the list item is added to the list, componentDidMount is the right place to do so. Please consider the following component which does the above steps.
import React from 'react';
import { Animated } from 'react-native';
class AnimatedListItem extends React.Component {
constructor(...props) {
super(...props);
this.state = {
height: new Animated.Value(0),
opacity: new Animated.Value(0)
};
}
componentDidMount() {
Animated.sequence([
Animated.timing(
this.state.height,
{
toValue: this.props.height,
duration: this.props.duration || 1000
}
),
Animated.timing(
this.state.opacity,
{
toValue: 1,
duration: this.props.duration || 1000
}
)
]).start();
}
render() {
const { height, opacity } = this.state;
return (
<Animated.View
style={{
...this.props.style,
height: height,
opacity: opacity
}}
>
{this.props.children}
</Animated.View>
);
}
}
export default AnimatedListItem;
In the above snippet, two animations are passed to Animated.sequence([...]) method to animate one after the other.
You can now use the above component in the renderItem method like
renderItem = () => {
return (
<AnimatedListItem height={50} duration={800} style={...}>
/* render actual component here */
</AnimatedListItem>
);
}
Hope this will help!
Note: This is a bare minimum example to achieve what you are looking for.
Can I set the position of react native action button to fixed on a flat list?
I have added an action button but when I scroll the flat list, the action button hides!
Thanks, advance.
You'll want to use the Animated API
First you need to create a Flatlist that you can animate. This is easily done with Animated.createAnimatedComponent():
import { Flatlist, Animated } from 'react-native'
const AnimatedFlatlist = Animated.createAnimatedComponent(Flatlist)
Now in your parent component constructor create a new Animated.Value. This will be the value you use to animate your component to scroll along with your AnimatedFlatlist:
constructor(props) {
super(props)
this.state = { translateY: new Animated.Value(0)}
}
Then wrap the component you want to be fixed in an Animated.View and make sure you add a transform to the component style, passing it the animated value you created :
<Animated.View style={{ transform: [{ translateY: this.state.translateY }]}}>
{YourFixedComponent}
</Animated.View>
Finally, you need to attach an Animated.event to your AnimatedFlatlist. This is done using the onScroll prop:
<AnimatedFlatlist
data={...}
renderItem={...}
keyExtractor={...}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: this.state.translateY}}}],
{useNativeDriver: true}
)}
scrollEventThrottle={1}
/>
Now your element with move along with your Flatlist. This is a basic example, and you will probably need to mess with interpolation and CSS positioning to get it exactly as you want it.
Encountered this issue recently when I work with react native animated API.
As the image shows, a card component is positioned at top left corner, its flip animation state is controlled by the rotateY value, moving animation is controlled by translateX and translateY values.
It seems the rotation pivot point always been set to the card's original position. After the card has been moved (changing the translateX and translateY value), the card flip rotation animates reference its original position.
It there a way to adjust the rotation pivot point? Alternatively, is there a way to animate component's position instead of translation? Thanks.
Got it working finally. Turns out you can animate the component position change without using the translate property, by adding a listener to the animated value and updating the component state accordingly:
in the constructor, setup card component initial position and cardPos animated value.
in the componentDidMount function, attach listeners to the animated values. when animated values change, update the component state.
in the render function set the component root value style to position:"absolute" and actual position sync to the values in component's state.
constructor(props){
super(props);
// set card initial position as component state
this.state = {
cardPosX: this.props.position.x,
cardPosY: this.props.position.y
};
this.flipAnimatedValue = new Animated.Value(
this.props.isFacingUp ? 180 : 0
);
this.flipAnimatedInterpolate = this.flipAnimatedValue.interpolate({
inputRange: [0, 90, 180],
outputRange: ["0deg", "90deg", "0deg"]
});
// create animated value for card Position X and Y
this.cardPosXAnimatedValue = new Animated.Value(this.props.position.x);
this.cardPosYAnimatedValue = new Animated.Value(this.props.position.y);
}
componentDidMount() {
// addListener for cardPos Animated Value
// when animated values change, update the component state
this.cardPosXAnimatedValue.addListener(({ value }) => {
this.setState({ cardPosX: value });
});
this.cardPosYAnimatedValue.addListener(({ value }) => {
this.setState({ cardPosY: value });
});
}
render(){
return (
<View
style={{
width: this.cardWidth,
height: this.cardHeight,
position: "absolute",
top: this.state.cardPosY, //card position sync with animated value
left: this.state.cardPosX
}}
>
... //child components
</View>
);
}