I have a react functional component Svg
being used as an icon for the Bottom-TabBar. On route change the current state.index
is compared to the route index
. The result which essentially is a boolean state isFocused
is passed to the Svg.
I am trying to animate the Svg based on this state and can not get the simple operation completed using reanimated. I am most certain that the value of fill is not being updated in useAnimatedProps
hook but I lack the experience of having deep knowledge with reanimated. Any help will be greatly appreciated
import Animated, {
useAnimatedProps,
useSharedValue,
} from 'react-native-reanimated';
import Svg, { Circle, Path } from 'react-native-svg';
const AnimatedSvg = Animated.createAnimatedComponent(Svg);
export default ({ isFocused }) => {
const fill = useSharedValue({ fill: 'transparent' });
const animatedProps = useAnimatedProps(() => {
isFocused
? (fill.value = { fill: 'red' })
: (fill.value = { fill: 'transparent' });
return fill.value;
});
return (
<AnimatedSvg
xmlns="http://www.w3.org/2000/svg"
width={24}
height={24}
animatedProps={animatedProps}
stroke={'white'}
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className="feather feather-search">
<Circle cx={11} cy={11} r={8} />
<Path d="m21 21-4.35-4.35" />
</AnimatedSvg>
);
};
CodePudding user response:
A more common approach would be to use a "progress-variable" as the shared value.
const fillProgress = useSharedValue(isFocused? 1 : 0);
You would the use this progress-variable to generate the animatedProps. Please note the use of interpolateColor
to get the actual interpolated color.
const animatedProps = useAnimatedProps(() => {
const fillValue = interpolateColor(fillProgress.value, [0, 1], ["transparent", "red"]);
return {
fill: fillValue
}
});
You have to return an object with the properties you would want to animate. If you wanted to animate fill and opacity for example, you would return {fill: "", opacity: -1}
with the appropriate values instead of ""
and -1
. Lastly you have to make the actual element you want to animate Animated. In this case, you want to animate the Circle
, not the Svg
so that has to be an Animated object.
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
You can then detect being focused using useEffect
and animate accordingly.
useEffect(() => {
fillProgress.value = withTiming(isFocused? 1 : 0);
}, [isFocused]);
Remember to set the initial value for the fillProgress
just like you do in the withTiming
function.
To summarize, you have to animate the element, that uses the animated properties and you should use progress-variables as mentioned above.
Here is the full modified code (tested on Android):
import Animated, {
useAnimatedProps,
useSharedValue,
} from 'react-native-reanimated';
import Svg, { Circle, Path } from 'react-native-svg';
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
export default function Icon ({ isFocused }) {
const fillProgress = useSharedValue(isFocused? 1 : 0);
const animatedProps = useAnimatedProps(() => {
const fillValue = interpolateColor(fillProgress.value, [0, 1], ["transparent", "red"]);
return {
fill: fillValue
}
});
useEffect(() => {
fillProgress.value = withTiming(isFocused? 1 : 0);
}, [isFocused]);
return (
<Svg
width={24}
height={24}
stroke={'white'}
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
className="feather feather-search">
<AnimatedCircle animatedProps={animatedProps} cx={11} cy={11} r={8} />
<Path d="m21 21-4.35-4.35" />
</Svg>
);
};