颤动颜色动画产生不需要的闪烁

Flutter color animation produces unwanted flashing

我想从这种略微不透明的白色到非常高不透明度的黑色制作动画。

颜色是

这将产生带有“闪光”的动画。然而,这不应该发生。

我做错了什么?当用 css 做同样的动画时,它不会“闪烁”。

这是使用 css 时的样子以及我的预期:

跳白并不重要,只是动画重新开始。

import 'dart:math';

import 'package:flutter/material.dart';

void main() => runApp(const AnimatedContainerApp());

class AnimatedContainerApp extends StatefulWidget {
  const AnimatedContainerApp({Key? key}) : super(key: key);

  @override
  _AnimatedContainerAppState createState() => _AnimatedContainerAppState();
}

class _AnimatedContainerAppState extends State<AnimatedContainerApp> {
  double _width = 50;
  double _height = 50;
  Color _color = Color(0xB1FFFFFF);
  BorderRadiusGeometry _borderRadius = BorderRadius.circular(8);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('AnimatedContainer Demo'),
        ),
        body: Center(
          child: AnimatedContainer(
            width: _width,
            height: _height,
            decoration: BoxDecoration(
              color: _color,
              borderRadius: _borderRadius,
            ),
            duration: const Duration(seconds: 1),
            curve: Curves.fastOutSlowIn,
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            setState(() {
              _color = Color(0x0B000000);
            });
          },
          child: const Icon(Icons.play_arrow),
        ),
      ),
    );
  }
}

我使用 Stack 小部件找到了替代方法。

class _AnimatedContainerAppState extends State<AnimatedContainerApp>{
  double _width = 50;
  double _height = 50;

  Color colorA = Color(0xB1FFFFFF);
  Color colorB = Color(0x9F000000); //change your color here

  double opacity = 0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.pink,
        appBar: AppBar(
          title: const Text('AnimatedContainer Demo'),
        ),
        body: Center(
          child: ClipRRect(
            borderRadius: BorderRadius.circular(8),
            child: SizedBox(
              key: const ValueKey("C"),
              width: _width,
              height: _height,
              child: Stack(
                fit: StackFit.expand,
                children: [
                  //fixed color
                  AnimatedOpacity(
                    opacity: (1 - opacity).abs(),
                    duration: const Duration(seconds: 1),
                    child: ColoredBox(color: colorA),
                  ),
                  AnimatedOpacity(
                    opacity: opacity,
                    duration: const Duration(seconds: 1),
                    child: ColoredBox(color: colorB),
                  ),
                ],
              ),
            ),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            setState(() {
              opacity = opacity == 0 ? 1 : 0;
            });
          },
          child: const Icon(Icons.play_arrow),
        ),
      ),
    );
  }
}

您也可以尝试使用其他动画小部件,例如 AnimatedCrossFadeFadeTransition

我现在明白为什么会出现这个“问题”了。 由于这两种颜色在不透明度和颜色方面都有很大差异,因此在 rgba 值上按时间进行基本插值会产生这种行为。 在对 alpha 进行插值的同时,颜色也是如此,这会在两者之间创建一个临时的较暗颜色。

Dart 通过将颜色差异映射到 rgba 光谱中的时间来插值颜色。

a + (b - a) * t

This article 很好地描述了发生的情况以及其他可能的颜色插值解决方案。

使用 HSL 而不是 rgba 也可能会解决问题。但是,每次都将颜色转换为 HSL 的性能不如平面 rgba 插值。 Dart 是关于性能的,所以这可能就是他们采用这种非常基本的方法的原因。

虽然我个人对这个问题的解决方案是非常基本的。我让设计师把颜色靠得更近:)