简体   繁体   中英

React Native: Only Animate One of Several Elements

I created a React Native component that consists of 5 icons in a row. The icons are clickable and I want to animate the one that is clicked.

My problem is: When I click an icon, ALL the icons are animated. This is because they are produced in a loop and all given the same properties.

How can I set up my component so that I can somehow only animate the one icon that is pressed?

Here's the component:

import React from 'react';
import { StyleSheet, Animated, View, Text, TouchableHighlight, } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
const AnimatedIcon = Animated.createAnimatedComponent(Icon);

export class IconRow extends React.Component {  

  constructor(props) {
    super(props);
    this.state = { iconFontSize: new Animated.Value(50) };
  }

  onIconPress = (index) => {
    Animated.sequence([
      Animated.timing(this.state.iconFontSize, { toValue: 40, duration: 100 }),
      Animated.timing(this.state.iconFontSize, { toValue: 58, duration: 100 }),
      Animated.timing(this.state.iconFontSize, { toValue: 50, duration: 100 }),
    ]).start();
  }

  renderIcons() {
    var icons = [];
    for (var i = 0; i < 5; i++) {
      icons.push(
        <TouchableHighlight key={i} underlayColor="transparent" onPress={this.onIconPress.bind(this, i)}>       
          <AnimatedIcon name="heart" style={{fontSize:this.state.iconFontSize, color: "red"}} />
        </TouchableHighlight>   
      );        
    }
    return icons;
  }

  render() {
    return (
        <View style={{flexDirection: "row"}}>
          {this.renderIcons()}
        </View>
    );
  }
}

Snack: https://snack.expo.io/HJJ0Edhlz

Here you are creating the whole row of icons that's why you face this problem.

Here you have to create one icon at a time rather than creating a row. Like, take a one view for creating a icon and set in one row

<View style = {{flexDirection:'row'}} >
    <IconRow />
    <IconRow />
    <IconRow />
    <IconRow />
    <IconRow />
</View>

After that, renderIcons function in IconRow class, remove for loop and depended variable 'i' like,

icons.push(
        <TouchableHighlight key={1} underlayColor="transparent" onPress={this.onIconPress.bind(this, 1)}>       
          <AnimatedIcon name="heart" style={{fontSize:this.state.iconFontSize, color: "red"}} />
        </TouchableHighlight> 
);

or you can iterate for loop only one time like,

for (var i = 0; i < 1; i++) {
      icons.push(
        <TouchableHighlight key={1} underlayColor="transparent" onPress={this.onIconPress.bind(this, 1)}>       
          <AnimatedIcon name="heart" style={{fontSize:this.state.iconFontSize, color: "red"}} />
        </TouchableHighlight> 
      );
}

So it creates only one icon at a time and in a row. If you want to give different key than please change '1' to another value.

Now animate the one that is clicked.

Hope It will be helpful.

@Eric - I'm unable to test this locally, but I'm pretty sure it will do what you want. If it doesn't work out, please let me know and I'll remove my answer.

import React from 'react';
import { StyleSheet, Animated, View, Text, TouchableHighlight, } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
const AnimatedIcon = Animated.createAnimatedComponent(Icon);

export class IconRow extends React.Component {  

  constructor(props) {
    super(props);
    this.state = { 
      iconFontSizes: [
        new Animated.Value(50),
        new Animated.Value(50),
        new Animated.Value(50),
        new Animated.Value(50),
        new Animated.Value(50)
      ], 
    };
  }

  onIconPress = (i) => {
    Animated.sequence([
      Animated.timing(this.state.iconFontSizes[i], { toValue: 40, duration: 100 }),
      Animated.timing(this.state.iconFontSizes[i], { toValue: 58, duration: 100 }),
      Animated.timing(this.state.iconFontSizes[i], { toValue: 50, duration: 100 }),
    ]).start();
  }

  renderIcons() {
    var icons = [];

    for (var i = 0; i < this.state.iconFontSizes.length; i++) {
      icons.push(
        <TouchableHighlight key={i} underlayColor="transparent" onPress={this.onIconPress.bind(this, i)}>       
          <AnimatedIcon name="heart" style={{fontSize:this.state.iconFontSizes[i], color: "red"}} />
        </TouchableHighlight>   
      );        
    }

    return icons;
  }

  render() {
    return (
        <View style={{flexDirection: "row"}}>
          {this.renderIcons()}
        </View>
    );
  }
}

It is because you are calling the same function which has the same reference in each call. Create a new component and loop in that component so each icon will have its own function with a different reference.

this will be the component of Icon

const Icon = ({ i }) => {
    const iconFontSize = new Animated.Value(50)

    const onIconPress = () => {
        Animated.sequence([
          Animated.timing(iconFontSize, { toValue: 40, duration: 100 }),
          Animated.timing(iconFontSize, { toValue: 58, duration: 100 }),
          Animated.timing(iconFontSize, { toValue: 50, duration: 100 }),
     ]).start();}



    return (
        <TouchableHighlight key={i} underlayColor="transparent" onPress {onIconPress}>       
          <AnimatedIcon name="heart" style={{fontSize: iconFontSize, color: "red"}} />
        </TouchableHighlight>   
    )
}

Bellow will be the parent component where you will import the above Icon component and loop on that. Now in each loop you will have a component and animated functions associated only with that component.

const IconRow = ({ }) => {
    renderIcons = () => {
      var icons = [];
      for (var i = 0; i < 5; i++) {
        icons.push(<Icon i={ i } />);        
      }
      return icons;
    }

    return (
        <View style={{flexDirection: "row"}}>
          {renderIcons()}
        </View> 
    )
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM