Flutter进阶—实现动画效果(七)
我们假设一种情况,如果应用程序使用条形图显示给定年份的产品类别的销售额,用户可以选择另一年,然后该应用程序将动画到该年的条形图。如果产品类别在两年内是相同的,或者恰好是相同的,除了在其中一个图表中右侧显示的其他类别,我们可以使用我们现有的代码。但如果公司在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进阶—实现动画效果(七)相关推荐
- Flutter进阶—实现动画效果(四)
在上一篇文章:Flutter进阶-实现动画效果(三)中,实现了一个随机高度.颜色的条形.这一篇文章我们会实现多个条形,同样是随机高度.颜色. 首先在bar.dart中创建BarChart类,并使用固定 ...
- Flutter进阶—实现动画效果(三)
在上一篇文章:Flutter进阶-实现动画效果(二)的最后,我们实现了一个控件,其中包含各种布局和状态处理控件.以及使用自定义的动画感知绘图代码绘制单个Bar的控件.还有一个浮动按钮控件,用于启动条形 ...
- Flutter进阶—实现动画效果(二)
在上一篇文章:Flutter进阶-实现动画效果(一)的最后,我们说到需要一个处理程序混乱的概念.在这一篇文章中,我们会引入补间,它是构建动画代码的一个非常简单的概念,主要作用是用面向对象的方法替代之前 ...
- Flutter进阶—实现动画效果(一)
上一篇文章我们了解了Flutter的动画基础:Flutter进阶-解析动画,这一篇文章我们就来实现一个图表的动画效果. 首先,我们需要创建一个新项目myapp,然后把main.dart的内容替换成下面 ...
- Flutter进阶—实现动画效果(十)
前面的两篇文章[动画效果(八).动画效果(九)]中,我们只需要统计产品和地区,如果现在增加一个统计项目--销售渠道,那么使用之前的堆叠条形图和分组条形图都不适合.我们可以将两者结合,使用分组+堆叠条形 ...
- Flutter进阶—实现动画效果(八)
通过学习前面的文章,我们现在终于能为更复杂的图表制作动画效果了[手动斜眼笑].接着上一篇文章讲,如果公司的产品销往全国各地,那么我们的图表要展示的内容就需要加上地区.我们可以使用堆叠条形图来试试效果, ...
- Flutter进阶—实现动画效果(五)
在本篇文章开始前,我们先来回顾一下之前我们都做了哪些事情.在第一篇文章中,我们在动画值更改时调用double lerpDouble(num a, num b, double t)重新绘制条形.在第二篇 ...
- Flutter进阶—实现动画效果(九)
在上一篇文章中,我们实现了统计每个产品和地区的销售额,如果现在需要统计每个产品和地区所占市场份额的百分比,那么使用堆叠条形图是不合适的,我们可以使用分组条形图,因为它可以同时在两个类别维度上进行定量比 ...
- Flutter进阶—实现动画效果(六)
在上一篇文章中,我们之前对BarChart.lerp的定义并不是高效的,我们正在创建的Bar实例,仅作为Bar.lerp的参数给出,并且针对动画参数t的每个值重复出现.每秒60帧,这意味着可能很多Ba ...
最新文章
- 偏度与峰度的正态性分布判断
- 开发工具:VSCode 摸鱼神器,确定不试一下?
- java中不带package和带package的编译运行方式
- HttpServletRequest 常用方法讲解
- ARP 地址解析协议
- Python格式化字符串f-string常用用法
- Android 使用dagger2进行依赖注入(基础篇)
- 《锋利的jQuery》bug总结(1)
- 前端面试基础题:请写出至少20个HTML5标签
- [电影]《指环王》新老三部曲完全赏析(王者归来)
- input file类型单个文件上传formData
- JavaScript中throw的错误异常处理
- 清除“我的电脑”地址栏中的记录
- 什么是闰年?闰年为什么是366天?为什么不能是100的倍数?
- SWFUpload多文件上传使用指南
- Java实现云端存储、短信、邮件、沙盒支付
- c语言应用(2)试卷管理
- ASA5520防火墙基本配置
- Godaddy域名解析
- 微软Bing的AI人工只能对话体验名额申请教程
热门文章
- 最新 Molecular Operating Environment (MOE) Linux Windows
- Nginx配置https和wss
- supervisor+gunicorn部署python web项目
- 【QCustomPlot】1.2 - QCustomPlot绘制静态曲线、常用函数的功能说明
- 【Qt教程】2.2 - Qt5 布局管理器(水平、垂直、栅格布局)、弹簧、设计一个登陆界面
- 随记 elasticsearch
- 火星人敏捷开发手册2012-04-30新增敏捷日常跟进内容
- bootstrapTable 取消 “正在加载中...”
- emmet 工具的基本使用,总结
- MyBatis 【中文编码问题】