Animating colors with withTiming or withSpring doesn't seem to work with colors values (hex or RGB).

1.
Animate colors
Color just changes
import Animated, {
useSharedValue,
withSpring,
useAnimatedStyle,
Easing,
withTiming,
} from 'react-native-reanimated';
import {View, Button} from 'react-native';
import React from 'react';
function getColor() {
return (
'#' +
('000000' + Math.floor(Math.random() * 16777215).toString(16)).slice(-6)
);
}
function hexToRGB(h) {
let r = 0,
g = 0,
b = 0;
// 3 digits
if (h.length == 4) {
r = '0x' + h[1] + h[1];
g = '0x' + h[2] + h[2];
b = '0x' + h[3] + h[3];
// 6 digits
} else if (h.length == 7) {
r = '0x' + h[1] + h[2];
g = '0x' + h[3] + h[4];
b = '0x' + h[5] + h[6];
}
return 'rgb(' + +r + ',' + +g + ',' + +b + ')';
}
export default function AnimatedStyleUpdateExample(props) {
const randomWidth = useSharedValue(10);
const color = useSharedValue(hexToRGB(getColor()));
const style = useAnimatedStyle(() => {
return {
width: withSpring(randomWidth.value),
backgroundColor: withTiming(color.value, {
duration: 500,
easing: Easing.bezier(0.5, 0.01, 0, 1),
}),
};
});
return (
<View
style={{
flex: 1,
flexDirection: 'column',
}}>
<Animated.View style={[{width: 100, height: 80, margin: 30}, style]} />
<Button
title="toggle"
onPress={() => {
color.value = hexToRGB(getColor());
randomWidth.value = Math.random() * 350;
}}
/>
</View>
);
}
@browniefed, why did you close this?
@jakub-gonet ah I was doing it wrong, should be interpolating. Although I still don't think top level coloring is handled after reviewing some stuff
Cool, thanks for the response, if you find another issue regarding colors feel free to reopen.
Has anyone managed to get colors to work? If I put a color in withSpring, I see this error:

@nandorojo, please show us code that is crashing, we can't help you based only on the stack trace.
Yup sorry about that, I'll add a code sample here.
This code produces that error:
import React, { useReducer } from 'react'
import Animated, { useAnimatedStyle, withSpring } from 'react-native-reanimated'
import { Button } from 'react-native'
export default function ColorError() {
const [backgroundColor, toggle] = useReducer(
(bg) => (bg === 'red' ? 'green' : 'red'),
'red'
)
const style = useAnimatedStyle(() => ({
backgroundColor: withSpring(backgroundColor),
}))
return (
<Animated.View
style={[
{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
style,
]}
>
<Button title="Toggle Color" onPress={toggle} color="white" />
</Animated.View>
)
}
I'm using the expo reanimated starter. Here's my package.json:
{
"version": "39.0.0",
"dependencies": {
"expo": "~39.0.0",
"expo-status-bar": "~1.0.2",
"react": "~16.13.0",
"react-dom": "~16.13.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-39.0.2.tar.gz",
"react-native-gesture-handler": "^1.8.0",
"react-native-reanimated": "2.0.0-alpha.6.1",
"react-native-web": "0.13.13",
"react-navigation-stack": "^2.8.4"
},
"devDependencies": {
"@babel/core": "^7.8.6",
"babel-preset-expo": "~8.1.0",
"eslint-config-nando": "^1.0.10"
},
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo web",
"eject": "expo eject"
},
"private": true
}
I also get this TS error:

withSpring takes a number as an argument, you can't use a string there. What do you want to achieve here? If you want to interpolate between two colors you can use interpolateColors (which is undocumented, but it should work).
Got it, that makes sense. I'm basically trying to animate from one color to the next, where I keep the color as a React state value. Whenever the color changes, I animate to the next one.
I'm working on a component library similar to framer-motion, powered by reanimated 2.
Link: https://github.com/nandorojo/redrip
Background: https://github.com/nandorojo/dripsy/issues/46
I鈥檓 hoping to pass backgroundColor to a custom animate prop. Whenever it changes, it automatically animates, similar to transition-property: background-color in CSS.
If you're interested, you can see how I'm doing it in src/redripify/use-map-animate-to-style.ts
I assume interpolateColors only works for interpolating based on a given animate node. In my case, however, I'm not creating any animated nodes -- instead, I'm using useAnimatedStyle directly, as shown in my example above.
Maybe I could create an animated value under the hood for every possible color style value, but it doesn't seem ideal.
Do you have any suggestions @jakub-gonet? Thanks so much for your time!
I don't maintain Rea2 and have limited knowledge about it but @zrebcu411 or @piaskowyk maybe know the answer for this one?
I tried using processColor for the color values. However, this leads to an odd flickering.
import { View, Button, StyleSheet } from 'react-native'
import React from 'react'
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
processColor, // I tried this
} from 'react-native-reanimated'
const colors = ['rgb(5,200,3)', 'rgb(10,5,100)']
export default function ColorBug() {
const color = useSharedValue(colors[0])
const toggleColor = () => {
if (color.value === colors[0]) {
color.value = colors[1]
} else {
color.value = colors[0]
}
}
const style = useAnimatedStyle(() => ({
backgroundColor: withTiming(processColor(color.value)),
}))
return (
<View style={styles.container}>
<Animated.View style={[styles.box, style]}></Animated.View>
<Button title="toggle" onPress={toggleColor} />
</View>
)
}
const styles = StyleSheet.create({
box: {
justifyContent: 'center',
backgroundColor: 'blue',
height: 100,
width: 100,
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
},
})
I tried using hex strings, RGB strings, and color names in the colors array, but they all have the same result.
Here's a video of what happens:

