nicholas
nicholas

Reputation: 14563

Animating backgroundColor in React Native

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

Answers (9)

Jayan
Jayan

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

Rafael
Rafael

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

AWE
AWE

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

Josh Martin
Josh Martin

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

rajendra upadhyay
rajendra upadhyay

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

neciu
neciu

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

cmrichards
cmrichards

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

Luke W
Luke W

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

browniefed
browniefed

Reputation: 94

Use setValue and state to control start and end colors.

Upvotes: -22

Related Questions