【Flutter 实战】自定义动画-涟漪和雷达扫描
老孟导读:此篇文章是 Flutter 动画系列文章第五篇,本文介绍2个自定义动画:涟漪和雷达扫描效果。
涟漪
实现涟漪动画效果如下:
此动画通过 CustomPainter 绘制配合 AnimationController 动画控制实现,定义动画控制部分:
class WaterRipple extends StatefulWidget {
final int count;
final Color color;
const WaterRipple({Key key, this.count = 3, this.color = const Color(0xFF0080ff)}) : super(key: key);
@override
_WaterRippleState createState() => _WaterRippleState();
}
class _WaterRippleState extends State<WaterRipple>
with SingleTickerProviderStateMixin {
AnimationController _controller;
@override
void initState() {
_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 2000))
..repeat();
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return CustomPaint(
painter: WaterRipplePainter(_controller.value,count: widget.count,color: widget.color),
);
},
);
}
}
count 和 color 分别代表水波纹的数量和颜色。
WaterRipplePainter 定义如下:
class WaterRipplePainter extends CustomPainter {
final double progress;
final int count;
final Color color;
Paint _paint = Paint()..style = PaintingStyle.fill;
WaterRipplePainter(this.progress,
{this.count = 3, this.color = const Color(0xFF0080ff)});
@override
void paint(Canvas canvas, Size size) {
double radius = min(size.width / 2, size.height / 2);
for (int i = count; i >= 0; i--) {
final double opacity = (1.0 - ((i + progress) / (count + 1)));
final Color _color = color.withOpacity(opacity);
_paint..color = _color;
double _radius = radius * ((i + progress) / (count + 1));
canvas.drawCircle(
Offset(size.width / 2, size.height / 2), _radius, _paint);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
重点是 paint 方法,根据动画进度计算颜色的透明度和半径。
使用如下:
class WaterRipplePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(height: 200, width: 200, child: WaterRipple())),
);
}
}
雷达扫描
实现雷达扫描效果:
此效果分为两部分:中间的 logo 图片和扫描部分。
中间的 logo 图片
中间的 logo 图片边缘有阴影效果,像是太阳发光一样,实现:
Container(
height: 70.0,
width: 70.0,
decoration: BoxDecoration(
color: Colors.grey,
image: DecorationImage(
image: AssetImage('assets/images/logo.png')),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.white.withOpacity(.5),
blurRadius: 5.0,
spreadRadius: 3.0,
),
]),
)
扫描
定义雷达扫描的动画控制器:
class RadarView extends StatefulWidget {
@override
_RadarViewState createState() => _RadarViewState();
}
class _RadarViewState extends State<RadarView>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
@override
void initState() {
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 5));
_animation = Tween(begin: .0, end: pi * 2).animate(_controller);
_controller.repeat();
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return CustomPaint(
painter: RadarPainter(_animation.value),
);
},
);
}
}
RadarPainter 定义如下:
class RadarPainter extends CustomPainter {
final double angle;
Paint _bgPaint = Paint()
..color = Colors.white
..strokeWidth = 1
..style = PaintingStyle.stroke;
Paint _paint = Paint()..style = PaintingStyle.fill;
int circleCount = 3;
RadarPainter(this.angle);
@override
void paint(Canvas canvas, Size size) {
var radius = min(size.width / 2, size.height / 2);
canvas.drawLine(Offset(size.width / 2, size.height / 2 - radius),
Offset(size.width / 2, size.height / 2 + radius), _bgPaint);
canvas.drawLine(Offset(size.width / 2 - radius, size.height / 2),
Offset(size.width / 2 + radius, size.height / 2), _bgPaint);
for (var i = 1; i <= circleCount; ++i) {
canvas.drawCircle(Offset(size.width / 2, size.height / 2),
radius * i / circleCount, _bgPaint);
}
_paint.shader = ui.Gradient.sweep(
Offset(size.width / 2, size.height / 2),
[Colors.white.withOpacity(.01), Colors.white.withOpacity(.5)],
[.0, 1.0],
TileMode.clamp,
.0,
pi / 12);
canvas.save();
double r = sqrt(pow(size.width, 2) + pow(size.height, 2));
double startAngle = atan(size.height / size.width);
Point p0 = Point(r * cos(startAngle), r * sin(startAngle));
Point px = Point(r * cos(angle + startAngle), r * sin(angle + startAngle));
canvas.translate((p0.x - px.x) / 2, (p0.y - px.y) / 2);
canvas.rotate(angle);
canvas.drawArc(
Rect.fromCircle(
center: Offset(size.width / 2, size.height / 2), radius: radius),
0,
pi / 12,
true,
_paint);
canvas.restore();
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
将两者结合在一起:
class RadarPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF0F1532),
body: Stack(
children: [
Positioned.fill(
left: 10,
right: 10,
child: Center(
child: Stack(children: [
Positioned.fill(
child: RadarView(),
),
Positioned(
child: Center(
child: Container(
height: 70.0,
width: 70.0,
decoration: BoxDecoration(
color: Colors.grey,
image: DecorationImage(
image: AssetImage('assets/images/logo.png')),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.white.withOpacity(.5),
blurRadius: 5.0,
spreadRadius: 3.0,
),
]),
),
),
),
]),
),
)
],
));
}
}
交流
老孟Flutter博客地址(330个控件用法):http://laomengit.com
欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:
![]() |
![]() |
【Flutter 实战】自定义动画-涟漪和雷达扫描的更多相关文章
- 【Flutter 实战】动画核心
老孟导读:动画系统是任何一个UI框架的核心功能,也是开发者学习一个UI框架的重中之重,同时也是比较难掌握的一部分,下面我们就一层一层的揭开 Flutter 动画的面纱. 任何程序的动画原理都是一样的, ...
- 【Flutter 实战】动画序列、共享动画、路由动画
老孟导读:此篇文章是 Flutter 动画系列文章第四篇,本文介绍动画序列.共享动画.路由动画. 动画序列 Flutter中组合动画使用Interval,Interval继承自Curve,用法如下: ...
- 【Flutter 实战】17篇动画系列文章带你走进自定义动画
老孟导读:Flutter 动画系列文章分为三部分:基础原理和核心概念.系统动画组件.8篇自定义动画案例,共17篇. 动画核心概念 在开发App的过程中,自定义动画必不可少,Flutter 中想要自定义 ...
- 【Flutter实战】自定义滚动条
老孟导读:[Flutter实战]系列文章地址:http://laomengit.com/guide/introduction/mobile_system.html 默认情况下,Flutter 的滚动组 ...
- 【Flutter 实战】一文学会20多个动画组件
老孟导读:此篇文章是 Flutter 动画系列文章第三篇,后续还有动画序列.过度动画.转场动画.自定义动画等. Flutter 系统提供了20多个动画组件,只要你把前面[动画核心](文末有链接)的文章 ...
- 《Flutter实战》开源电子书
<Flutter实战>开源电子书 <Flutter实战> 开源了,本书为 Flutter中文网开源电子书项目,本书系统介绍了Flutter技术的各个方面,本书属于原创书籍(并非 ...
- Android 5.0自定义动画
材料设计中的动画对用户的操作给予了反馈,并且在与应用交互时提供了持续的可见性.材料主题提供了一些按钮动画和活动过渡,Android 5.0允许你自定义动画并且可以创建新的动画: Touch Feedb ...
- python全栈开发day48-jqurey自定义动画,jQuery属性操作,jQuery的文档操作,jQuery中的ajax
一.昨日内容回顾 1.jQuery初识 1).使用jQuery而非JS的六大理由 2).jQuery对象和js对象转换 3).jQuery的两大特点 4).jQuery的入口函数三大写法 5).jQu ...
- Flutter实战】文本组件及五大案例
老孟导读:大家好,这是[Flutter实战]系列文章的第二篇,这一篇讲解文本组件,文本组件包括文本展示组件(Text和RichText)和文本输入组件(TextField),基础用法和五个案例助你快速 ...
随机推荐
- Jmeter 中 CSV 如何参数化测试数据并实现自动断言
当我们使用Jmeter工具进行接口测试,可利用CSV Data Set Config配置元件,对测试数据进行参数化,循环读取csv文档中每一行测试用例数据,来实现接口自动化.此种情况下,很多测试工程师 ...
- PHP array_reduce() 函数
实例 发送数组中的值到用户自定义函数,并返回一个字符串: <?phpfunction myfunction($v1,$v2){return $v1 . "-" . $v2;} ...
- Spring Cloud Data Flow初体验,以Local模式运行
1 前言 欢迎访问南瓜慢说 www.pkslow.com获取更多精彩文章! Spring Cloud Data Flow是什么,虽然已经出现一段时间了,但想必很多人不知道,因为在项目中很少有人用.不仅 ...
- Neo4j 学习笔记(-)
Neo4j 的使用说明(一)(基于V3.4.9) 下一篇(二):https://www.cnblogs.com/infoo/p/11947467.html 一.Neo4j简介 Neo4j是一个高性能的 ...
- 关于idea 在创建maven 骨架较慢问题解决
在设置中->maven>runner>VM Options 粘贴 -DarchetypeCatalog=internal 其中 -D archetype:原型,典型的意思 ( ...
- heap相关算法的简单实现
// 12:06 PM/09/28/2017 #pragma once //向下调整算法 主要用来make_heap 以及pop_heap inline void adjustDown(int* he ...
- hibernate自动创建表报错,提示不存在
报错:ERROR: HHH000299: Could not complete schema update 或 不能执行statement等 解决方式: 根据mysql版本更改hibernate.c ...
- iOS开发实战之搜索控制器UISearchController使用
当tableView中的数据过多的时候,在tableView上加一个搜索框就变的很必要了,本文就讨论搜索控制器的使用,以及谓词的简单实现. .m文件中代码如下 添加搜索控制器的各种协议 <UIS ...
- python2.3嵌套if结构:
#案例:存款100万的请款下,买宝马:老爸资助大于50万买宝马740:大于30万买宝马520:小于20万宝马320.存款大于50万小于100万买丰田:大于20万小于50万买二手车:小于20万自行车! ...
- eureka和feign的使用
1 eureka和feign的简介(copy来的) eureka:Eureka是Netflix开发的服务发现组件,本身是一个基于REST的服务.Spring Cloud将它集成在其子项目spring- ...