Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animate Gradient Color React Native

I'm trying to create a gradient in react native that will start as one color when the app opens, and then gradually change into another color every 30 seconds. The regular linear gradient worked without trying to add animation. I tried using interpolation and the Animated timing as seen in the react native documentation, but nothing seems to work.

My Code:

import React, {Component} from 'react';
import {processColor, AppRegistry, StyleSheet, Dimensions, Animated, Image, Easing, View} from 'react-native';

import TimerMixin from 'react-timer-mixin';
import LinearGradient from 'react-native-linear-gradient';

var screenWidth = Dimensions.get('window').width;
var screenHeight = Dimensions.get('window').height;

//HEX version of colors
var gradientColors = [['#EF2A2A', '#EF6A2A'], //Red
['#EF6A2A', '#EFD82A'], //Orange
['#1BD170', '#61E822'], //Green
['#22D2E6', '#26F084'], //Aqua
['#2A3BEF', '#2ADCEF'], //Blue
['#EF2AD2', '#2A3BEF'], //Purple
['#EF2AD2', '#EF2A2A'] //Pink
]
var gradientColorsNoHash = [['EF2A2A', 'EF6A2A'], //Red
['EF6A2A', 'EFD82A'], //Orange
['1BD170', '61E822'], //Green
['22D2E6', '26F084'], //Aqua
['2A3BEF', '2ADCEF'], //Blue
['EF2AD2', '2A3BEF'], //Purple
['EF2AD2', 'EF2A2A'] //Pink
]
/*var gradientColors = [['ef2a2a', 'ef6a2a'], //Red
['ef6a2a', 'efd82a'], //Orange
['1bd170', '61e822'], //Green
['22d2e6', '26f084'], //Aqua
['2a3bef', '2adcef'], //Blue
['ef2ad2', '2a3bef'], //Purple
['ef2ad2', 'ef2a2a'] //Pink
]*/
//RGBA Version of Colors
/*var gradientColors = [['rgba(239, 42, 42, 1)', 'rgba(239, 106, 42, 1)'], //Red
['rgba(239, 106, 42, 1)', 'rgba(239, 216, 42, 1)'], //Orange
['rgba(0, 221, 103, 1)', 'rgba(97, 232, 35, 1)'], //Green
['rgba(34, 210, 230, 1)', 'rgba(38, 240, 132, 1)'], //Aqua
['rgba(42, 59, 239, 1)', 'rgba(42, 220, 239, 1)'], //Blue
['rgba(239, 42, 210, 1)', 'rgba(42, 59, 239, 1)'], //Purple
['rgba(239, 42, 210, 1)', 'rgba(239, 42, 42, 1)'] //Pink
]*/

function hex(c) {
  var s = "0123456789abcdef";
  var i = parseInt(c);
  if (i == 0 || isNaN(c))
    return "00";
  i = Math.round(Math.min (Math.max (0, i), 255));
  //console.log('hex(c) complete!');
  return s.charAt((i - i % 16) / 16) + s.charAt(i % 16);
}

// Convert an RGB triplet to a hex string
function convertToHex (rgb) {
  return hex(rgb[0]) + hex(rgb[1]) + hex(rgb[2]);
}

// Convert a hex string to an RGB triplet
function convertToRGB(hex) {
  var color = [];
  color[0] = parseInt(hex.substring(0, 2), 16);
  color[1] = parseInt(hex.substring(2, 4), 16);
  color[2] = parseInt(hex.substring(4, 6), 16);
  return color;
}

function generateColor(colorStart,colorEnd,colorCount) {

    // The beginning of your gradient
    var start = convertToRGB(colorStart);

    // The end of your gradient
    var end   = convertToRGB(colorEnd);

    // The number of colors to compute
    var len = colorCount;

    //Alpha blending amount
    var alpha = 0.0;

    var saida = [];

    for (i = 0; i < len; i++) {
        var c = [];
        alpha += (1.0/len);

        c[0] = start[0] * alpha + (1 - alpha) * end[0];
        c[1] = start[1] * alpha + (1 - alpha) * end[1];
        c[2] = start[2] * alpha + (1 - alpha) * end[2];

        saida.push(convertToHex(c));

    }

    return saida;

}

var number = randomIntFromInterval(0,6)
function randomIntFromInterval(min,max) { return Math.floor(Math.random()*(max-min+1)+min); }

const GradientView = React.createClass({
  mixins: [TimerMixin],

  getInitialState() {
    return {
      gradIndex: number,
      colorTop: gradientColors[number][0],
      colorBottom: gradientColors[number][1],
    }
  },
  componentDidMount() {
    this.setInterval(() => {

      var count = 0
      var topGradArray = generateColor(gradientColorsNoHash[this.state.gradIndex][0],(this.state.gradIndex === 6 ? 0 : gradientColorsNoHash[this.state.gradIndex+1][0] ),770);
      var bottomGradArray = generateColor(gradientColorsNoHash[this.state.gradIndex][1],(this.state.gradIndex === 6 ? 0 : gradientColorsNoHash[this.state.gradIndex+1][1] ),770);
      console.log('Gradients Made');
      var clearId = this.setInterval(() => {
        if (count == 0) {
          this.setState({ clearId: clearId, gradIndex: ( this.state.gradIndex === 6 ? 0 : this.state.gradIndex+1 ) });
          console.log('clearId SET!');
            }

            this.setState({
              colorTop: processColor(topGradArray[count]),
              colorBottom: processColor(bottomGradArray[count]),
            });
            count = count+1

            if (count == 769) {
              console.log('colorTop and Bottom Saved');
              this.clearInterval(this.state.clearId)
            }
          }, 13);

    }, 30000);
  },

  render(){
    return(
      <LinearGradient colors={[this.state.colorTop, this.state.colorBottom]}>
        <View style={styles.translucentContainer}/>
      </LinearGradient>
    );
  }
});

const styles = StyleSheet.create({
  translucentContainer: {
    width: screenWidth,
    height: screenHeight,
    backgroundColor: 'white',
    opacity: 0.3,
  },
});

export default GradientView;
AppRegistry.registerComponent('GradientView', () => GradientView);

UPDATE: After going through many different resources, I've come to the conclusion that the only way to animate the LinearGradient class is to change the color incrementally and rapidly like in their documentation. However, their example is continuous and doesn't allow you to set a desired final color. For my application, I want the gradient to stay one color for 30 seconds, and then go through a 10 second transition to the next color gradient, and then repeat. So for example, it would look like: Red Gradient (30 seconds), Transition Red to Orange (10 seconds), Orange Gradient (30 seconds), Transition Orange to Green (10 seconds), etc.

I'm getting two types of error with this code that seem to alternate. Generally, the first error is this one that appears when the first timer (the 30 second one) goes off:

The first error

After dismissing that error message to see what would happen, this error pops up when the same timer goes off again:

The second error

At this point I think the source of error is in generating the colors properly in the function contained in componentDidMount()

like image 574
Armin Avatar asked Jan 19 '17 20:01

Armin


People also ask

How do you use the gradient color in react native?

An optional object of the following type: { x: number, y: number } . Coordinates declare the position that the gradient starts at, as a fraction of the overall size of the gradient, starting from the top left corner. Example: { x: 0.1, y: 0.1 } means that the gradient will start 10% from the top and 10% from the left.

How do you add a linear gradient in react native?

... <LinearGradient colors={['red', 'yellow', 'green' ]} style={styles. linearGradient} start={{ x: 0, y: 0.5 }} end={{ x: 1, y: 0.5 }} locations={[0, 0.7, 0.9]} > <Text>H. Location Gradient</Text> </LinearGradient> <LinearGradient colors={['red', 'yellow', 'green' ]} style={styles.


1 Answers

I found a working solution!

Use linear interpolation to generate your gradient. This is the simplest way I found to control the gradient properly.

chroma.js :

I found a library called chroma.js that can do this well ! They have a method called scale.colors that can do the job for you!

Install the package :

npm install chroma-js

You can adjust the INTERVAL and the GRADIENT_COLOR_LENGTH constants to change the effect.

Then use the generated spectrum variables in the code :

import React from 'react'
import { AppRegistry, StyleSheet, Dimensions, View } from 'react-native'

import TimerMixin from 'react-timer-mixin'
import LinearGradient from 'react-native-linear-gradient'
import Chroma from 'chroma-js'

var screenWidth = Dimensions.get('window').width
var screenHeight = Dimensions.get('window').height

const TOP_COLORS = ['#EF2A2A', '#EF6A2A', '#1BD170', '#22D2E6', '#2A3BEF', '#EF2AD2', '#EF2AD2']
const BOTTOM_COLORS = ['#EF6A2A', '#EFD82A', '#61E822', '#26F084', '#2ADCEF', '#2A3BEF', '#EF2A2A']
const GRADIENT_COLOR_LENGTH = 700
const TOP_COLORS_SPECTRUM = Chroma.scale(TOP_COLORS).colors(GRADIENT_COLOR_LENGTH)
const BOTTOM_COLORS_SPECTRUM = Chroma.scale(BOTTOM_COLORS).colors(GRADIENT_COLOR_LENGTH)
const INTERVAL = 50

const GradientView = React.createClass({
  mixins: [TimerMixin],

  getInitialState () {
    return {
      topIndex: 0,
      bottomIndex: 0,
      colorTop: TOP_COLORS_SPECTRUM[0],
      colorBottom: BOTTOM_COLORS_SPECTRUM[0]
    }
  },

  componentDidMount () {
    this.setInterval(() => {
      let { topIndex, bottomIndex } = this.state

      topIndex++
      if (topIndex === TOP_COLORS_SPECTRUM.length) {
        topIndex = 0
      }

      bottomIndex++
      if (bottomIndex === BOTTOM_COLORS_SPECTRUM.length) {
        bottomIndex = 0
      }

      this.setState({
        topIndex: topIndex,
        bottomIndex: bottomIndex,
        colorTop: TOP_COLORS_SPECTRUM[topIndex],
        colorBottom: BOTTOM_COLORS_SPECTRUM[bottomIndex]
      })
    }, INTERVAL)
  },

  render () {
    return (
      <LinearGradient colors={[this.state.colorTop, this.state.colorBottom]}>
        <View style={styles.translucentContainer} />
      </LinearGradient>
    )
  }
})

const styles = StyleSheet.create({
  translucentContainer: {
    width: screenWidth,
    height: screenHeight,
    backgroundColor: 'white',
    opacity: 0.3
  }
})

export default GradientView
AppRegistry.registerComponent('GradientView', () => GradientView)
like image 91
AlexB Avatar answered Oct 25 '22 12:10

AlexB