在React Native中实现背景色动画

80

我该如何在React Native中将一个颜色动画化为另一个颜色?我发现通过插值Animated.Value,可以通过以下方式实现颜色动画:

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)']
})

Animated.timing(this.state.color, {toValue: RED}).start();

但是如果想要从黑色到蓝色,使用这种方法,你必须经过红色。如果添加更多的颜色,你最终会进入一个1980年代的迪斯科舞厅。

有没有另一种方法可以让你直接从一种颜色到另一种颜色呢?


在你的例子中,你使用了inputRange: [BLACK, RED, BLUE]。如果你把它改成[BLACK, BLUE],那么颜色是否按照你预期的从黑色渐变到蓝色呢? - Martin Konicek
2
那个方法可以行,但与我所需的相反。我需要将颜色添加到列表中,并根据用户操作从一个颜色过渡到另一个颜色。例如,从黑色到蓝色,然后从蓝色到绿色,最后从绿色到黄色。 - nicholas
1
你解决了这个问题吗?我现在也在为同样的东西苦苦挣扎。真的很奇怪,因为看起来它对RN来说是一个真正的问题。 - Velidan
8个回答

84

假设你有一个Animated.Value,比如说x,你可以像这样插值颜色:

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>
    );
}

您可以在我在Github发布的问题中找到完整的工作示例。


1
最新的react-native版本可能存在一个bug - 尝试使用这段代码从白色插值到蓝色,但是出现了“received bad props”错误,其中期望的是'rgba(255,255,255,1.0)'而不是'rgba(255,255,255,1)'。正在进行调查。 - Dylan Pierce
7
啊,我试图在上面的代码示例中添加动画,但忘记使用Animated.View而不是普通的View。切换到<Animated.View>解决了我的问题。 - Dylan Pierce
16
我得到了 Style 属性 'backgroundColor' 不受本地动画模块支持 的提示。 - onmyway133
8
@onmyway133,您是否使用本机驱动程序?如果是,请将其设置为false。 - ebillis
6
只有我觉得这个答案没有帮助到 OP 解决如何从颜色 A 直接过渡到颜色 C 而不经过颜色 B 的问题吗?这只是在红色和绿色之间进行插值。它没有展示如何在插值范围内直接过渡任意两种颜色,无论它们在插值输出范围中的顺序如何。 - SweetFeet
显示剩余2条评论

12

如果您可以在按下按钮的瞬间获取动画颜色值的颜色,那么您可能可以做到这一点。像这样:

var currentColor = ? : 
this.state.color = 0; 
var bgColor = this.state.color.interpolate({
  inputRange: [0, 1],
  outputRange: [currentColor, targetColor]
});

所以对于每个按钮,您将设置不同的目标颜色。


1

1

我做了一个钩子来执行David Schumann告诉我的操作。

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

使用钩子:

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,
  },
});

这里是一个例子 https://snack.expo.dev/@rafaelnsantos/rude-croissant


0

通过在结尾处复制第一个颜色成功防止我的循环闪烁:

  const backgroundColourIndex = useRef(new Animated.Value(0)).current;

  let backgroundColorAnimated = backgroundColourIndex.interpolate({
    inputRange: [0, 1, 2, 3],
    outputRange: ['red', 'blue', 'yellow', 'red'],
  });

-3

您还可以按步骤进行插值,对于像颜色这样的非数字值,可以像这样:

<Animated.Text
  style={{
    color: colorAnim.interpolate({
      inputRange: [0, 0.5, 1],
      outputRange: ['black', 'gray', 'white']
  })
}}>

-3
const animatedBkg = interpolate(scale, {
  inputRange: [0, 150],
  outputRange: [Animated.color(242, 81, 48), Animated.color(0,0,0)],
  // extrapolate: Extrapolate.CLAMP,
})

已使用reanimated进行测试。


2
什么是Animated.Color? - Nick

-22
使用 setValue 和 state 来控制起始和结束颜色。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接