React Native:如何为特定组件设置动画?

React Native: How to animate a particular component?

我正在做一个测验。所有选项都将在 for 循环中呈现。

预期行为:

当我点击一个选项时,如果它是错误的答案那么它应该将背景颜色更改为红色并且它应该摇晃。

下面是我正在尝试的代码。

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;

上面的代码动画(摇动)了所有的选项(根据所写的登录是正确的),我卡住了如何只让点击的选项动画而不是所有的?

已编辑:

带有 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;

第二次编辑: 正在尝试简化问题。

以下是零道具的简化代码。我只想为点击的元素设置动画。

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;

由于您想分别为它们设置动画,因此它们不能绑定到同一个 Animated 对象。您必须将它们设为多个,例如:

示例:

export class App extends Component {
    constructor() {
      super();
      this.getOptions = this.getOptions.bind(this);
      this.originalOptions = [0,1,2,3];
      this.shakeAnimations = this.originalOptions.map( (i) => new Animated.Value(0) );
    }
    showErrorAnimation(i) {
      this.shakeAnimations[i].setValue(0);
      Animated.timing(this.shakeAnimations[i], {
        toValue: 1,
        duration: 300,
        easing: Easing.linear
      }).start();
    }

    getOptions() {

      var options = this.originalOptions.map( (i) => {
            const marginLeft = this.shakeAnimations[i].interpolate({
                inputRange: [0, 0.2, 0.4, 0.6, 0.8, 0.9, 1],
                outputRange: [0, -10, 10, -10, 10, -10, 0]
            });
            return (
            <TouchableWithoutFeedback
                onPress={() => this.showErrorAnimation(i)}
                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"
    },
  };

结果: