【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), ); }, ); }}

countcolor 分别代表水波纹的数量和颜色。

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」
让你每天进步一点点




本文分享自微信公众号 - 老孟Flutter(lao_meng_qd)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

微信关注我们

原文链接:https://my.oschina.net/u/4082303/blog/4530149

转载内容版权归作者及来源网站所有!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

相关文章

发表评论

资源下载

更多资源
优质分享Android(本站安卓app)

优质分享Android(本站安卓app)

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario,低调大师唯一一个Java游戏作品

Mario,低调大师唯一一个Java游戏作品

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Eclipse(集成开发环境)

Eclipse(集成开发环境)

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse 附带了一个标准的插件集,包括Java开发工具(Java Development Kit,JDK)。

Sublime Text 一个代码编辑器

Sublime Text 一个代码编辑器

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。