I create a class component with animation for common button:
import React, { Component } from 'react';
import { Text, TouchableOpacity, StyleSheet, Animated, Easing, View } from 'react-native';
class AnimatedPrimaryButton extends Component {
constructor(props) {
super(props);
this.state = {
toggle: false,
animated: new Animated.Value(0)
}
}
animatedButton = (toggle) => {
this.state.animated.setValue(toggle ? 1 : 0);
Animated.timing(this.state.animated, {
toValue: toggle ? 0 : 1,
duration: 250,
easing: Easing.bounce
}).start();
this.setState({ toggle: !toggle });
}
render() {
const { toggle, animated } = this.state;
const { onPress, disabled, width, height } = this.props;
return (
<TouchableOpacity
disabled={disabled}
onPress={onPress}
style={[styles.buttonStyle, { width, height }]}
>
<Animated.View style={{
// other styles
transform: [{ scale: animated.interpolate({ inputRange: [0, 1], outputRange: [0, 1]})
}
]
}}>
</Animated.View>
<Text style={[styles.textStyle, { fontSize }]}>
{children}
</Text>
</TouchableOpacity>
);
};
}
const styles = StyleSheet.create({
// some styles
});
export default AnimatedPrimaryButton;
I use the create component on other screen like:
import AnimatedPrimaryButton from '../Shared/Button/AnimatedPrimaryButton';
doSomething = () => {
// do something...
}
render() {
return (
<View>
<AnimatedPrimaryButton
onPress={() => this.doSomething()}
width={400}
height={57}
fontSize={20}
backgroundColor={confirmButtonBg}
disabled={disabledConfirmButton}
>
{I18n.t('SIGN_IN_BUTTON')}
</AnimatedPrimaryButton>
</View>
);
}
Now I want to use doSomething function and trigger animatedButton at the same time.
In some conditions my disable will switch true or false, so I try to set the code on my AnimatedPrimaryButton is not working.
onPress={() => !disabled ? this.animatedButton(toggle) : onPress}
It looks like use the props onPress under arrow function won't work.
How to use doSomething and animatedButton function on class component AnimatedPrimaryButton ?
Any help would be appreciated.
in AnimatedPrimaryButton component you can make a onPress function
onPress(){
this.props.onPress();
this. animatedButton();
}
and rest you are sending the doSomething() function correctly on onPress while calling AnimatedPrimaryButton on other screen.
Related
I have a FlatList and each item is an accordion, I m using class based react and I want to be able to toggle each accordion individually using createRef but I was unsuccessful
export default class shopingScreen extends React.component{
constractor(props){
super(props);
this.state = {
showAccordion : false
}
this.accordian = React.createRef();
}
handleListItem(item,index){
return (
<TouchableOpacity ref={this.accordian} onPress={()=> {this.setState(prevState =>({!prevState.showAccordion}) )
<Icon name='chevron-up'/>
</TouchableOpacity>
{this.state.showAccordion&&<Text>{item}</Text>
}
renderList(){
return (
<View>
<FlatList
data ={fakeList}
keyExtractor ={(item,index)=> Math.random().toString()}
renderItem={({item,index})=> this.handleListItem(item,index)}
</View>
)
}
}
Every thing gets much easier if you take handleListItem and make it its own component. Each item needs its own accordion, its own boolean state, its own ref, and its own Animation.Value (for the accordion effect). If you tried to manage all that logic in a single component it gets really messy (see AssetExample.js here)
But when separated your list component from the list item component everything is much cleaner link
// List component
import React from 'react';
import { View, FlatList, StyleSheet } from 'react-native';
import { colorGenerator } from '#phantom-factotum/colorutils';
import ListItem from './ListItem';
const fakeList = colorGenerator(5).map((color, i) => ({
color,
title: 'Item ' + (i + 1),
id: 'list-item-' + i,
}));
export default class ShoppingScreen extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<View>
<FlatList
data={fakeList}
keyExtractor={(item, index) => item.id}
renderItem={({ item, index }) => (
<ListItem item={item} index={index} />
)}
/>
</View>
);
}
}
const styles = StyleSheet.create({});
// list item
import React from 'react';
import { MaterialCommunityIcons } from '#expo/vector-icons';
import {
View,
FlatList,
TouchableOpacity,
Text,
Animated,
StyleSheet,
} from 'react-native';
const ITEM_HEIGHT = 50;
export default class ListItem extends React.Component {
constructor(props) {
super(props);
this.state = {
showAccordion: false,
};
this.itemHeight = new Animated.Value(0);
this.itemRef = React.createRef(null);
}
render() {
const showAccordion = this.state.showAccordion;
const animatedStyle = {
height: this.itemHeight.interpolate({
inputRange: [0, 1],
outputRange: [0, ITEM_HEIGHT],
}),
overflow: 'hidden',
};
return (
<TouchableOpacity
style={[
styles.itemContainer,
{ backgroundColor: this.props.item.color },
]}
ref={this.itemRef}
onPress={() => {
const nextVal = !showAccordion;
Animated.timing(this.itemHeight, {
toValue: nextVal ? 1 : 0,
duration: 200,
}).start();
this.setState((prevState) => ({
...prevState,
showAccordion: nextVal,
}));
}}>
<MaterialCommunityIcons
name={showAccordion ? 'chevron-up' : 'chevron-down'}
/>
<Animated.View style={animatedStyle}>
<Text>{this.props.item.title}</Text>
</Animated.View>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
itemContainer: {
padding: 5,
paddingVertical: 10,
marginVertical: 10,
// overflow: 'hidden',
},
});
Im creating a reusable Text component with a onFocus and onBlur animation, but when I put this in a form; the focus and blur event triggers the animation for every Input in the form... can you help me to avoid this behavior?
Here is the code if you need more details, but I think this is very clear
import React, { Component } from 'react';
import { TextInput, View, Text, Animated, StyleSheet } from 'react-native';
const animatedPlaceholder = new Animated.Value(30);
class Input extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
isFocused: false,
textLength: 0
};
}
secureTextEntry = this.props.secureTextEntry || false;
autoCapitalize = this.props.autoCapitalize || 'sentences';
keyboardType = this.props.keyboardType || 'default';
focus = () => {
this.setState({isFocused: true});
Animated.timing(animatedPlaceholder, {
toValue: 0,
duration: 300
}).start();
}
blur = () => {
this.setState({isFocused: false});
Animated.timing(animatedPlaceholder, {
toValue: 30,
duration: 300
}).start();
}
render() {
return(
<View {...this.props}>
<Animated.Text style={
this.state.isFocused ? styles.usedValue : styles.emptyValue
} > {this.props.placeholder} </Animated.Text>
<TextInput
onFocus={this.focus}
onBlur={this.blur}
autoCapitalize={this.autoCapitalize}
secureTextEntry={this.secureTextEntry}
keyboardType={this.keyboardType}
style={
styles.textInput
}
/>
</View>
);
}
}
export default Input;
I didn't quite got your question, but i created a component which animates the place holder when its focused, animated back if value is empty,
check this snack example https://snack.expo.io/#ashwith00/frowning-cookie
Code
import React, { Component } from 'react';
import { TextInput, View, Text, Animated, StyleSheet } from 'react-native';
export default class Input extends Component {
constructor(props) {
super(props);
this.state = {
id: '',
isFocused: false,
textLength: 0,
};
this.animatedPlaceholder = new Animated.Value(0);
}
secureTextEntry = this.props.secureTextEntry || false;
autoCapitalize = this.props.autoCapitalize || 'sentences';
keyboardType = this.props.keyboardType || 'default';
focus = () => {
Animated.timing(this.animatedPlaceholder, {
toValue: -40,
duration: 300,
}).start();
};
blur = () => {
if (!this.props.value) {
Animated.timing(this.animatedPlaceholder, {
toValue: 0,
duration: 300,
}).start();
}
};
render() {
const {value, onChangeText} = this.props;
return (
<View style={[ {
justifyContent: 'center'
}]}>
<Animated.Text
style={{
position: 'absolute',
transform: [{translateY: this.animatedPlaceholder}]
}}>
{' '}
{this.props.placeholder}{' '}
</Animated.Text>
<TextInput
value={value}
onChangeText={onChangeText}
onFocus={this.focus}
onBlur={this.blur}
autoCapitalize={this.autoCapitalize}
secureTextEntry={this.secureTextEntry}
keyboardType={this.keyboardType}
style={styles.textInput}
/>
</View>
);
}
}
const styles = StyleSheet.create({
usedValue: {} ,
emptyValue: {},
textInput: {
alignSelf: 'stretch',
height: 50,
borderWidth: 0.4
}
})
I am trying to animate border color in React Native, but animation doesn't work. Border color doesn't have ORIGINAL_COLOR = '#a0a0a0' neither SUCCESS_COLOR = '#008FEB', it is black. How can I make default color ORIGINAL_COLOR = '#a0a0a0' if keyboard is hidden and SUCCESS_COLOR = '#008FEB' when keyboard shows up?
const styles = StyleSheet.create({
inputContainer: {
borderBottomWidth: 1,
},
});
<Input
containerStyle={styles.inputContainer}
underlineColorAndroid="transparent"
/>;
Input.jsx
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { TextInput, Text, View, Animated, Keyboard } from 'react-native';
import styles from './styles';
const SUCCESS_COLOR = '#008FEB';
const ORIGINAL_COLOR = '#a0a0a0';
export default class Input extends Component {
constructor(props) {
super(props);
this.color = new Animated.Value(ORIGINAL_COLOR);
}
componentWillMount () {
this.keyboardWillShowSub = Keyboard.addListener('keyboardWillShow', this.keyboardWillShow);
this.keyboardWillHideSub = Keyboard.addListener('keyboardWillHide', this.keyboardWillHide);
}
componentWillUnmount() {
this.keyboardWillShowSub.remove();
this.keyboardWillHideSub.remove();
}
keyboardWillShow = (event) => {
console.log(SUCCESS_COLOR);
Animated.timing(this.color, {
duration: event.duration,
toValue: SUCCESS_COLOR,
}).start();
};
keyboardWillHide = (event) => {
console.log(ORIGINAL_COLOR);
Animated.timing(this.color, {
duration: event.duration,
toValue: ORIGINAL_COLOR,
}).start();
};
render() {
const {
value,
} = this.props;
return (
<Animated.View style={[styles.containerStyle, { borderBottomColor: this.color }]}>
<TextInput
ref="input"
{...this.props}
value={value}
/>
</Animated.View>
);
}
}
Here you go: https://snack.expo.io/#zvona/interpolation-of-color
The key is to use interpolate to change the number value into rgb value:
let borderBottomColor = this.color.interpolate({
inputRange: [0, 1],
outputRange: [ORIGINAL_COLOR, SUCCESS_COLOR]
});
I would like to call a function at the end of the function scrollTo called like that :
scrollTo({y: 0, animated: true})
but by default this function doesn't have a second parameter.
So how can i handle the end of the scroll animation to trigger an other function ?
You can use onMomentumScrollEnd as mentioned in this issue
However if you want more control over your scroll state you can implement smth like this
import React from 'react';
import { StyleSheet, Text, View, ScrollView, Button } from 'react-native';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<ScrollView
style={{ marginVertical: 100 }}
ref={this.refScrollView}
onScroll={this.onScroll}
>
<Text style={{ fontSize: 20 }}>
A lot of text here...
</Text>
</ScrollView>
<Button title="Scroll Text" onPress={this.scroll} />
</View>
);
}
componentDidMount() {
this.scrollY = 0;
}
onScroll = ({ nativeEvent }) => {
const { contentOffset } = nativeEvent;
this.scrollY = contentOffset.y;
if (contentOffset.y === this.onScrollEndCallbackTargetOffset) {
this.onScrollEnd()
}
}
onScrollEnd = () => {
alert('Text was scrolled')
}
refScrollView = (scrollView) => {
this.scrollView = scrollView;
}
scroll = () => {
const newScrollY = this.scrollY + 100;
this.scrollView.scrollTo({ y: newScrollY, animated: true });
this.onScrollEndCallbackTargetOffset = newScrollY;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
padding: 20,
},
});
I use Animated.Text for change Animation Text but it's not working properly
I also require in animation fade out old text & fade in the new text.
import React, { Component, PropTypes } from 'react';
import {
StyleSheet,
View,
Text,
Image,
Dimensions,
Animated
} from 'react-native';
import styles from './styles';
const win = Dimensions.get('window');
export default class Logo extends Component {
constructor(props) {
super(props);
this.tempText = new Animated.Value("Hello");
}
componentWillMount () {
Animated.timing(this.tempText, {
duration: 5000,
toValue: "New Text",
}).start();
};
render() {
return (
<View style={{flex:1}}>
<Animated.Text style={{color: "#9b9b9b"}}>
{this.tempText}
</Animated.Text>
</View>
);
}
}
Actual output Get - Change text after 5 Second but it's not working.please help me.
What you're trying to achieve can be done without using Animated at all, and actually, Animated isn't intended for this particular use.
Replacing the text can be done with a simple variable, and the text change can be triggered by a setTimeout.
Animated is intended for changing a numeric value, not a text value. Think of it this way - if the change is supposed to happen over a 5 second interval, what would the mid-value be?
Do this instead:
export default class Logo extends Component {
constructor(props) {
super(props);
this.state = {text: "Hello"};
}
componentDidMount () {
setTimeout(() => this.setState({text: "New Text"}), 5000);
};
render() {
return (
<View style={{flex:1}}>
<Animated.Text style={{color: "#9b9b9b"}}>
{this.state.text}
</Animated.Text>
</View>
);
}
}
My example with smoothly opacity animation.
Sorry, without fadeIn, fadeOut.
const inputRange = [0, 1, 2, 3];
const AnimatedText = Animated.createAnimatedComponent(Text);
const animationProps = {
duration: 500,
easing: Easing.out(Easing.linear),
isInteraction: false,
useNativeDriver: true,
};
class Onboarding extends PureComponent {
activeDot = 0;
animationDot = new Animated.Value(0);
toggleOnButton = () => {
Animated.timing(this.animationDot, {
toValue: this.activeDot + 1,
...animationProps,
}).start((endState) => {
if (endState.finished) {
this.activeDot = this.activeDot + 1;
}
});
}
renderButton = () => {
const opacityNext = this.animationDot.interpolate({
inputRange,
outputRange: [1, 1, 1, 0]
});
const opacityGetStarted = this.animationDot.interpolate({
inputRange,
outputRange: [0, 0, 0, 1]
});
return (
<TouchableOpacity style={styles.button} onPress={this.toggleOnButton}>
<AnimatedText style={[styles.buttonText, { opacity: opacityNext }]}>
Next
</AnimatedText>
<AnimatedText style={[styles.buttonText, {
top: normalize(isIOS ? 12 : 8), position: 'absolute', opacity: opacityGetStarted
}]}
>
Get started
</AnimatedText>
</TouchableOpacity>
);
}
}