@jakub-gonet would you mind re-opening this?
Sure, but I'm afraid I won't be able to help.
Got it, thanks for the heads up! I'll keep an eye out here.
Hi @nandorojo!
You should move withTiming(processColor(color.value)) from useAnimatedStyle to different method and use color.value instead, and it will work
Here is code snippet
```
const colors = ['rgba(5, 200, 3, 1)', 'rgba(10, 5, 100, 1)']
....
const toggleColor = () => {
if (color.value === colors[0]) {
color.value = withTiming(processColor(colors[1]))
} else if (color.value === colors[1]) {
color.value = withTiming(processColor(colors[0]))
}
}
const style = useAnimatedStyle(() => ({
backgroundColor: color.value,
}))
```
FYI tested this case on ios, android and web
On both mobile platforms bug is reproducible
On web I just see empty screen (screenshot attached)
When moved withTiming(processColor(color.value)) from useAnimatedStyle, animation works correctly on all of the platforms
Working Code:
import Animated, {
useSharedValue,
withTiming,
useAnimatedStyle,
Easing,
processColor,
} from 'react-native-reanimated';
import { View, Button, StyleSheet } from 'react-native';
import React from 'react';
const colors = ['rgba(5,200,3, 1)', 'rgba(10,5,100,1)']
export default function ColorBug() {
const color = useSharedValue(colors[0])
const toggleColor = () => {
if (color.value === colors[0]) {
color.value = withTiming(processColor(colors[1]))
} else {
color.value = withTiming(processColor(colors[0]))
}
}
const style = useAnimatedStyle(() => ({
backgroundColor: color.value,
}))
return (
<View style={styles.container}>
<Animated.View style={[styles.box, style]}></Animated.View>
<Button title="toggle" onPress={toggleColor} />
</View>
)
}
const styles = StyleSheet.create({
box: {
justifyContent: 'center',
backgroundColor: 'blue',
height: 100,
width: 100,
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
},
})

@dmmaslenn Thanks for testing that, I'll try this out.
I'm actually working on a library that needs to drive the animation inside of the UAS hook, so it would be great to have this bug fixed. I really appreciate you finding the source of it.
Which version are you on, by the way?
I wonder if this could be related to https://github.com/software-mansion/react-native-reanimated/issues/1511?
Hey @nandorojo I鈥檓 sorry for my late reply.
Well basically it isn't a bug 馃榿 When you use processColor(colorStr) it is converted to a number. In the next step you try to animate this value but this is a number not a color, and interpolation of number is different then interpolation of color. In summary why it was acting strange - because if you want to interpolate color in smooth way you should interpolate each channel (R, G, B, A) separately not as one 32bit number
You have two simple solutions:
Based on your example code. You should just remove processColor()
Code
import { View, Button, StyleSheet } from 'react-native'
import React from 'react'
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated'
const colors = ['rgb(5,200,3)', 'rgb(10,5,100)']
export default function ColorBug() {
const color = useSharedValue(colors[0])
const toggleColor = () => {
if (color.value === colors[0]) color.value = colors[1]
else color.value = colors[0]
}
const style = useAnimatedStyle(() => ({
backgroundColor: withTiming(color.value),
}))
return (
<View>
<Animated.View style={[styles.box, style]}></Animated.View>
<Button title="toggle" onPress={toggleColor} />
</View>
)
}
const styles = StyleSheet.create({
box: {
justifyContent: 'center',
backgroundColor: 'blue',
height: 100,
width: 100,
},
})
My recommended way is use interpolateColor:
Code
import { View, Button, StyleSheet } from 'react-native'
import React from 'react'
import Animated, {
useAnimatedStyle,
useSharedValue,
withTiming,
interpolateColor,
Easing
} from 'react-native-reanimated'
const colors = ['rgb(5,200,3)', 'rgb(10,5,100)']
export default function ColorBug() {
const color = useSharedValue(0)
const toggleColor = () => {
if (color.value === 0) {
color.value = withTiming(1, {duration: 200, easing: Easing.linear})
} else {
color.value = withTiming(0, {duration: 200, easing: Easing.linear})
}
}
const style = useAnimatedStyle(() => ({
backgroundColor: interpolateColor(
color.value,
[0, 1],
colors,
),
}))
return (
<View>
<Animated.View style={[styles.box, style]}></Animated.View>
<Button title="toggle" onPress={toggleColor} />
</View>
)
}
const styles = StyleSheet.create({
box: {
justifyContent: 'center',
backgroundColor: 'blue',
height: 100,
width: 100,
},
})
I tested this on the latest version of Reanimated.
Have you any more questions or can I close this issue?
@piaskowyk Thanks! The only open question 鈥撀營'm passing a string to withTiming, but it expects a number. Should I @ts-ignore?
I think that we should change the passing type in the signature of function. I think we will change this in the newer version.