周末发表了一篇文章《这个项目也太屌了吧》,给大家推荐了一个炫酷的Flutter粒子时钟项目,不过没有将具体实现思路和代码,所幸,作者自己写了一篇博客将这个项目的背景、实现思路、和所遇到的问题,我觉得对非常有用,因此翻译出来,整理给大家!原文题目《我是如何创建粒子时钟,并赢得了#FlutterClock挑战的》

背景

Google在2019年11月18日发起了The Flutter Clock Challenge挑战活动,内容很简单:使用Flutter UI工具包设计时钟。Google专家小组将根据四个主要标准对参赛作品进行评判:视觉美感创意新颖性代码质量整体执行力

在这之前,我只使用过Flutter一两次,这对于我来说是一个潜在的机会。

最初的想法

挑战开始了大约两周后,我才有了一些想法,但还没有编写任何代码,解决此类问题时,我通常的方法是首先寻找现有的解决方案来找灵感。但这次不行。相反,我在Figma上新建了一个文档,并列出了一些想法。它们都是非常简单数字时钟设计晦涩的单色组合

很快地,我就对这个设计感到厌倦了,所以我关闭了Figma,就去下载了Flutter Clock GitHub(https://github.com/flutter/flutter_clock)库,以查看一些示例代码。

这个库包含了2个项目,一个是基于模拟时钟的演示,另一个是基于数字时钟的演示,我在Figma上设计都是数字的,所以自然而然地,我启动了基本的数字时钟项目。再次缺乏灵感,在示例项目的帮助下,我搁置了挑战,开始去做其他事儿。

几天之后,在一次晨跑中,我又开始认真的思考这个挑战,一个普通的成年人每天看几次手表?对我来说,让时钟变得有趣起来是真正的挑战。是否可以使“报时间”成为自动体验?比如:即使您对时间不感兴趣,看着手表也很有趣。这不仅需要视觉上令人惊叹的设计新颖的动画方案

  • 1、如果每次您看钟时都有不同的外观会怎样?
  • 2、我们能否激发好奇心,使您渴望下一次设计迭代?或者,当您喜欢的设计永久消失时,您会感到难过吗?
  • 3、我们是否可以将背景形状、颜色、动画随机改变以使它:a. 看起来很酷,b. 足够满足1和2的随机性,c. 不会分散你看时间的注意力?

我以前从未完成过任何艺术,或者根本没有做过Flutter,所以我着手建造这样的时钟。

粒子与随机性

第一次迭代只是一个时钟。如上所述,我从示例数字时钟项目开始,而不是从头开始。创建的第一个Widget是一个CustomPainter,仅绘制了一个圆圈。很不错,但是从长远来看不是很有趣。

随机性增加了,从颜色开始,然后确定位置和大小。所有逻辑仍然在单个CustomPainterpaint()方法中,这几乎使动画变得不可能,因此需要将一堆逻辑重构成一个简单的粒子系统。我看了Flutter Vignettes项目,以寻求启发。

https://flutter.gskinner.com/

这时候,制作模拟粒子时钟的想法变得更加明显了。

将粒子变成模拟时钟

提出想法后,我要做的就是编写代码以实现所有目标。数学部分花费了我最多的时间才最终完成,大多是我多年以前学过的数学,不过好多我都忘记了,角度,弧度,PI和类似的东西,网上又许多解决方案,但是你将不得不做一些修改以适应您的用例。

以下是获取时针弧度的方法:

/// Gets the radians of the hour hand.
double _getHourRadians() =>(time.hour * pi / 6) +(time.minute * pi / (6 * 60)) +(time.second * pi / (360 * 60));

我在计算中包括了time.minutetime.second,以使时针在数小时之间平滑地动画。

然后,从弧度获得2D运动矢量就很简单了。

// Particle movement vector.
p.vx = sin(-angle);
p.vy = cos(-angle);

现在,p.vxp.vy拥有 有关粒子在每个动画滴答声中应该移动多远的信息,同时保持在时针的角度里。

除了钟针外,粒子还可能作为噪音产生。然后它将从中心沿随机方向发射。在发出时,还将为所有粒子分配随机的速度颜色大小绘画样式(填充或笔划)。这使时钟看起来更有趣。

时钟的早期版本中。这有四分之一标记,有些粒子有速度标记。时钟的第一个版本只是粒子,这里就不花时间截图演示了。

使用Flutter Widgets 添加图层

到现在为止,所有内容都使用单个CustomPainter小部件(即widget,下文也一样)绘制。时钟看起来不错,但是很难告诉你时间。而且,背景是单色,看上去很无聊。

Flutter非常适合构建复杂的布局。毕竟,它是一个用于用户界面的工具包。将一堆小部件彼此堆叠,您只需将它们包装在Stack widget中。粒子时钟的最后一个场景小部件负责构建3个主要层:

  • 1、Background:一个带有CustomPaint小部件的堆栈,该小部件绘制不同颜色和绘画样式的随机形状,以及一个应用了模糊效果的BackdropFilter

  • 2、Clock Face: 带有2个CustomPaint 小部件的堆栈,

    • a. 时钟标记-绘制时钟标记。每分钟标记,每5分钟标记具有额外的可见性。
    • b. 秒针-绘制两秒的针弧。
  • 3、 Particle FX: 一个CustomPaint小部件,用于绘制所有粒子。

@override
Widget build(BuildContext context) {return AnimatedContainer(duration: Duration(milliseconds: 1500),curve: Curves.easeOut,color: _bgColor,child: ClipRect(child: Stack(children: <Widget>[_buildBgBlurFx(),_buildClockFace(),CustomPaint(painter: ClockFxPainter(fx: _fx),child: Container(),),],),),);
}

即使底层代码很复杂,Flutter仍可以通过小部件组合来管理布局。

时钟绘图层和覆盖层。

这是与上述相同的图片,但没有覆盖层。

与时间同步的动画

我很早就想到,如果动画与时钟的滴答声同步发生,那就太酷了。最终版本中的解决方案非常简单。没有到达到想象中的目标。最初,我把它变成了一个非常复杂的问题,并尝试了各种怪异的技巧使它起作用。

@override
void tick(Duration duration) {var secFrac = DateTime.now().millisecond / 1000;var vecSpeed = duration.compareTo(easingDelayDuration) > 0? max(.2, Curves.easeInOutSine.transform(1 - secFrac)): 1;particles.asMap().forEach((i, p) {// Movementp.x -= p.vx * vecSpeed;p.y -= p.vy * vecSpeed;// etc...}
}

以上代码在每个动画刻度上运行。通过结合使用DateTime.now()(以毫秒为单位)和Curves,我们得到一个介于01之间的值。max函数确保该数字保持在0.2以上,以始终保持粒子随着每个刻度移动。

然后,在计算粒子的新xy位置时,将vecSpeed编号与运动矢量结合使用。

调色板和清晰度

在图形用户界面中随机分配颜色时,通常会让人感到厌烦。当然,这是有充分的理由,因为它通常会使GUI的访问性降低。在保持易读性的同时将随机颜色应用于GUI并不是一个容易解决的问题。幸运的是,Flutter有一些工具可以使我们更轻松。

首先,我使用了ColourLovers API来获取其用户最喜欢的一些调色板。简而言之,许多调色板的颜色之间的对比度很差。我根据WCAG Contrast指南,创建了一个过滤调色板阵列的脚本来解决了这一问题。过滤后,该列表仅包含调色板,其中至少存在一种对比度大于或等于4.5的颜色组合。

然后,在Flutter中,我们仅需使用Color类的computeLuminance方法即可找到良好的匹配项。

/// Gets a random palette from a list of palettes and sorts its'
/// colors by luminance.
///
/// Given if [dark] or not, this method makes sure the luminance
/// of the background color is valid.
static Palette getPalette(List<Palette> palettes, bool dark) {Palette result;while (result == null) {Palette palette = Rnd.getItem(palettes);List<Color> colors = Rnd.shuffle(palette.components);var luminance = colors[0].computeLuminance();if (dark ? luminance <= .1 : luminance >= .1) {var lumDiff = colors.sublist(1).asMap().map((i, color) => MapEntry(i,[i, (luminance - color.computeLuminance()).abs()],),).values.toList();lumDiff.sort((List<num> a, List<num> b) {return a[1].compareTo(b[1]);});List<Color> sortedColors =lumDiff.map((d) => colors[d[0] + 1]).toList();result = Palette(components: [colors[0]] + sortedColors,);}}return result;
}

该代码返回一个Palette,仅包含一个颜色列表。通过颜色之间的亮度差异调色板进行排序。

然后,此方法的调用者可以确保组件的第一项最后一项具有足够好的对比度。

一小部分,可能有许多不同颜色变化。请注意,就亮度而言,强调色始终总是与背景色最远的一种。

最后的润色

大部分魔术发生在编写此代码的最后几个小时。使发出的粒子从中心淡入,而不是从无处突然弹出。这使整体外观更加平滑。我对弧/速度标记进行了相同的操作,并一次将它们限制为仅几个粒子,以减少视觉复杂性。

最初,我不确定如何避免沿钟针方向发射噪声粒子,但知道必须这样做。在放弃寻找数学解之后,我用了一些蛮力的代码解决了这个问题(当然,数学解方案是有的,只是我没有耐心寻找到它)。

// Find a random angle while avoiding clutter at the hour & minute hands.
var am = _getMinuteRadians();
var ah = _getHourRadians() % (pi * 2);
var d = pi / 18;// Probably not the most efficient solution right here.
do {angle = Rnd.ratio * pi * 2;
} while (_isBetween(angle, am - d, am + d) || _isBetween(angle, ah - d, ah + d));

有效!这样一来,所有噪音颗粒都从针中移开,就更容易分辨时间了。

最后

有时令人沮丧(谢谢,数学!

Flutter自定义Widget实例 -如何创建炫酷粒子时钟效果!相关推荐

  1. Flutter自定义Widget之炫酷粒子时钟效果

    周末发表了一篇文章<这个项目也太屌了吧>,给大家推荐了一个炫酷的Flutter粒子时钟项目,不过没有将具体实现思路和代码,所幸,作者自己写了一篇博客将这个项目的背景.实现思路.和所遇到的问 ...

  2. android 炫酷进度条,Android打造炫酷进度条效果

    本文实例为大家分享了Android炫酷进度条效果的具体代码,供大家参考,具体内容如下 HorizontalProgressbarWithProgress的代码 import android.conte ...

  3. [译] CSS 变量实现炫酷鼠标悬浮效果

    原文: Stunning hover effects with CSS variables 我的博客:[译] CSS 变量实现炫酷鼠标悬浮效果 我最近从Grover网站上的有趣的悬停动画中获得灵感.将 ...

  4. 用html实现炫酷罗马时钟

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200430141647148.gi ## 炫酷罗马时钟 我们看一下运行效果 这是代码可以复制 <!DOCT ...

  5. 使用vue3+element-plus+particles实现炫酷(粒子背景)登陆界面

    效果图如下: 撸代码前准备(从无到有): 1.导入element-plus: # yarn yarn add element-plus # npm npm install element-plus - ...

  6. 那些你不知道的炫酷按钮交互效果

    在日常开发中肯定都遇到过不少有按钮交互的地方,但你有用到什么炫酷的交互效果吗?日常开发中想必最多的就是点击按钮背景文字变色,阴影,按钮按下,加载中之类的效果,相对都比较平平无奇,都是一些按部就班的效果 ...

  7. css3个性loading,css3 中实现炫酷的loading效果

    •今天实现了一个炫酷的loading效果,基本全用css来实现,主要练习一下css3的熟练运用 •js需要引入jquery 只用到了一点点js •先看效果图 html: 加载中 . . . css: ...

  8. H5炫酷特效系列4——炫酷粒子变化特效

    这个示例虽然没有相对比较实用的特性,但是表现出来的效果确实非常震撼眼球.先上效果图,让各位同胞感受一下: 特效的表现感很强,有兴趣的读者可以仔细学习一下下面的代码,尝试着理解其中的逻辑. <!D ...

  9. android svg动画框架,Android实现炫酷SVG动画效果

    svg是目前十分流行的图像文件格式了,svg严格来说应该是一种开放标准的矢量图形语言,使用svg格式我们可以直接用代码来描绘图像,可以用任何文字处理工具打开svg图像,通过改变部分代码来使图像具有交互 ...

最新文章

  1. Linux下显示前10个占用空间最大的文件或目录命令
  2. 数据结构与算法(1)——数组与链表
  3. Java Bean与Map之间相互转化的实现
  4. 『数据库』这篇数据库的文章真没人看--数据库完整性
  5. redirect路由配置 vue_Web前端:Vue路由进阶配置
  6. 美国大学计算机专业排名2014,2014年美国大学计算机科学专业排名
  7. 安卓 adb 的原理浅析
  8. networkx edge 属性_python图算法库Networkx笔记-第一章
  9. pandas 日期比较大小_如何计算不同pandas列中两个日期之间的年数
  10. C语言数组和指针的区别
  11. ZEMAX | 如何使用 ISO 元件制图工具
  12. H3C UniServer R6900 G3 服务器介绍
  13. 如何在html页面集成markdown编辑器
  14. SMILES学习笔记
  15. 谷歌身份验证器 java demo实现 及使用中问题分析
  16. 漫谈唯一设备ID,android开发工程师
  17. 内存类型UDIMM、RDIMM、LRDIMM
  18. mysql进阶(十九)SQL语句如何精准查找某一时间段的数据
  19. 【日常训练】2019-10-24am_xjoi结界[生与死的境界]/codeforces878E_贪心
  20. html添加一条虚线垂直的,【html问题】在网页中添加垂直分割线

热门文章

  1. 基于visual c++之windows核心编程代码分析(47)实现交换网络的QQ号嗅探
  2. 2021四川高考成绩等位分查询,四川省2021高考理科最新成绩一分一段查询
  3. 阿里面试必备:100个高频Spring面试题,助你一臂之力!
  4. java毕业设计乐居租房网的设计与实现Mybatis+系统+数据库+调试部署
  5. 自媒体人想要写好实时热点文章,一定要掌握好这三点
  6. 四大美女 沉鱼-->西施 落雁-->王昭君 闭月-->貂禅 羞花-->杨玉环
  7. 阿里云主机遭受DDOS攻击IP不能使用如何更换弹性公网IP
  8. 为什么输入百度的IP地址不能直接访问
  9. 《令人心动的offer(第二季)》学习笔记
  10. respond.php,phpcms v9宽字节注入【phpcms/modules/pay/respond.php】