我们假设一种情况,如果应用程序使用条形图显示给定年份的产品类别的销售额,用户可以选择另一年,然后该应用程序将动画到该年的条形图。如果产品类别在两年内是相同的,或者恰好是相同的,除了在其中一个图表中右侧显示的其他类别,我们可以使用我们现有的代码。但如果公司在2016年有A,B,C和X类产品,但是在2017年中断了B并推出了D?

动画效果可以做得非常好看,但仍然会让用户感到困惑。为什么?因为它不保留语义。它将表示产品类别B的图形元素转换为代表类别C的一个图形元素,而将C的图形元素转换到其他地方。正因为2016 B恰好是在2017 C后来出现的同一位置,并不意味着前者应该变成后者。相反,2016年B应该消失,2016年C应该向左移动到2017年,而2017年D应该出现在右边。我们可以使用传统的合并排序列表实现这种混合。

我们可以分别给每一个条形以不同的颜色,然后使用颜色来区分销售的产品,一种颜色代表一个产品,当一种颜色消失时,说明该产品已经下架,反之,则说明新产品已经上架。

通过语义对应的组件进行复合值之间的线性插值(lerp),当组件形成排序列表时,合并算法可以将这些组件放在侧面上,使用不可见组件来处理单面合并。我们所需要的是使Bar实例以线性顺序相互比较。然后我们可以将它们合并。

// ...
class BarChart {// ...factory BarChart.random(Size size, Random random) {const barWidthFraction = 0.75;final ranks = selectRanks(random, ColorPalette.primary.length);final barCount = ranks.length;final barDistance = size.width / (1+barCount);final barWidth = barDistance * barWidthFraction;final startX = barDistance - barWidth/2;final bars = new List.generate(barCount,(i)=> new Bar(ranks[i],startX + i * barDistance,barWidth,random.nextDouble() * size.height,ColorPalette.primary[ranks[i]],),);return new BarChart(bars);}static List<int> selectRanks(Random random, int cap) {final ranks = <int>[];var rank = 0;while(true) {// 模拟产品的上架下架if(random.nextDouble() < 0.2) rank++;if(cap <= rank) break;ranks.add(rank);rank++;}return ranks;}static BarChart lerp(BarChart begin, BarChart end, double t) {final bars = <Bar>[];final bMax = begin.bars.length;final eMax = end.bars.length;var b = 0;var e = 0;while (b + e < bMax + eMax) {/*这里的条件判断中包含两种情况b < bMax && e == eMax:当新图表条形数减少时b < bMax && begin.bars[b] < end.bars[e]:当新图表不包含旧图表的颜色条形时满足一种情况,处理旧图表中多余的条形,即向左侧方清除旧条形*/if(b < bMax && (e == eMax || begin.bars[b] < end.bars[e])) {bars.add(Bar.lerp(begin.bars[b], begin.bars[b].collapsed, t));b++;/*这里的条件判断中包含两种情况e < eMax && b == bMax:当新图表条形数增加时e < eMax && end.bars[e] < begin.bars[b]:当新图表包含旧图表没有的颜色条形时满足一种情况,处理旧图表中没有的条形,即向右侧方绘制新条形*/} else if(e < eMax && (b == bMax || end.bars[e] < begin.bars[b])) {bars.add(Bar.lerp(end.bars[e].collapsed, end.bars[e], t));e++;// 当新图表与旧图表有同一个颜色条形时,原地变形} else {bars.add(Bar.lerp(begin.bars[b], end.bars[e], t));b++;e++;}}return new BarChart(bars);}
}class BarChartTween extends Tween<BarChart> {BarChartTween(BarChart begin, BarChart end) : super(begin: begin, end: end);@overrideBarChart lerp(double t) => BarChart.lerp(begin, end, t);
}class Bar {Bar(this.rank, this.x, this.width, this.height, this.color);final int rank;final double x;final double width;final double height;final Color color;Bar get collapsed => new Bar(rank, x, 0.0, 0.0, color);/*bool operator <(Duration other)如果此Duration的值小于other值,则返回truebool operator <(Duration other) => this._duration < other._duration;*/bool operator <(Bar other) => rank < other.rank;static Bar lerp(Bar begin, Bar end, double t) {assert(begin.rank == end.rank);return new Bar(begin.rank,lerpDouble(begin.x, end.x, t),lerpDouble(begin.width, end.width, t),lerpDouble(begin.height, end.height, t),Color.lerp(begin.color, end.color, t));}
}class BarTween extends Tween<Bar> {BarTween(Bar begin, Bar end) : super(begin: begin, end: end) {assert(begin.rank == end.rank);}@overrideBar lerp(double t) => Bar.lerp(begin, end, t);
}
// ...

具体来说,我们将以整数rank属性的形式为每个条形分配一个排序键。然后可以方便地使用rank来从调色板中分配每个条形的颜色,从而使我们能够跟踪动画演示中各个条形图的移动。

随机条形图现在将基于随机选择的行列。

但这不是最有效的解决方案,我们正在BarChart.lerp中重复执行合并算法,对于t的每个值都执行一次。为了解决这个问题,我们将实现前面一篇文章中提到的想法,以便在BarChartTween中存储可重用的信息。

