I'm having below svg path, which rotates around origin in every value
state change successfully, where value is some degrees value:
<Path
transform={{
rotation: this.state.value,
originX: width / 2,
originY: height / 2
}}
d={`M ${width * 0.5} ${height * 0.5} L${width * 0.75} ${height * 0.75}`} //the actual path does not matter
fill={Colors.black}
stroke={Colors.black}
/>
I call setState on the value
every ~20 ms, but this is not smooth enough, + it's not the recommended way of doing a transition.
What i want to achieve, is animate the rotation using the animated API, to achieve much smoother rotation.
I have tried making my value
state an Animated.Value
. Then making the Path
a Animated.createAnimatedComponent(Path)
and calling:
Animated.timing(this.state.value, {
toValue: newDegreesValue,
duration: 1000,
useNativeDriver: true
}).start();
Then i render the path like this:
<Path
style={transform:[{
rotate: this.state.value
}]}
d={`M ${width * 0.5} ${height * 0.5} L${width * 0.75} ${height * 0.75}`} //the actual path does not matter
fill={Colors.black}
stroke={Colors.black}
/>
This doesn't work at all close to the previously working code using state. One reason is the originX, originY values that are not supported by the animation API and i don't know how to replace, but I'm not sure it's the only reason, maybe rotate
is somehow different than rotation
property too?.
So, the question is, how can the same outcome by the code which is continuously calling setState be achieved using animated api?
This is the way i achieved it:
// init the animatedComponents
const NeedlePath = Animated.createAnimatedComponent(Path);
const AnimatedG = Animated.createAnimatedComponent(G);
// set the animation
Animated.timing(this.state.angle, {
toValue: 240, // degrees
duration: 1000,
useNativeDriver: true
}).start();
// pivotX and pivotY are the originX, originY points.
// the width and height are calculated dynamically, so that the whole svg is a square (equal height width), and the center of it are my origins.
render() {
let height = this.state.height;
let width = this.state.width;
let [pivotX, pivotY] = [0, 0];
if (height && width) {
[pivotX, pivotY] = [width / 2, height / 2];
}
return (
<View
style={{ flex: 1}}
onLayout={event => {
this.findDimesions(event.nativeEvent.layout); // find height and width dynamically, so that this remain a square container with same height and width
}}
>
{height !== undefined && width !== undefined && (
<Svg height={height} width={width} style={{ alignItems: "center" }}>
<G transform={`translate(${pivotX}, ${pivotY})`}>
<AnimatedG
style={{
transform: [
{
rotate: this.state.angle.interpolate({
inputRange: [0, 360],
outputRange: [`0deg`, `360deg`]
})
}
]
}}
>
<NeedlePath
transform={`translate(-${pivotX} -${pivotY})`}
d={`M ${width * 0.5} ${height * 0.5} L${width * 0.5 + width * 0.25} ${height * 0.5 + height * 0.25}`}
fill={Colors.black}
stroke={Colors.black}
/>
</AnimatedG>
</G>
</Svg>
)}
</View>
);
}
The style={{ alignItems: "center" }}
is crucial to achieve the same result on android too, instead of setting translationX and translationY with offsetAndroid. This is working for version v9.13.3
of react-native-svg. Maybe in newer versions this get affected, because of fixing the translations problem, and i don't know what the impact will be on the code here. You can refer here for more info about the translations with offset needed on android