Flutter进阶之如何实现动画效果(二)

在上一篇文章:Flutter进阶—实现动画效果(一)的最后,我们说到需要一个处理程序混乱的概念。在这一篇文章中,我们会引入补间,它是构建动画代码的一个非常简单的

在上一篇文章: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入门学习Dart语言变量及基本如何使用概念

Flutter 异步编程之单线程下异步模型图文示例详解

如何如何使用Flutter开发一款电影APP详解

Flutter定义tabbar底部导航路由跳转的方法

Android Studio下Flutter环境搭建图文好代码教程