I am making a quiz. And all options will render in for loop.
Expected Behaviour:
When I click on an option, if it is the wrong answer then it should change the background color to red and it should shake.
Below is the code I am trying.
import React, { Component } from "react";
import {
View,
Text,
TouchableWithoutFeedback,
Animated,
Easing
} from "react-native";
class MCQOptions extends Component {
state = {
optionSelectedStatus: 0 // 0: unselected, 1: correct, -1: wrong
};
constructor() {
super();
this.animatedValue = new Animated.Value(0);
this.shakeAnimValue = new Animated.Value(0);
}
onOptionSelected(i) {
// this.props.showNextQuestion();
var answer = this.props.answer;
if (answer == i) {
this.setState({ optionSelectedStatus: 1 });
this.showCorrectAnimation();
} else {
this.setState({ optionSelectedStatus: -1 });
this.showErrorAnimation();
}
}
showErrorAnimation() {
this.shakeAnimValue.setValue(0);
Animated.timing(this.shakeAnimValue, {
toValue: 1,
duration: 300,
easing: Easing.linear
}).start();
}
showCorrectAnimation() {}
getOptions() {
var options = [];
var optionSelectedStyle = styles.optionUnselected;
var optionShadowStyle = styles.optionShadow;
if (this.state.optionSelectedStatus == 1) {
optionSelectedStyle = styles.optionCorrect;
optionShadowStyle = null;
} else if (this.state.optionSelectedStatus == -1) {
optionSelectedStyle = styles.optionWrong;
optionShadowStyle = null;
}
const marginLeft = this.shakeAnimValue.interpolate({
inputRange: [0, 0.2, 0.4, 0.6, 0.8, 0.9, 1],
outputRange: [0, -10, 10, -10, 10, -10, 0]
});
for (var i = 0; i < this.props.options.length; i++) {
options.push(
<TouchableWithoutFeedback
onPress={this.onOptionSelected.bind(this, this.props.indexes[i])}
key={"options_" + i}
>
<View style={styles.optionBox}>
<View style={optionShadowStyle} />
<Animated.Text
style={[
styles.option,
optionSelectedStyle,
{ marginLeft: marginLeft }
]}
key={"option" + i}
>
{this.props.options[i]}
</Animated.Text>
</View>
</TouchableWithoutFeedback>
);
}
return options;
}
render() {
const marginTop = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [100, 0]
});
const opacity = this.animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 1]
});
return (
<Animated.View style={{ marginTop: marginTop, opacity: opacity }}>
{this.getOptions()}
</Animated.View>
);
}
// Animations
componentDidMount() {
this.slideUpOptionsContainer();
}
componentWillReceiveProps() {
this.slideUpOptionsContainer();
this.setState({ optionSelectedStatus: 0 });
}
slideUpOptionsContainer() {
this.animatedValue.setValue(0);
Animated.timing(this.animatedValue, {
toValue: 1,
duration: 300,
easing: Easing.linear
}).start();
}
}
const styles = {
optionBox: {
margin: 5
},
optionsContainer: {
marginTop: 100
},
option: {
padding: 10,
textAlign: "center",
borderRadius: 10,
overflow: "hidden",
width: "100%"
},
optionUnselected: {
backgroundColor: "#FFF"
},
optionWrong: {
backgroundColor: "red"
},
optionCorrect: {
backgroundColor: "green"
},
optionShadow: {
backgroundColor: "rgba(255,255,255,0.85)",
position: "absolute",
width: "100%",
height: "100%",
left: -5,
top: 5,
borderRadius: 10
}
};
export default MCQOptions;
The above code animating(shake) all the options (Which is proper according to the login written), and I am stuck how to make only the clicked option get animated instead all?
Edited:
Parent component with props feed:
class MCQ extends Component<{}> {
render() {
var options = ["yes", "no", "can't define"];
var indexes = [1,2,3];
var answer = 1;
optionsObj = <MCQOptions
options={options}
indexes={indexes}
answer={answer}/>;
return (
<View style={styles.container} >
<View style={styles.optionsContainer}>
{optionsObj}
</View>
</View>
);
}
}
const styles = {
container: {
flex: 1,
backgroundColor: "blue",
paddingTop: 20,
justifyContent: 'flex-start',
padding: 20
},
};
export default MCQ;
Second EDIT: Trying to simplify problem.
Below is the simplified code with zero props. I want to animate clicked element only.
import React, { Component } from "react";
import {
View,
Text,
TouchableWithoutFeedback,
Animated,
Easing
} from "react-native";
class MCQOptions extends Component {
constructor() {
super();
this.shakeAnimValue = new Animated.Value(0);
}
showErrorAnimation() {
this.shakeAnimValue.setValue(0);
Animated.timing(this.shakeAnimValue, {
toValue: 1,
duration: 300,
easing: Easing.linear
}).start();
}
getOptions() {
const marginLeft = this.shakeAnimValue.interpolate({
inputRange: [0, 0.2, 0.4, 0.6, 0.8, 0.9, 1],
outputRange: [0, -10, 10, -10, 10, -10, 0]
});
var options = [];
for (var i = 0; i < 4; i++) {
options.push(
<TouchableWithoutFeedback
onPress={this.showErrorAnimation.bind(this)}
key={"options_" + i}
>
<View style={styles.optionBox}>
<Animated.Text style={[
styles.option,
{ marginLeft: marginLeft }
]}
key={"option" + i}
>
{"Option "+i}
</Animated.Text>
</View>
</TouchableWithoutFeedback>
);
}
return options;
}
render() {
return (
<View style={{ marginTop: 100}}>
{this.getOptions()}
</View>
);
}
}
const styles = {
optionBox: {
margin: 5
},
optionsContainer: {
marginTop: 100
},
option: {
padding: 10,
textAlign: "center",
borderRadius: 10,
overflow: "hidden",
width: "100%"
},
optionUnselected: {
backgroundColor: "#FFF"
},
optionWrong: {
backgroundColor: "red"
},
};
export default MCQOptions;