-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use with animation #180
Comments
Yes, it can. Probably you want to create Animated variants of each component (with |
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:
using 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.
|
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
|
@anhtuank7c Thanks for the suggestion but now I get this error message:
|
@grubstarstar @LeeBradley I'm having exactly the same issue. Did you ever find a workable solution? Thanks! I opened this before finding this thread: |
@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 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. |
props being set were not passing through extractProps, leading to various mismatches in the values. software-mansion#180 software-mansion#326
@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: #328 The only gotcha is that you still have to use this.myAnim.__getAnimatedValue() when sending the value into setNativeProps
|
props being set were not passing through extractProps, leading to various mismatches in the values. software-mansion#180 software-mansion#326
props being set were not passing through extractProps, leading to various mismatches in the values. software-mansion#180 software-mansion#326
@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 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 |
@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:
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 |
@dk0r I found same error with Polyline :( |
@anhtuank7c There are only certain attributes that are exposed to |
Do y’all think we’ll ever be able to offload animations on the native side via |
Its fully possible, just have to integrate with the driver and the declarative animation passing ;) You wanna take a shot at it? |
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 |
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 However it might actually be useful to have a |
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! msand@fbd6591 (Edit force-pushed some missing files) |
@joshyhargreaves I have a PR to react-native for string interpolation on iOS now 😄facebook/react-native#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 msand@a3c9aa2 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: msand/react-native@d3d6e66 |
@msand any update about the PR? |
@msand also, can you show an example with 'd' attribute? |
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: |
@oriharel For animation of the Path d attribute check here: facebook/react-native#18187 (comment) |
@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 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): 864d761 |
@oriharel I just tried running
And it seemed to work fine even with useNativeDriver: true |
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 |
Regarding d attribute animation in the element, I managed to animate the famous Batman example but needed a very ugly hack - I commented out the |
@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. |
Can it uses with react-native's Animated
The text was updated successfully, but these errors were encountered: