加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Flutter进阶—实现动画效果(五)

发布时间:2020-12-14 14:53:30 所属栏目:百科 来源:网络整理
导读:在本篇文章开始前,我们先来回顾一下之前我们都做了哪些事情。在第一篇文章中,我们在动画值更改时调用double lerpDouble(num a,num b,double t)重新绘制条形。在第二篇文章中,我们首先用Tween类帮助我们管理动画值,并重新绘制条形,然后把绘制条形动画相

在本篇文章开始前,我们先来回顾一下之前我们都做了哪些事情。在第一篇文章中,我们在动画值更改时调用double lerpDouble(num a,num b,double t)重新绘制条形。在第二篇文章中,我们首先用Tween类帮助我们管理动画值,并重新绘制条形,然后把绘制条形动画相关的类提取到bar.dart文件。在第三篇文章中,我们首先在Bar类中增加颜色的字段,再新建color_palette.dart文件,用于获取颜色值,同时用工厂构造函数Bar.empty和Bar.random分别创建空白Bar实例和随机Bar实例。在第四篇文章中,我们新增了BarChart类,用于创建指定数量的Bar实例列表,并将绘制条形的代码更改为绘制条形图。

接下来,我们为Bar类增加x坐标和宽度属性,然后我们使BarChart支持具有不同列数的图表。我们的新图表将适用于数据集,其中bar i代表某些系列中的第i个值,如产品发布后第i天的销售额。这样的图表涉及0..n个条形,但一个图表的条形数量n可能不同于下一个图表。

比如有两个图表,分别有5个和7个条形。5个条形的表格可以按照之前的方法进行动画化。bars的索引5和6在另一个动画终点没有对方,但是现在我们可以自由地给每个条形自己的位置和宽度,我们可以引入两个不可见的条形来扮演这个角色。视觉上效果是随着动画的进行,bars的索引5和6成长为最终的外观。如果是相反方向的动画,则bars的索引5和6将会减弱或淡入隐形。

复合值之间的线性插值(lerp)通过相应的组件相关联,如果某个组件在一个终点丢失,则在其位置使用一个不可见组件。通常有几种方法来选择不可见的组件,假设我们的产品经理决定使用零宽度、零高度的条形,其x坐标和颜色从其可见对象继承,我们将为Bar添加一个方法来创建给定实例的collapsed版本。

import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
import 'dart:ui' show lerpDouble;
import 'dart:math';
import 'color_palette.dart';

class BarChart {
  final List<Bar> bars;

  BarChart(this.bars);

  factory BarChart.empty(Size size) {
    return new BarChart(<Bar>[]);
  }

  factory BarChart.random(Size size,Random random) {
    const barWidthFraction = 0.75;
    const minBarDistance = 20.0;
    // floor():返回不大于此的最大整数
    final barCount = random.nextInt((size.width/minBarDistance).floor()) + 1;
    final barDistance = size.width / (1+barCount);
    final barWidth = barDistance * barWidthFraction;
    final startX = barDistance - barWidth/2;
    final color = ColorPalette.primary.random(random);
    final bars = new List.generate(
      barCount,(i)=> new Bar(
        startX + i * barDistance,barWidth,random.nextDouble() * size.height,color,),);
    return new BarChart(bars);
  }

  static BarChart lerp(BarChart begin,BarChart end,double t) {
    // max:返回两个数字中较大的一个
    final barCount = max(begin.bars.length,end.bars.length);
    final bars = new List.generate(
      barCount,(i) => Bar.lerp(
        // ??:如果为空时取的默认值
        begin._barOrNull(i) ?? end.bars[i].collapsed,end._barOrNull(i) ?? begin.bars[i].collapsed,t,)
    );
    return new BarChart(bars);
  }

  Bar _barOrNull(int index) => (index<bars.length ? bars[index] : null);
}

class BarChartTween extends Tween<BarChart> {
  BarChartTween(BarChart begin,BarChart end) : super(begin: begin,end: end);

  @override
  BarChart lerp(double t) => BarChart.lerp(begin,end,t);
}

class Bar {
  Bar(this.x,this.width,this.height,this.color);
  final double x;
  final double width;
  final double height;
  final Color color;

  Bar get collapsed => new Bar(x,0.0,color);

  static Bar lerp(Bar begin,Bar end,double t) {
    return new Bar(
        lerpDouble(begin.x,end.x,t),lerpDouble(begin.width,end.width,lerpDouble(begin.height,end.height,Color.lerp(begin.color,end.color,t)
    );
  }
}

class BarTween extends Tween<Bar> {
  BarTween(Bar begin,Bar end) : super(begin: begin,end: end);

  @override
  Bar lerp(double t) => Bar.lerp(begin,t);
}

class BarChartPainter extends CustomPainter {
  BarChartPainter(Animation<BarChart> animation)
      : animation = animation,super(repaint: animation);

  final Animation<BarChart> animation;

  @override
  void paint(Canvas canvas,Size size) {
    final paint = new Paint()..style = PaintingStyle.fill;
    final chart = animation.value;
    for(final bar in chart.bars) {
      paint.color = bar.color;
      canvas.drawRect(
        new Rect.fromLTWH(
          bar.x,size.height - bar.height,bar.width,bar.height
        ),paint
      );
    }
  }

  @override
  bool shouldRepaint(BarChartPainter old) => false;
}

将上述代码整合到我们的应用程序中,包括为此新设置重新定义BarChart.random和BarChart.empty。现在可以合理地使用空白图表来包含空图表零条形,而随机的条形图可以包含所有相同随机颜色的随机数量的条形,并且每个具有随机选择的高度。但是由于位置和宽度现在是Bar定义的一部分,我们需要BarChart.random来指定这些属性。为BarChart.random提供图表Size参数是合理的,可以缓解BarChartPainter.paint的大部分计算。

最后我们需要更新main.dart文件,让我们的应用程序可以重新显示。

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
  static const size = const Size(200.0,100.0);
  // ...
  @override
  void initState() {
    // ...
    tween = new BarChartTween(
      new BarChart.empty(size),new BarChart.random(size,random));
    animation.forward();
  }
  // ...
  void changeData() {
    setState(() {
      tween = new BarChartTween(
        tween.evaluate(animation),new  BarChart.random(size,random),);
      animation.forward(from: 0.0);
    });
  }
  // ...
  }
}

未完待续~~~

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读