React-native-svg: Can't animate fill color for path via setNativeProps

Created on 3 May 2017  路  3Comments  路  Source: react-native-svg/react-native-svg

I'm attempting to animate the fill color of some custom paths. Doing so via normal props isn't performant enough, so I'm trying to use requestAnimationFrame and setNativeProps

this.state.fillColor = this.state.opacityAnim.interpolate({
      inputRange: [0, 0.20, 0.40, 0.80, 1],
      outputRange: ['rgb(147, 189, 186)', 'rgb(235, 154, 64)', 'rgb(226, 117, 58)', 'rgb(220, 85, 52)', 'rgb(220, 85, 52)'],
    })

I have this interpolation running nicely, and I can do this without error (but it's too slow):

<Svg>
  <Path
  ref={c => this._path = c}
  d={pos.path}
  fill={this.state.fillColor.__getAnimatedValue()}
  />
</Svg>

What I can't do however is this. This fails:

animate() {
    requestAnimationFrame(() => {
      if (this._path && this.state.fillColor) {
        this._path.setNativeProps({
          fill: this.state.fillColor.__getAnimatedValue(),
        })
      }
      this.animate()
    })
  }

I'm getting this error:
screen shot 2017-05-03 at 12 54 44 pm

2017-05-03 13:00:59.789 [error][tid:main][RCTConvert.m:56] Error setting property 'fill' of RNSVGPath with tag #1223: JSON value 'rgba(147, 189, 186, 1)' of type NSString cannot be converted to NSArray
2017-05-03 13:00:59.792 [error][tid:main][RCTConvert+RNSVG.m:102] Error setting property 'fill' of RNSVGPath with tag #1223: Too few elements in array (expected at least 5): (null)

What might I be doing wrong? Is it possible to do this?

I also attemped to not use the __getAnimatedValue() and change the <Path/> to <AnimatedPath/> with const AnimatedPath = Animated.createAnimatedComponent(Path) but then I get this error:
screen shot 2017-05-03 at 1 13 27 pm

I've seen some examples of people animating the path using setNativeProps, but not the fill color.

Any advice greatly appreciated, thanks!

Most helpful comment

Hey, I had success with this thanks to @ethantran's examples. Here's a full example.

```Logo.js
import React, { PureComponent } from 'react';
import { Animated } from 'react-native';
import PropTypes from 'prop-types';
import Svg, { Path } from 'react-native-svg';
import extractBrush from 'react-native-svg/lib/extract/extractBrush';

export default class extends PureComponent {
static propTypes = {
fill: PropTypes.string,
path: PropTypes.string.isRequired,
};
static defaultProps = {
fill: 'rgba(0, 33, 71, 1)',
};

constructor(props) {
super(props);
this.lastFill = this.props.fill;

this.state = {
  fillValue: new Animated.Value(0),
};

this.state.fillValue.addListener(() => {
  const { fill } = this.props;
  const { fillValue } = this.state;
  const color = fillValue.interpolate({
    inputRange: [0, 1],
    outputRange: [this.lastFill, fill],
  });
  this.path.setNativeProps({
    fill: extractBrush(color.__getAnimatedValue()),
  });
});

}

animate = () => {
const { fillValue } = this.state;
Animated.timing(fillValue, {
toValue: 1,
duration: 350,
}).start(({ finished }) => {
if (finished) {
this.lastFill = this.props.fill;
this.state.fillValue.setValue(0);
}
});
};

componentDidMount() {
this.animate();
}

componentDidUpdate() {
this.animate();
}

render() {
return (
d={this.props.path}
ref={ref => (this.path = ref)}
fill={this.state.lastFill}
/>

);
}
}
```

All 3 comments

You have to use extractBrush in lib/extract to animate the fill.

setNativeProps = (props) => {
            props.propList = getPropList(props, this.prevProps);
            if (props.fill) {
                props.fill = extractBrush(props.fill);
            }
            if (props.stroke) {
                props.stroke = extractBrush(props.stroke);
            }
            this._component && this._component.setNativeProps(props);
        }

I provide more examples at #55

Hey, I had success with this thanks to @ethantran's examples. Here's a full example.

```Logo.js
import React, { PureComponent } from 'react';
import { Animated } from 'react-native';
import PropTypes from 'prop-types';
import Svg, { Path } from 'react-native-svg';
import extractBrush from 'react-native-svg/lib/extract/extractBrush';

export default class extends PureComponent {
static propTypes = {
fill: PropTypes.string,
path: PropTypes.string.isRequired,
};
static defaultProps = {
fill: 'rgba(0, 33, 71, 1)',
};

constructor(props) {
super(props);
this.lastFill = this.props.fill;

this.state = {
  fillValue: new Animated.Value(0),
};

this.state.fillValue.addListener(() => {
  const { fill } = this.props;
  const { fillValue } = this.state;
  const color = fillValue.interpolate({
    inputRange: [0, 1],
    outputRange: [this.lastFill, fill],
  });
  this.path.setNativeProps({
    fill: extractBrush(color.__getAnimatedValue()),
  });
});

}

animate = () => {
const { fillValue } = this.state;
Animated.timing(fillValue, {
toValue: 1,
duration: 350,
}).start(({ finished }) => {
if (finished) {
this.lastFill = this.props.fill;
this.state.fillValue.setValue(0);
}
});
};

componentDidMount() {
this.animate();
}

componentDidUpdate() {
this.animate();
}

render() {
return (
d={this.props.path}
ref={ref => (this.path = ref)}
fill={this.state.lastFill}
/>

);
}
}
```

I wish I had looked for this issue before, I just posted my solution on #55. I used __getValue().

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kevinejohn picture kevinejohn  路  41Comments

msageryd picture msageryd  路  32Comments

uriklar picture uriklar  路  40Comments

obetomuniz picture obetomuniz  路  38Comments

webschik picture webschik  路  80Comments