// ...
class BarChartTween extends Tween<BarChart> {final _tweens = <BarTween>[];BarChartTween(BarChart begin, BarChart end) : super(begin: begin, end: end) {final bMax = begin.bars.length;final eMax = end.bars.length;var b = 0;var e = 0;while (b + e < bMax + eMax) {if(b < bMax && (e == eMax || begin.bars[b] < end.bars[e])) {_tweens.add(new BarTween(begin.bars[b], begin.bars[b].collapsed));b++;} else if(e < eMax && (b == bMax || end.bars[e] < begin.bars[b])) {_tweens.add(new BarTween(end.bars[e].collapsed, end.bars[e]));e++;} else {_tweens.add(new BarTween(begin.bars[b], end.bars[e]));b++;e++;}}}@overrideBarChart lerp(double t) => new BarChart(new List.generate(_tweens.length,(i) => _tweens[i].lerp(t)));
}
// ...

然后我们就可以删除静态的BarChart.lerp方法了。

未完待续~~~

Flutter进阶—实现动画效果(七)相关推荐

  1. Flutter进阶—实现动画效果(四)

    在上一篇文章:Flutter进阶-实现动画效果(三)中,实现了一个随机高度.颜色的条形.这一篇文章我们会实现多个条形,同样是随机高度.颜色. 首先在bar.dart中创建BarChart类,并使用固定 ...

  2. Flutter进阶—实现动画效果(三)

    在上一篇文章:Flutter进阶-实现动画效果(二)的最后,我们实现了一个控件,其中包含各种布局和状态处理控件.以及使用自定义的动画感知绘图代码绘制单个Bar的控件.还有一个浮动按钮控件,用于启动条形 ...

  3. Flutter进阶—实现动画效果(二)

    在上一篇文章:Flutter进阶-实现动画效果(一)的最后,我们说到需要一个处理程序混乱的概念.在这一篇文章中,我们会引入补间,它是构建动画代码的一个非常简单的概念,主要作用是用面向对象的方法替代之前 ...

  4. Flutter进阶—实现动画效果(一)

    上一篇文章我们了解了Flutter的动画基础:Flutter进阶-解析动画,这一篇文章我们就来实现一个图表的动画效果. 首先,我们需要创建一个新项目myapp,然后把main.dart的内容替换成下面 ...

  5. Flutter进阶—实现动画效果(十)

    前面的两篇文章[动画效果(八).动画效果(九)]中,我们只需要统计产品和地区,如果现在增加一个统计项目--销售渠道,那么使用之前的堆叠条形图和分组条形图都不适合.我们可以将两者结合,使用分组+堆叠条形 ...

  6. Flutter进阶—实现动画效果(八)

    通过学习前面的文章,我们现在终于能为更复杂的图表制作动画效果了[手动斜眼笑].接着上一篇文章讲,如果公司的产品销往全国各地,那么我们的图表要展示的内容就需要加上地区.我们可以使用堆叠条形图来试试效果, ...

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

    在本篇文章开始前,我们先来回顾一下之前我们都做了哪些事情.在第一篇文章中,我们在动画值更改时调用double lerpDouble(num a, num b, double t)重新绘制条形.在第二篇 ...

  8. Flutter进阶—实现动画效果(九)

    在上一篇文章中,我们实现了统计每个产品和地区的销售额,如果现在需要统计每个产品和地区所占市场份额的百分比,那么使用堆叠条形图是不合适的,我们可以使用分组条形图,因为它可以同时在两个类别维度上进行定量比 ...

  9. Flutter进阶—实现动画效果(六)

    在上一篇文章中,我们之前对BarChart.lerp的定义并不是高效的,我们正在创建的Bar实例,仅作为Bar.lerp的参数给出,并且针对动画参数t的每个值重复出现.每秒60帧,这意味着可能很多Ba ...

最新文章

  1. 偏度与峰度的正态性分布判断
  2. 开发工具:VSCode 摸鱼神器,确定不试一下?
  3. java中不带package和带package的编译运行方式
  4. HttpServletRequest 常用方法讲解
  5. ARP 地址解析协议
  6. Python格式化字符串f-string常用用法
  7. Android 使用dagger2进行依赖注入(基础篇)
  8. 《锋利的jQuery》bug总结(1)
  9. 前端面试基础题:请写出至少20个HTML5标签
  10. [电影]《指环王》新老三部曲完全赏析(王者归来)
  11. input file类型单个文件上传formData
  12. JavaScript中throw的错误异常处理
  13. 清除“我的电脑”地址栏中的记录
  14. 什么是闰年?闰年为什么是366天?为什么不能是100的倍数?
  15. SWFUpload多文件上传使用指南
  16. Java实现云端存储、短信、邮件、沙盒支付
  17. c语言应用(2)试卷管理
  18. ASA5520防火墙基本配置
  19. Godaddy域名解析
  20. 微软Bing的AI人工只能对话体验名额申请教程

热门文章

  1. 最新 Molecular Operating Environment (MOE) Linux Windows
  2. Nginx配置https和wss
  3. supervisor+gunicorn部署python web项目
  4. 【QCustomPlot】1.2 - QCustomPlot绘制静态曲线、常用函数的功能说明
  5. 【Qt教程】2.2 - Qt5 布局管理器(水平、垂直、栅格布局)、弹簧、设计一个登陆界面
  6. 随记 elasticsearch
  7. 火星人敏捷开发手册2012-04-30新增敏捷日常跟进内容
  8. bootstrapTable 取消 “正在加载中...”
  9. emmet 工具的基本使用,总结
  10. MyBatis 【中文编码问题】