Can it uses with react-native's Animated
Yes, it can. Probably you want to create Animated variants of each component (with Animated.createAnimatedComponent) to useAnimated.Value` objects as properties.
I'm struggling to use Animated to animate the 'd' property on a Path component. It looks like this is because Animated.Values can only be numerical. So I reverted to trying to use setNativeProps on the Path component to set 'd', but an error is thrown.
Can you tell me if setNativeProps is supported on Paths? Thanks!
I think d property is atm impossible to do with Animated as you only can interpolate from number to string. I've not tried the setNativeProps route, maybe you can show your code? I could try and figure out what's happening in native. Btw on Android or iOS?
Thanks for your response. I tried a few approaches this morning which I'll share with you. There approaches are as follows:
setStatesetNativePropsusing setState works fine, but I just thought there would be a more performant way of doing it that having to re-render the tree each frame, which is why I attempted using setNativeProps. The error message I get for the second and third approach are the same, i'm assuming this is because Animated.Value just uses setNativeProps internally. This is the error message. It looks like it's on the native side (iOS) but I haven't investigated it any further.

Here are the code snippets I used for each:
using setState
export default class testing extends Component {
constructor(props) {
super(props)
this._origin = { x: 100, y: 100 }
this._radius = 50
this.state = {
arcEndX: Math.sin(0) * this._radius,
arcEndY: Math.cos(0) * this._radius - this._radius,
largeArcFlag: Math.sin(0) >= 0 ? 0 : 1
}
this.setArcEndFromRadians = this.setArcEndFromRadians.bind(this)
}
setArcEndFromRadians(radians) {
this.setState({
arcEndX: Math.sin(radians) * this._radius,
arcEndY: Math.cos(radians) * this._radius - this._radius,
largeArcFlag: Math.sin(radians) >= 0 ? 0 : 1
})
}
componentDidMount() {
let radians = 0
let timer = setInterval(() => {
radians += 0.02
this.setArcEndFromRadians(radians)
}, 16)
}
render() {
return (
<View>
<Svg
height="200"
width="200">
<Path
d={ `M ${this._origin.x},${this._origin.y} l 0,50 a 50,50 0 ${this.state.largeArcFlag} 0 ${this.state.arcEndX},${this.state.arcEndY} z` }/>
</Svg>
</View>
)
}
}
using setNativeProps (for better performance, error thrown)
export default class testing extends Component {
constructor(props) {
super(props)
this._origin = { x: 100, y: 100 }
this._radius = 50
this._arc = {
arcEndX: Math.sin(0) * this._radius,
arcEndY: Math.cos(0) * this._radius - this._radius,
largeArcFlag: Math.sin(0) >= 0 ? 0 : 1
}
this.setArcEndFromRadians = this.setArcEndFromRadians.bind(this)
}
setArcEndFromRadians(radians) {
let arcEndX = Math.sin(radians) * this._radius
let arcEndY = Math.cos(radians) * this._radius - this._radius
let largeArcFlag = Math.sin(radians) >= 0 ? 0 : 1
this._thePath.setNativeProps({
d: `M ${this._origin.x} ${this._origin.y} l 0 50 a 50,50 0 ${largeArcFlag} 0 ${arcEndX} ${arcEndY} z`
})
}
componentDidMount() {
let radians = 0
let timer = setInterval(() => {
radians += 0.02
this.setArcEndFromRadians(radians)
}, 16)
}
render() {
return (
<View>
<Svg
height="200"
width="200">
<Path
ref={ ref => this._thePath = ref }
d={ `M ${this._origin.x},${this._origin.y} l 0,50 a 50,50 0 ${this._arc.largeArcFlag} 0 ${this._arc.arcEndX},${this._arc.arcEndY} z` }/>
</Svg>
</View>
)
}
}
using Animated interpolation (simplified to just drawing a line, rather than a circle, error thrown)
let AnimatedPath = Animated.createAnimatedComponent(Path)
export default class testing extends Component {
constructor(props) {
super(props)
this._origin = { x: 100, y: 100 }
this._radians = new Animated.Value(0)
this._arcX = this._radians.interpolate({
inputRange: [
0,
100
],
outputRange: [
`M ${this._origin.x},${this._origin.y} l 0,0`,
`M ${this._origin.x},${this._origin.y} l 100,100`
]
})
}
componentDidMount() {
Animated.spring(this._radians, {
toValue: Math.PI,
friction: 5,
tension: 135
}).start()
}
render() {
return (
<View>
<Svg
height="200"
width="200">
<AnimatedPath
d={ this._arcX }/>
</Svg>
</View>
)
}
}
Components has method setNativeProps but he doesnt work. Is there any other normal ways animate svg's part, other then just to wrap each in Svg?
I was able to animate an Svg object by animating a react-native View component wrapped around it.
import React, { Component } from 'react';
import {
View,
Animated,
Easing
} from 'react-native';
import Svg, {
Line,
G,
Text
} from 'react-native-svg';
class MovingHand extends Component {
constructor(props){
super(props)
const { remainder, duration } = props.timer
const start = duration - remainder
this.state = {
wind: new Animated.Value(start),
duration,
start
}
}
componentDidMount() {
Animated.timing(
this.state.wind,
{
toValue: 1,
duration: this.state.start * 1000,
easing: Easing.none,
}
).start()
}
render() {
const {
width,
height,
radius,
strokeWidth,
} = this.props;
const { start, duration } = this.state
const motionStyle = {
transform: [{
rotate: this.state.wind.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']
})
}]
}
return (
<Animated.View style={motionStyle}>
<Svg width={width} height={height}>
<Line
x1={radius}
y1={0.20 * radius}
x2={radius}
y2={radius}
stroke='brown'
strokeWidth={2 * strokeWidth}
strokeLinecap='round'
/>
</Svg>
</Animated.View>
)
}
}
export default MovingHand;
Getting a very similar issue when attempting to animate using color values

At the risk of posting to more than one animation issue, is there any way to animate an SVG element without wrapping it in a Animated View?
I'm trying to adjust the radius of a circle without using scale transform in the styles, because I later want to adjust the start and end points of a SVG Line.
Here's what I'm running into:
// var AnimatedCircle = Animated.createAnimatedComponent(Circle); <-- Done before.
<AnimatedCircle cx="250" cy="250" r={this.state.circleRadius} fill="black" />
With the following animation on mount:
// this.state.circleRadius = new Animated.Value(50) <-- Starting value
Animated.spring(
this.state.circleRadius,
{ toValue: 100, friction: 3 }
).start();
And getting the following error:

@joshuapinter
Try:
<AnimatedCircle
cx="250"
cy="250"
r={`${this.state.circleRadius}`}
fill="black"
/>
@anhtuank7c Thanks for the suggestion but now I get this error message:
Invalid float: "[object Object]"

@grubstarstar @leebradley I'm having exactly the same issue. Did you ever find a workable solution? Thanks!
I opened this before finding this thread:
https://github.com/react-native-community/react-native-svg/issues/326
@udfalkso I found that simply using setState as per my first example above was sufficiently performant in the end for my needs. @Lyonsclay solution of animating the wrapping View is good if you can accomplish what you need using that but I couldn't in my case. I found that once I had my solution built in release mode the performance was more acceptable.
It would be great to be able to use with Animated though, especially if it could hand over the animation details to the native thread using "useNative" https://facebook.github.io/react-native/blog/2017/02/14/using-native-driver-for-animated.html it seems like this is where animation belongs.
Thanks @grubstarstar. I'm already doing the wrapping View trick for opacity changes, but I can't do that for fill color.
@udfalkso nah, true. Is setState too slow? I managed a pretty smooth radial animation just using that.
@grubstarstar setState's performance really depends on what else you're doing in the component's methods, like render. For simple components, setState may be sufficient but it quickly loses value on more complex components.
The advantage of using Animated is that it completes the animation on the UI thread so is more or less unaffected by what else your component is trying to do.
@grubstarstar Yes unfortunately in my case I'm doing this for many paths at the same time (20+) and things grind to a halt.
@grubstarstar @joshuapinter I managed to get it working with some fairly small changes.
Check out the PR. I hope it works for you guys too: https://github.com/react-native-community/react-native-svg/pull/328
The only gotcha is that you still have to use this.myAnim.__getAnimatedValue() when sending the value into setNativeProps
animate = () => {
if (this._path && this.fillColor) {
this._path.setNativeProps({
fill: this.fillColorInterpolation.__getAnimatedValue(),
})
}
requestAnimationFrame(this.animate.bind(this))
}
@udfalkso good work! I'll take a look at that...
Hi guys,
I just wanted to follow up from my comment.
Found a workable solution using addListener and using setNativeProps. A little messy but works none-the-less and is quite performant.
Here's a simplified version of the solution:
constructor(props) {
super(props);
this.state = { circleRadius: new Animated.Value(50) };
this.state.circleRadius.addListener( (circleRadius) => {
this._myCircle.setNativeProps({ r: circleRadius.value.toString() });
});
setTimeout( () => {
Animated.spring( this.state.circleRadius, { toValue: 100, friction: 3 } ).start();
}, 2000)
}
render() {
return(
<Svg height="400" width="400">
<AnimatedCircle ref={ ref => this._myCircle = ref } cx="250" cy="250" r="50" fill="black" />
</Svg>
)
}
And the resulting animation:

And this is being rendered on a very complex component where using setState isn't fluid at all.
@joshuapinter Thanks. I implemented your animated circle and tried to extend the example to a Polygon (in an attempt to animate a point in the polygon) but received the following error:
undefined is not a function (evaluating '_this3.root.getNativeElement()')
Here's my source for both the broken polygon and the working circle: https://gist.github.com/dk0r/76761b6cc5a3069b9443fabb81801a55

If you guys need examples on how to get most of the components animated, check my comment at #55
@joshuapinter Your solution working good while update r, i tried to update rotate but nothing changed :(
@dk0r I found same error with Polyline :(
@anhtuank7c There are only certain attributes that are exposed to setNativeProps. I'm not sure what they are but if anybody finds a good list of them, please post it here!
Do y鈥檃ll think we鈥檒l ever be able to offload animations on the native side via useNativeDriver?
Its fully possible, just have to integrate with the driver and the declarative animation passing ;) You wanna take a shot at it?
@joshuapinter For the native props you can check the view managers https://github.com/react-native-community/react-native-svg/tree/master/ios/ViewManagers
@msand I鈥檇 like to possibly take a stab at it. :) I鈥檓 not very familiar with how this might work though. Would every SVG prop need to be implemented for natively driving animation?
@zachgibson I don't really know. But, I think somehow on the native side we need to allow animated values as props, and then register onchange listeners to actually set the values on the shadow nodes and invalidate up the tree to queue a re-rendering of the bitmap. It might be a really minor change, just have to figure out how to glue the listener and the animated/shadow nodes. @vjeux Could you mentor here a bit, would be much appreciated!
I鈥檓 sorry but I haven鈥檛 been working on react native for 1.5 years now... i won鈥檛 be able to spend time on this :(
Alright, no probs. I managed to animate opacity of a Svg Rect with the native driver in iOS, by removing this one method 馃槂 https://github.com/react-native-community/react-native-svg/blob/152e839126e66a708a9492d203ef7fb4302e1030/ios/ViewManagers/RNSVGNodeManager.m#L27-L30
@msand I had just done the same actually, but had been trying to animate the radius of SVG circle (getting RCTConvert errors still)!
It looks like viewNameForRectTag will come back nilhere, unless the view gets added to the map of shadowViews here if you remove that line.
RCTShadowView seems to have a tonne of yoga stuff, so still not sure what makes the most sense to do.
Yeah I was having some issues with animating the props represented as NSString, haven't figured out how to deal with that yet. But CGFloat based ones seem to work with just removing that one method to get the shadow nodes registered, trying to figure out how to get it to skip the YGValue conversion now.
ah good observation! I'm stuck on the same 馃
I think any property name collisions with RCTShadowView need to be pre-fixed with rnsvg or something similar. Or now that I think about it, the removed method probably needs to exist, and give a shadow node corresponding to the element.
@msand this looks pretty suspect: https://github.com/react-native-community/react-native-svg/blob/master/elements/Circle.js#L35, for example.
It might not actually have any affect actually, as createAnimatedComponent does some HOC magic.
Ah so yes, that was part of the problem actually! Just as a proof of concept I had changed the exported view property type of r on RNSVGCircle to NSNumber, and then converted the number value to a string inside the setter of r. But the toString in Circle.js, would've meant a string would've still been passed to the native side for the first render of the circle, thus failing the props validation.
But with that, animating the radius works with useNativeDriver!
I had my suspicions before, but I'm pretty sure this is not anything to do with property name collisions before with RCTShadowView, as I had hardcoded the 'viewName' to test this.
Most of the properties are strings, because they support units and they depend on e.g. the font-size and clip bounds so need to be passed as such to the native side for the css resolution logic. E.g. Rect has a name collision for width and height with RCTShadowView causing it to use the YGValue converter.
It looks like the string property aspect is a bit of a pain point! I'll have a look some of the string properties that support units in the documentation as I'm not too familiar with that myself. Having a separate prop for each unit like so fontSizePx and fontSizeEm would allow us to get around this issue by the looks of it for example, and use numbers for these props.
However it might actually be useful to have a Animated.convertToString native method where you could do something like this animatedValue.convertToString('${value}px'), and you'd pass that to your fontSize prop. I'm not sure if this would be useful for anything else outside of react-native svg? 馃
I've managed to hack together a proof of concept for android as well, but I'm really hoping it's the wrong approach and that there is some better way of getting it to work, using this approach it would have to create a View/ViewGroup for each node, at least it doesn't have to be attached to the layout tree, but its silly to have it, when all I use it for is to get the id / shadow node corresponding to it. Also the queueing of the rendering might not be as efficient as it should be. Any feedback would be much appreciated! https://github.com/msand/react-native-svg/commit/fbd65912512aa74c00d13d32a26c1f7b3e538b46 (Edit force-pushed some missing files)
@joshyhargreaves I have a PR to react-native for string interpolation on iOS now 馃槃https://github.com/facebook/react-native/pull/18187 Waiting for some feedback, and if good will port it to Android as well. Allowing us to animate all of the string props with unit support. Added the missing ReactProps to Android as well https://github.com/msand/react-native-svg/commit/a3c9aa287257eb6ef8a6853dded0a490b0b1830c
import React, { Component } from 'react';
import { StyleSheet, View, Dimensions, Animated } from 'react-native';
import { Svg, Rect } from 'react-native-svg';
const { width, height } = Dimensions.get('window');
const AnimatedRect = Animated.createAnimatedComponent(Rect);
function getInitialState() {
const anim = new Animated.Value(0);
const fillOpacity = anim.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
});
const offset = fillOpacity.interpolate({
inputRange: [0, 1],
outputRange: [0, 10],
});
const strokeOpacity = offset.interpolate({
inputRange: [0, 5],
outputRange: [0, 1],
extrapolateRight: 'clamp',
});
const strokeWidth = strokeOpacity.interpolate({
inputRange: [0, 1],
outputRange: ['0', '5'],
});
return { anim, fillOpacity, offset, strokeOpacity, strokeWidth };
}
export default class App extends Component {
state = getInitialState();
componentDidMount() {
const { anim } = this.state;
Animated.timing(anim, {
toValue: 1,
duration: 3000,
useNativeDriver: true,
}).start();
}
render() {
const { fillOpacity, offset, strokeOpacity, strokeWidth } = this.state;
return (
<View style={styles.container}>
<Svg width={width} height={height} viewBox="0 0 100 100">
<AnimatedRect
x="5"
y="5"
width="90"
height="90"
stroke="blue"
fill="green"
strokeDasharray="1 1"
strokeWidth={strokeWidth}
strokeDashoffset={offset}
strokeOpacity={strokeOpacity}
fillOpacity={fillOpacity}
/>
</Svg>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#ecf0f1',
},
});
Now with android support for native string interpolation as well: https://github.com/msand/react-native/commit/d3d6e669fcf5e53c7b8c8cb33dbe8e81f587cef3
@msand any update about the PR?
@msand also, can you show an example with
@msand Your answer from 5 March was very helpful. However, it doesn't seem to work when animating rotation properties. (See this issue). Do you have any idea on how to solve this?
v7.0.0 has been released with support for useNativeDriver
@designingSparks You probably need to use setNativeProps with a matrix for the transform for now, you might want to ask @msageryd
@designingSparks I didn't actually cope with the matrix algebra needed, so I'm using a library for the calculations.
https://gitlab.com/epistemex/transformation-matrix-js
This library let's you chain your transforms and gives a matrix back to you. Get hold of a ref to your svg component (I'm moving around G-elements). Use the components of the returned matrix as input to setNativeProps like this:
const matrix = new Matrix()
.translate(x, y)
.rotateDeg(45)
.scaleU(0.5);
this.svgGroupRef.setNativeProps({
matrix: [matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f], //[scaleX, 0, 0, scaleY, x, y],
});
The comment "[scaleX, .." is only roughly what the components means. It serves as a reminder of what goes where, but don't use it as the complete truth of the components.
Edit: I managed to find the original discussion, which helped me with this:
https://github.com/react-native-community/react-native-svg/issues/556#issuecomment-354099452
@oriharel For animation of the Path d attribute check here: https://github.com/facebook/react-native/pull/18187#issuecomment-414157937
Although, be aware that the path parsing on android is quite inefficient at the moment, and one of the biggest consumers of cpu when animating the path data. At least from what I could see when profiling that example in android studio.
@msand Thanks! what version of React Native I need in order to use react-native-svg v7.0.3?
I've mostly tested using 0.56 and 0.57, but as long as it builds and runs without exceptions you should be fine. If you have any issues with any recent version then please report it.
I've implemented support for animation of transform styles using the same syntax as for react-native views now (with and without useNativeDriver): fb4e877
and the same for x and y on Text and TSpan https://github.com/react-native-community/react-native-svg/issues/803#issuecomment-428785429
import React from "react";
import { Dimensions, Animated } from "react-native";
import Svg, { Text, TSpan, G } from "react-native-svg";
const { width, height } = Dimensions.get("window");
const AnimatedG = Animated.createAnimatedComponent(G);
class NativeAnimGTransform extends React.Component {
state = {
anim: new Animated.Value(0),
};
componentDidMount() {
this.animate(this.props.value);
}
componentDidUpdate({ value }) {
this.animate(value);
}
animate = value =>
Animated.timing(this.state.anim, {
useNativeDriver: true,
duration: 4000,
toValue: value,
}).start();
render() {
const { anim } = this.state;
return (
<Svg width={width} height={height}>
<AnimatedG
style={{
transform: [
{
translateY: anim.interpolate({
inputRange: [0, 1],
outputRange: [0, 100],
}),
},
],
}}
>
<Text>
<TSpan>Test</TSpan>
</Text>
</AnimatedG>
</Svg>
);
}
}
export default function App() {
return <NativeAnimGTransform value={1} />;
}
Now with useNativeDriver animation support for all number accepting properties (without need for my fork of react-native, works at least in 0.57 straight away): https://github.com/react-native-community/react-native-svg/commit/864d7610d3aaea2b4e4f722f04ea52ddf292aae7
@oriharel I just tried running react-native init oldVersion --version v0.50.4 upgraded sdk, gradle / build tools versions, and then yarn add react-native-svg resulting in:
"react": "16.0.0",
"react-native": "0.50.4",
"react-native-svg": "^7.1.2",
And it seemed to work fine even with useNativeDriver: true
So it still seems be compatible with the versions given in the readme at least
I've implemented the SVGLength interface now, and optimised the code for the case when the arguments are numbers rather than strings. https://github.com/react-native-community/react-native-svg/compare/SVGLength
In properties where the spec allows for SVGLengthList it now accepts single values as either number or string, and arrays of values which can even be mixed, as long as each value can be interpreted as a SVGLength. So, if want to optimise your code, you should pass all unit-less lengths/dimensions as Number instead of String, and preferably author your svg content without using units, instead rely on the viewbox attribute together with the width and height. This eliminates representing numbers as strings, which is inefficient. And, thanks to these changes, the animations do not cause a redundant double > string > double round-trip.
Regarding d attribute animation in the checkPattern() method inside the AnimatedInterpolation.js of the react-native node module. Check it out here. It makes me wonder, however, if the animation works, why should we be limited by the harsh checkPattern() criteria.
@oriharel I would expect that one to work, it's probably a bug in the implementation of checkPattern, it should just make sure that there is exactly the same number of substrings which can be interpreted as independent numbers in all strings, such that it can actually produce a string with a number in each place where it is needed.
It is also possible to animate between arbitrary paths in general, but there are more ways than one to do it, so it requires deciding more boundary conditions / dynamics to enable linear interpolation. And that's something which tends to need experimentation and tailor made curves/algorithms to actually looks nice, so it doesn't belong in a simple linear interpolator.
But, at least the batman example should certainly be possible to animate using the js logic in plain react-native, and using the native driver in my fork. Or, now that I think about it, I remember vaguely that the plain react-native limits string interpolation to color strings, and the code you removed just removes that redundant restriction. I'll have to check and test a bit once I have a bit more time for this.
Closing this now as everything should be working correctly afaik.
Most helpful comment
@joshyhargreaves I have a PR to react-native for string interpolation on iOS now 馃槃https://github.com/facebook/react-native/pull/18187 Waiting for some feedback, and if good will port it to Android as well. Allowing us to animate all of the string props with unit support. Added the missing ReactProps to Android as well https://github.com/msand/react-native-svg/commit/a3c9aa287257eb6ef8a6853dded0a490b0b1830c