Flutter 动画剖析(二) 自定义补间动画&绘制
前言
上一篇文章分析了Flutter动画的使用方法以及常见的动画效果,这次我们结合绘制来分析下如何在绘制中使用自定义补间动画,像一些比较炫酷的加载loading效果,点赞小手的动画等,这些其实都可以称为自定义补间动画。
补间动画Tween与绘制
补间动画关键在于我们要自定义动画中的数据变化的过程以及我们想要的运动曲线,其中的数据变化过程就是由Tween
这个类控制的,例如系统内置的颜色渐变、矩形渐变都是通过继承Tween
实现的,
自定义Tween
一般情况可以组合现成的和纯自定义。
组合Tween:上一篇文章我们实现了颜色渐变和矩形大小变化,例如我们需要一个矩形组件在变化的同时进行颜色渐变,该如何做呢?默认我们实现两个Tween
即可实现,这样我们就需要向绘制组件传递两个Animation
不过我们也可以将它们组合为一个新的补间动画。
代码:
// 矩形or颜色渐变
class ColorRectTween extends Tween<ColorRect?>{
ColorRectTween({super.begin, super.end });
@override
ColorRect lerp(double t) {
return ColorRect(Color.lerp(begin?.color, end?.color, t) ?? Colors.white,
Rect.lerp(begin?.rect, end?.rect, t) ?? Rect.zero);
}
}
class ColorRect {
Color? color;
Rect? rect;
ColorRect(this.color,this.rect);
}
上方代码我们将矩形渐变和颜色渐变组合成了一个新的Tween
,这样做的好处一是可以统一管理动画的过程,二是只需传递一个动画声明即可。
代码:
var a = ColorRect(Colors.yellow, Rect.zero);
var b = ColorRect(Colors.red,
Rect.fromCenter(center: Offset.zero, width: 100, height: 100));
//最终的动画声明 由 a 到 b
late Animation<ColorRect?> _animation =
ColorRectTween(begin: a, end: b).animate(_controller);
将最终的动画声明传递给绘制组件。
class _LoadingPaint extends CustomPainter {
Animation<ColorRect?> animation;
// 加入动画需要实现super方法,设置repaint属性 repaint是一个侦听器,可以在动画变化时通知画板更新
_LoadingPaint( this.animation) : super(repaint: animation);
Paint _paint = Paint()
..color = Colors.black54
..style = PaintingStyle.fill;
@override
void paint(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
Path path = Path();
path.addRect(animation.value!.rect);
canvas.drawPath(path, _paint..color = animation.value!.color);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
效果:
改变上方动画数据开始和结束代码:
var a = ColorRect(Colors.yellow, Rect.fromCenter(center: Offset.zero, width: 50, height: 200));
var b = ColorRect(Colors.red, Rect.fromLTWH(-100,50, 200, 50));
就可以实现一个类似柱形被压扁的效果。
系统内置的其他补间动画Tween:
颜色渐变动画:ColorTween
,用来进行两色之间的渐变。
矩形渐变动画:RectTween
,用来进行两个矩形之间的动画效果,可以直接指定矩形的位置。
Size大小渐变动画:SizeTween
,用来进行Size大小的渐变。
返回四舍五入取整渐变动画:IntTween
。
@override
int lerp(double t) => (begin! + (end! - begin!) * t).round();
返回向下取整渐变动画StepTween
,和 IntTween
类似。
@override
int lerp(double t) => (begin! + (end! - begin!) * t).floor();
直接设置运动曲线动画CurveTween
,可以直接设置运动曲线,但无法自自定义动画数据。
CurveTween({ required this.curve })
自定义Tween实现自动书写文字效果
对于自动文字书写,其实掌握了原理,实现起来也非常的简单,在动画的过程中,文字作为驱动的数据,那么我们就需要自定义一个StringTween
,用来返回动画过程中的文字。实现lerp(t = 0-1)
方法,上篇文章说了,无论任何动画,他的开始和结束的原始值都是0 ~ 1
,所以这里我们需要进行对结束的文本,也就是最终展示的文本进行裁剪处理。
class StringTween extends Tween<String> {
StringTween({super.begin,super.end});
@override
String lerp(double t) {
// 返回开始加结束的文本
return "$begin${end?.substring(0,((end!.length*t).round()))}";
}
}
late AnimationController _controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 10000))
..repeat(reverse: false); ;
// 实现在10s内输出 相信技术,传递价值。
late Animation<String> _animation = StringTween(begin: '',end: "相信技术,传递价值。") .animate(_controller);
动画准备就绪,下面就是将这个动画数据传递给画板绘制文字。 绘制代码:
// 绘制文本
var textPainter = TextPainter(
text: TextSpan(
text: animation.value,//动画中的数据
style: TextStyle(
fontSize: 20,
foreground: Paint()
..style = PaintingStyle.fill
..color = Colors.red
..strokeWidth = 1,
)),
textAlign: TextAlign.left,
maxLines: 2,
ellipsis: "...",
textDirection: TextDirection.ltr);
textPainter.layout();
textPainter.paint(canvas, Offset(-size.width/2+100,0));
效果:
一般这种动画后面都会跟一个光标,这里我们修改下动画过程中返回的数据,后面加一个竖线用来充当假光标。
@override
String lerp(double t) {
// 1s内光标显示1次
if((t*100).round() % 10 <5){
return "$begin${end?.substring(0,((end!.length*t).round()))} |";
}else{
return "$begin${end?.substring(0,((end!.length*t).round()))} ";
}
}
效果:
这样,基本就实现了简单的文字自动书写的动画效果。
总结
动画与绘制是制作自定义组件非常常见的配合,无论动画数据变化还是运动曲线的变化,都是0到1的变化,只是负责的职能不同,本篇文章主要简单介绍了在绘制中如何自定义补间动画数据的变化,希望对你有所帮助~ 如有疑问,欢迎指正。
转载自:https://juejin.cn/post/7157625135690678286