Reputation: 14563
How would I go about animating from one color to another in React Native. I've found that by interpolating an Animated.Value you can animate colors by:
var BLACK = 0;
var RED = 1;
var BLUE = 2;
backgroundColor: this.state.color.interpolate({
inputRange: [BLACK, RED, BLUE],
outputRange: ['rgb(0, 0, 0)', 'rgb(255, 0, 0)', 'rgb(0, 0, 255)']
})
and
Animated.timing(this.state.color, {toValue: RED}).start();
But using this method, going from BLACK to BLUE, you have to go through red. Add more colors to the mix and you end up in a 1980s disco.
Is there another way of doing this that allows you to go straight from one color to another?
Upvotes: 84
Views: 89376
Reputation: 1
With reanimate v3
const progress = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
backgroundColor: interpolateColor(progress.value, [0, 1], ["red", "green"]),
}));
return <Animated.View style={[{ width: 100, height: 100 }, animatedStyle]} />;
Upvotes: 0
Reputation: 31
I did a hook to do what David Schumann told.
useColorAnimation.js
import { useRef, DependencyList, useMemo, useEffect, useState } from "react";
import { Animated } from "react-native";
const useColorAnimation = (color) => {
const anim = useMemo(() => new Animated.Value(0), [color]);
const [finished, setFinished] = useState(true)
const currentColor = useRef(color);
const nextColor = useMemo(()=> color, [color]);
const animColor = anim.interpolate({
inputRange: [0, 1],
outputRange: [currentColor.current, nextColor],
});
useEffect(() => {
setFinished(false)
Animated.spring(anim, {
toValue: 1,
useNativeDriver: false,
}).start(() => {
currentColor.current = nextColor;
setFinished(true)
});
}, [color]);
return [animColor, finished];
};
export default useColorAnimation
using the hook:
import React, { useState } from 'react';
import { StyleSheet, Animated, Button } from 'react-native';
import useColorAnimation from './useColorAnimation';
const colors = ['rgb(0, 0, 0)', 'rgb(255, 0, 0)', 'rgb(0, 0, 255)'];
const getNextColor = (currentColor) => {
const index = colors.indexOf(currentColor) + 1;
return index == colors.length ? colors[0] : colors[index];
};
export default function App() {
const [color, setColor] = useState(colors[0]);
const [backgroundColor, finished] = useColorAnimation(color);
const handleButton = () => setColor((current) => getNextColor(current));
return (
<Animated.View style={[styles.container, { backgroundColor }]}>
<Button title="Next" onPress={handleButton} disabled={!finished} />
</Animated.View>
);
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
flex: 1,
},
});
here an example https://snack.expo.dev/@rafaelnsantos/rude-croissant
Upvotes: 3
Reputation: 74
Managed to stop mine from blinking when in a loop by duplicating the first colour at the end:
const backgroundColourIndex = useRef(new Animated.Value(0)).current;
let backgroundColorAnimated = backgroundColourIndex.interpolate({
inputRange: [0, 1, 2, 3],
outputRange: ['red', 'blue', 'yellow', 'red'],
});
Upvotes: 0
Reputation: 340
I created an example here that should how to do this with the lastest version of react native.
https://cjoshmartin.com/blog/react-native-animations-example/
you can also read more here: https://www.codedaily.io/courses/Master-React-Native-Animations/Color-Background-Color https://reactnative.dev/docs/animations
Upvotes: 1
Reputation: 71
const animatedBkg = interpolate(scale, {
inputRange: [0, 150],
outputRange: [Animated.color(242, 81, 48), Animated.color(0,0,0)],
// extrapolate: Extrapolate.CLAMP,
})
tested with reanimated.
Upvotes: -3
Reputation: 4485
Given you have Animated.Value
lets say x
, you can interpolate color like this:
render() {
var color = this.state.x.interpolate({
inputRange: [0, 300],
outputRange: ['rgba(255, 0, 0, 1)', 'rgba(0, 255, 0, 1)']
});
return (
<Animated.View style={{backgroundColor:color}}></Animated.View>
);
}
You can find full working example in the issue I've posted on github.
Upvotes: 90
Reputation: 1772
If you could get the color of the animated color value at the instant you pressed the button then you could probably do it. Something like this :
var currentColor = ? :
this.state.color = 0;
var bgColor = this.state.color.interpolate({
inputRange: [0, 1],
outputRange: [currentColor, targetColor]
});
So for each button you'd set a different targetColor.
Upvotes: 14
Reputation: 8924
You can also interpolate in steps, for non-numeric values like color, like this:
<Animated.Text
style={{
color: colorAnim.interpolate({
inputRange: [0, 0.5, 1],
outputRange: ['black', 'gray', 'white']
})
}}>
Upvotes: -3