颤动颜色动画产生不需要的闪烁
Flutter color animation produces unwanted flashing
我想从这种略微不透明的白色到非常高不透明度的黑色制作动画。
颜色是
Color(0xB1FFFFFF)
- 白色
Color(0x0B000000)
- 黑色
这将产生带有“闪光”的动画。然而,这不应该发生。
我做错了什么?当用 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),
),
),
);
}
}
您也可以尝试使用其他动画小部件,例如 AnimatedCrossFade
、 FadeTransition
等
我现在明白为什么会出现这个“问题”了。
由于这两种颜色在不透明度和颜色方面都有很大差异,因此在 rgba 值上按时间进行基本插值会产生这种行为。
在对 alpha 进行插值的同时,颜色也是如此,这会在两者之间创建一个临时的较暗颜色。
Dart 通过将颜色差异映射到 rgba 光谱中的时间来插值颜色。
a + (b - a) * t
This article 很好地描述了发生的情况以及其他可能的颜色插值解决方案。
使用 HSL 而不是 rgba 也可能会解决问题。但是,每次都将颜色转换为 HSL 的性能不如平面 rgba 插值。 Dart 是关于性能的,所以这可能就是他们采用这种非常基本的方法的原因。
虽然我个人对这个问题的解决方案是非常基本的。我让设计师把颜色靠得更近:)
我想从这种略微不透明的白色到非常高不透明度的黑色制作动画。
颜色是
Color(0xB1FFFFFF)
- 白色Color(0x0B000000)
- 黑色
这将产生带有“闪光”的动画。然而,这不应该发生。
我做错了什么?当用 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),
),
),
);
}
}
您也可以尝试使用其他动画小部件,例如 AnimatedCrossFade
、 FadeTransition
等
我现在明白为什么会出现这个“问题”了。 由于这两种颜色在不透明度和颜色方面都有很大差异,因此在 rgba 值上按时间进行基本插值会产生这种行为。 在对 alpha 进行插值的同时,颜色也是如此,这会在两者之间创建一个临时的较暗颜色。
Dart 通过将颜色差异映射到 rgba 光谱中的时间来插值颜色。
a + (b - a) * t
This article 很好地描述了发生的情况以及其他可能的颜色插值解决方案。
使用 HSL 而不是 rgba 也可能会解决问题。但是,每次都将颜色转换为 HSL 的性能不如平面 rgba 插值。 Dart 是关于性能的,所以这可能就是他们采用这种非常基本的方法的原因。
虽然我个人对这个问题的解决方案是非常基本的。我让设计师把颜色靠得更近:)