如何实现一个简单的雨滴动画?手把手告诉你

 

目的

写了几个Flutter的demo,但是对Flutter的自定义view和动画都不太了解,看到一个类似效果在android的实现,就尝试用Flutter做一下。同时也是学习Flutter的自定义view和动画相关的知识。

效果

img效果动图

在蓝色区域点击,会产品水波纹动画。

宛如水珠落在池塘,雨滴落在青青草地~

思路

动画很简单,虽然有多个雨滴,不过每次点击都是重复的动画,所以只用管一个雨滴动画是怎么实现的,其他的都是重复。

单独来看一个雨滴动画,其实就是一个圆圈慢慢的变大同时慢慢的变浅,最后消失。

所以我们封装一套上述的动画逻辑,然后在用户每次点击时生成一个相应的动画即可。

实现

自定义view

首先我们要解决的是自定义view的问题,我们知道Flutter中的一起UI皆Flutter,但是不同于android中的View会直接提供一个draw方法让你做自由的绘制操作。在Flutter中,除了StatefuleWidget等申明了支持继承的类外,其他的都是不建议继承重写的。如要要做一个新的Widget,官方建议是通过组合Widget来实现。

当然对于我们这里这种需要自己做绘制操作的,就不是组合可以解决的了,这种情况下,Flutter提供了CustomPainter类,这个类提供了paint方法,可以通过重写该方法,实现对canvas的绘制。然后作为CustomPaint的参数,控制该Widget的展示样式。

这里由于主要的绘制是水纹,要实现多个重复动画,所以具体的绘制逻辑封装了起来

class RainDrop extends CustomPainter {   RainDrop(this.rainList);    List<RainDropDrawer> rainList = List(); // 雨点列表   Paint _paint = new Paint()..style = PaintingStyle.stroke; // 配置画笔    @override   void paint(Canvas canvas, Size size) {     rainList.forEach((item) {       item.drawRainDrop(canvas, _paint); // 实际的绘制逻辑     });     rainList.removeWhere((item) { // 移出无效对象       return !item.isValid();     });   }   // ... }

水纹圈的绘制

每一个水纹的动画都是一样的,所以统一封装了起来。

class RainDropDrawer {   static const double MAX_RADIUS = 30;   double posX;   double posY;   double radius = 5;    RainDropDrawer(this.posX, this.posY); // (2)    drawRainDrop(Canvas canvas, Paint paint) { // (1)     double opt = (MAX_RADIUS - radius) / MAX_RADIUS; // (3)     paint.color = Color.fromRGBO(0, 0, 0, opt);     canvas.drawCircle(Offset(posX, posY), radius, paint); // (4)     radius += 0.5;   }    bool isValid() { // (5)     return radius < MAX_RADIUS;   } }

注释(1)处,上文提到的CustomPainter会把canvas传过来,在这里完成单个水纹的绘制工作。

注释(2)处,每个水纹圈需要确定的是位置,只要位置就行了,大小是随着时间均匀扩大的,给默认起始值就行。

注释(3)处,透明度是随着半径扩大而逐渐透明的,这里简单的做了线性的映射。

注释(4)处,绘制水纹圈,然后让水纹半径自增,实现每次绘制扩大的效果。

注释(5)处,给定失效的条件。超过一定半径这个水纹就消失了。

扩散动画

Flutter中提供了很多的动画实现,这里用到的是AnimationController。

其实AnimationController在这里就是提供了一个回调,每次收到vsync信号时回调做一次更新。

    _animation = new AnimationController(       // 因为是repeat的,这里的duration其实不care         duration: const Duration(milliseconds: 200),         vsync: this)       ..addListener(() {         if (_rainList.isEmpty) { //(1)           _animation.stop();         }         setState(() {});       });

这里的动画是通过repeat启动的,所以不用太关心duration,因为只要不手动关闭实际上是会一直回调的。

vsync设置的是当前的widget,提供了一个ticker,会定时回调。然后在回调中setState让当前widget更新UI。

注释(1)处是动画停止的条件判断,当每次点击往_rainList中加一个对象,每个对象绘制会判断大小是否有效,如果无效会被从列表中移出,当列表中没有元素时就停止动画。

手势识别

上述基本实现了多个雨滴的展示和动画,然后我们要来实现对用户点击的响应。

Flutter提供了GestureDetector这个widget来做手势识别。所以我们只需要用这个widget wrap住我们的自定义view,然后实现对应的手势监听方法即可。

      GestureDetector(         onTapUp: (TapUpDetails tapUp) {           RenderBox getBox = context.findRenderObject();           var localOffset = getBox.globalToLocal(tapUp.globalPosition); // (1)            var rainDrop = RainDropDrawer(localOffset.dx, localOffset.dy);           _rainList.add(rainDrop);           _animation.repeat(); // (2)         },         

                    
                
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信