Reputation: 2804
I have created a simple animation with Animated
from react-native
with react-native-svg
.
This do the jobs well,
But Now I switched to react-native-reanimated
cuz I read on their website that reanimated is faster than Animated
from react-native
.
But here I faced with a problem, and that is I cant find function addListener
to listen to the value changes.
Code with Animated
from react-native
:
const circleRadius = new Animated.value(100);
circleRadius.addListener( circleRadius => {
circleSVG.current.setNativeProps({ cx: circleRadius.value.toString() });
});
How can I implement above addListener
function in react-native-reanimated
?
Upvotes: 7
Views: 15116
Reputation: 51
import React, { FC, useRef } from 'react';
import { StyleSheet, TextInput, View } from 'react-native';
import Svg, { G, Circle } from 'react-native-svg';
import Animated, { call, Easing, interpolate, useCode } from 'react-native-reanimated';
import { timing } from 'react-native-redash';
interface DonutChartProps {
percentage: number;
radius?: number;
strokeWidth?: number;
duration?: number;
color?: string;
delay?: number;
textColor?: string;
max?: number;
}
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
const AnimatedTextInput = Animated.createAnimatedComponent(TextInput);
const DonutChart: FC<DonutChartProps> = ({
percentage,
radius = 40,
strokeWidth = 10,
duration = 500,
color = 'tomato',
textColor,
max = 100,
}) => {
const inputRef = useRef<TextInput>(null);
const halfCircle = radius + strokeWidth;
const circumference = 2 * Math.PI * radius;
const maxPercentage = (100 * percentage) / max;
const animation = timing({
from: 0,
to: 1,
duration,
easing: Easing.inOut(Easing.linear),
});
const strokeDashoffset = interpolate(animation, {
inputRange: [0, 1],
outputRange: [circumference, circumference - (maxPercentage * circumference) / 100],
});
const textValue = interpolate(animation, {
inputRange: [0, 1],
outputRange: [0, Math.round(percentage)],
});
useCode(
() => [
call([textValue], ([textValue]) => {
if (inputRef.current) {
inputRef.current.setNativeProps({
text: `${Math.round(textValue)}`,
});
}
}),
],
[textValue]
);
return (
<View>
<Svg width={radius * 2} height={radius * 2} viewBox={`0 0 ${halfCircle * 2} ${halfCircle * 2}`}>
<G rotation="-90" origin={`${halfCircle}, ${halfCircle}`}>
<Circle
cx="50%"
cy="50%"
stroke={color}
strokeWidth={strokeWidth}
r={radius}
fill="transparent"
strokeOpacity={0.2}
/>
<AnimatedCircle
cx="50%"
cy="50%"
stroke={color}
strokeWidth={strokeWidth}
r={radius}
fill="transparent"
strokeDasharray={circumference}
strokeDashoffset={strokeDashoffset}
strokeLinecap="round"
/>
</G>
</Svg>
<AnimatedTextInput
ref={inputRef}
underlineColorAndroid="transparent"
editable={false}
defaultValue="0"
style={[
StyleSheet.absoluteFillObject,
{ fontSize: radius / 2, color: textColor ?? color, fontWeight: '900', textAlign: 'center' },
]}
/>
</View>
);
};
export default DonutChart;
Upvotes: 3
Reputation: 1953
Reanimated is meant to be a declarative API that allows you to run more advanced animations and thus complex logic on the native thread.
The reason something similar to addListener
is not implemented is because it would require unnecessary messages between the native and JS thread. So rather than using a listener and setNativeProps
to update the cx
property of your circle, it would be best to use an AnimatedNode
.
const circleRadius = new Animated.value(100);
circleRadius.addListener( circleRadius => {
circleSVG.current.setNativeProps({ cx: circleRadius.value.toString() });
});
import { Circle } from 'react-native-svg';
// must make Circle compatible with Animated Values
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
// then within your svg
<AnimatedCircle
// ... add other props
cx={circleRadius}
/>
Upvotes: 1
Reputation: 17354
You can achieve similar behavior using Animated.call
. Here is a nice tutorial about the subject.
Edited:
For example, to listen to circleRadius
changes, you could use this code:
import { call, useCode } from 'react-native-reanimated'
useCode(() => {
return call([circleRadius], (circleRadius) => {
console.log(circleRadius)
})
}, [circleRadius])
Does it do what you want?
Upvotes: 18