在上一篇文章:Flutter进阶—实现动画效果(一)的最后,我们说到需要一个处理程序混乱的概念。在这一篇文章中,我们会引入补间,它是构建动画代码的一个非常简单的概念,主要作用是用面向对象的方法替代之前面向过程的方法。tween是一个值,它描述了其他值的空间中的两个点之间的路径,比如条形图的动画值从0运行到1。
补间在Dart中表示类型为Tween的对象
abstract class Tween<T> { final T begin; final T end; Tween(this.begin, this.end); T lerp(double t); }
术语lerp来自计算机图形学领域,是线性插值(作为名词)和线性内插(作为动词)的缩写。参数t是动画值,补间应该从begin(当t为0时)到end(当t为1时)。
FlutterSDK的Tween类与Dart非常相似,但是一个支持变化begin和end的具体类。我们可以使用单个Tween来整理代码,用于处理条形图高度。
import 'package:flutter/material.dart'; import 'package:flutter/animation.dart'; import 'dart:math'; void main() { runApp(new MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', home: new MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin { final random = new Random(); int dataSet = 50; AnimationController animation; Tween<double> tween; @override void initState() { super.initState(); animation = new AnimationController( duration: const Duration(milliseconds: 300), vsync: this ); // Tween({T begin, T end }):创建tween(补间) tween = new Tween<double>(begin: 0.0, end: dataSet.toDouble()); animation.forward(); } @override void dispose() { animation.dispose(); super.dispose(); } void changeData() { setState(() { dataSet = random.nextInt(100); tween = new Tween<double>( /* @override T evaluate( Animation<double> animation ) 返回给定动画的当前值的内插值 当动画值分别为0.0或1.0时,此方法返回begin和end */ begin: tween.evaluate(animation), end: dataSet.toDouble() ); animation.forward(from: 0.0); }); } @override Widget build(BuildContext context) { return new Scaffold( body: new Center( child: new CustomPaint( size: new Size(200.0, 100.0), /* Animation<T> animate( Animation<double> parent ) 返回一个由给定动画驱动的新动画,但它承担由该对象确定的值 */ painter: new BarChartPainter(tween.animate(animation)) ) ), floatingActionButton: new FloatingActionButton( onPressed: changeData, child: new Icon(Icons.refresh), ), ); } } class BarChartPainter extends CustomPainter { static const barWidth = 10.0; BarChartPainter(Animation<double> animation) : animation = animation, super(repaint: animation); final Animation<double> animation; @override void paint(Canvas canvas, Size size) { final barHeight = animation.value; final paint = new Paint() ..color = Colors.blue[400] ..style = PaintingStyle.fill; canvas.drawRect( new Rect.fromLTWH( size.width-barWidth/2.0, size.height-barHeight, barWidth, barHeight ), paint ); } @override bool shouldRepaint(BarChartPainter old) => false; }
我们使用Tween将条形高度动画终点包装在一个值中,它完全与AnimationController和CustomPainter进行接口,因为Flutter框架现在会在每个动画时间点上标记CustomPaint进行重绘,而不是将整个MyHomePage子树标记为重构、重新布局和重绘。这些都是显示的改进,但是,补间的概念不止如此,它提供了组织我们的想法和代码的结构。
回到我们的代码,我们需要一个Bar类型和一个BarTween来动画化它。我们将与bar相关的类提取到bar.dart文件中,放到main.dart同级目录下。
import 'package:flutter/material.dart'; import 'package:flutter/animation.dart'; import 'dart:ui' show lerpDouble; class Bar { Bar(this.height); final double height; static Bar lerp(Bar begin, Bar end, double t) { return new Bar(lerpDouble(begin.height, end.height, t)); } } class BarTween extends Tween<Bar> { BarTween(Bar begin, Bar end) : super(begin: begin, end: end); @override Bar lerp(double t) => Bar.lerp(begin, end, t); } class BarChartPainter extends CustomPainter { static const barWidth = 10.0; BarChartPainter(Animation<Bar> animation) : animation = animation, super(repaint: animation); final Animation<Bar> animation; @override void paint(Canvas canvas, Size size) { final bar = animation.value; final paint = new Paint() ..color = Colors.blue[400] ..style = PaintingStyle.fill; canvas.drawRect( new Rect.fromLTWH( size.width-barWidth/2.0, size.height-bar.height, barWidth, bar.height ), paint ); } @override bool shouldRepaint(BarChartPainter old) => false; }
我们遵循FlutterSDK的惯例来定义Bar类的静态方法BarTween.lerp。DartSDK中没有double.lerp,所以我们使用dart:ui包中的lerpDouble函数来达到同样的效果。
现在我们的应用程序可以用条形图重新显示。
import 'package:flutter/material.dart'; import 'package:flutter/animation.dart'; import 'dart:math'; import 'bar.dart'; void main() { runApp(new MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', home: new MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => new _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin { final random = new Random(); AnimationController animation; BarTween tween; @override void initState() { super.initState(); animation = new AnimationController( duration: const Duration(milliseconds: 300), vsync: this ); tween = new BarTween(new Bar(0.0), new Bar(50.0)); animation.forward(); } @override void dispose() { animation.dispose(); super.dispose(); } void changeData() { setState(() { tween = new BarTween( tween.evaluate(animation), new Bar(100.0 * random.nextDouble()), ); animation.forward(from: 0.0); }); } @override Widget build(BuildContext context) { return new Scaffold( body: new Center( child: new CustomPaint( size: new Size(200.0, 100.0), painter: new BarChartPainter(tween.animate(animation)) ) ), floatingActionButton: new FloatingActionButton( onPressed: changeData, child: new Icon(Icons.refresh), ), ); } }
未完待续。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍Flutter进阶之实现动画效果(十),包括了Flutter进阶之实现动画效果(十)的使用技巧和注意事项,需要的朋友参考一下 前面的两篇文章【动画效果(八) 、动画效果(九) 】中,我们只需要统计产品和地区,如果现在增加一个统计项目——销售渠道,那么使用之前的堆叠条形图和分组条形图都不适合。我们可以将两者结合,使用分组+堆叠条形图,实际效果如下图所示: 如上图,我们使用同一种颜色的不
本文向大家介绍Flutter进阶之实现动画效果(九),包括了Flutter进阶之实现动画效果(九)的使用技巧和注意事项,需要的朋友参考一下 在上一篇文章中,我们实现了统计每个产品和地区的销售额,如果现在需要统计每个产品和地区所占市场份额的百分比,那么使用堆叠条形图是不合适的,我们可以使用分组条形图,因为它可以同时在两个类别维度上进行定量比较。分组条形图的实际效果如下图所示: 要实现上面的效果,我们
本文向大家介绍Flutter进阶之实现动画效果(一),包括了Flutter进阶之实现动画效果(一)的使用技巧和注意事项,需要的朋友参考一下 上一篇文章我们了解了Flutter的动画基础,这一篇文章我们就来实现一个图表的动画效果。 首先,我们需要创建一个新项目myapp,然后把main.dart的内容替换成下面的代码 启动项目后,应用程序会显示一个居中的文本标签,显示“数据集:null”和浮动按钮来
本文向大家介绍Flutter进阶之实现动画效果(三),包括了Flutter进阶之实现动画效果(三)的使用技巧和注意事项,需要的朋友参考一下 在上一篇文章:Flutter进阶—实现动画效果(二)的最后,我们实现了一个控件,其中包含各种布局和状态处理控件。以及使用自定义的动画感知绘图代码绘制单个Bar的控件。还有一个浮动按钮控件,用于启动条形图高度的动画变化。 现在开始向我们的单个条形添加颜色,在Ba
本文向大家介绍Flutter进阶之实现动画效果(四),包括了Flutter进阶之实现动画效果(四)的使用技巧和注意事项,需要的朋友参考一下 在上一篇文章:Flutter进阶—实现动画效果(三)中,实现了一个随机高度、颜色的条形。这一篇文章我们会实现多个条形,同样是随机高度、颜色。 首先在bar.dart中创建BarChart类,并使用固定长度的Bar实例列表。我们将使用5个条形,表示一周的5个工作
本文向大家介绍Flutter进阶之实现动画效果(五),包括了Flutter进阶之实现动画效果(五)的使用技巧和注意事项,需要的朋友参考一下 在本篇文章开始前,我们先来回顾一下之前我们都做了哪些事情。在第一篇文章中,我们在动画值更改时调用double lerpDouble(num a, num b, double t)重新绘制条形。在第二篇文章中,我们首先用Tween类帮助我们管理动画值,并重新绘制