基于Flutter制作一个吃豆人加载动画

目录
  • 效果图
  • 绘制静态吃豆人、豆豆、眼睛
  • 加入动画属性
  • 总结

效果图

国际惯例,先看效果图:

具体效果就是吃豆人会根据吃不同颜色的豆子改变身体的颜色。

绘制静态吃豆人、豆豆、眼睛

首先,我们需要将这个静态的吃豆人绘制出来,我们可以把吃豆人看做是一个实心圆弧,豆豆和眼睛就是一个圆。

关键代码:

//画头
_paint
  ..color = color.value
  ..style = PaintingStyle.fill;
var rect = Rect.fromCenter(
    center: Offset(0, 0), width: size.width, height: size.height);
/// 起始角度
var a =  40 / 180 * pi;
// 绘制圆弧
canvas.drawArc(rect, 0, 2 * pi - a * 2, true, _paint);

// 画豆豆
canvas.drawOval(
    Rect.fromCenter(
        center: Offset(
            size.width / 2 +
                ddSize -
                angle2.value * (size.width / 2 + ddSize),
            0),
        width: ddSize,
        height: ddSize),
    _paint..color = color2.value);

//画眼睛
canvas.drawOval(
    Rect.fromCenter(
        center: Offset(0, -size.height / 3), width: 8, height: 8),
    _paint..color = Colors.black87);

动画属性: 嘴巴的张合:通过圆弧的角度不断改变实现,豆豆移动:从头的右侧源源不断的有豆子向左移动,改变豆豆x轴的坐标即可,接下来我们让吃豆人动起来吧。

加入动画属性

这里我们需要创建2个动画控制器,一个控制头,一个控制豆豆,我们看到因为头部一开一合属于动画正向执行一次然后再反向执行一次,相当于执行了两次,豆豆的从右边到嘴巴只执行了一次,所以头的执行时间是豆豆执行时间的两倍,嘴巴一张一合才能吃豆子嘛,吃豆完毕,将豆子颜色赋值给头改变颜色,豆子随机获取另一个颜色,不断的吃豆。 这里的绘制状态有多种情况,嘴巴的张合、豆子的平移、颜色的改变都需要进行重新绘制,这里我们可以使用 Listenable.merge方法来进行监听,接受一个Listenable数组,可以将我们需要改变的状态放到这个数组里,返回一个 Listenable赋值给CustomPainter构造函数repaint属性即可,然后在监听只需判断这个Listenable即可。

factory Listenable.merge(List<Listenable?> listenables) = _MergingListenable;

关键代码: 动画执行相关。

late Animation<double> animation; // 吃豆人
late Animation<double> animation2; // 豆豆
late AnimationController _controller = AnimationController(
    vsync: this, duration: Duration(milliseconds: 500)); //1s
late AnimationController _controller2 = AnimationController(
    vsync: this, duration: Duration(milliseconds: 1000)); //2s

//初始化吃豆人、豆豆颜色
ValueNotifier<Color> _color = ValueNotifier<Color>(Colors.yellow.shade800);
ValueNotifier<Color> _color2 =
    ValueNotifier<Color>(Colors.redAccent.shade400);

// 动画轨迹
late CurvedAnimation cure = CurvedAnimation(
    parent: _controller, curve: Curves.easeIn); // 动画运行的速度轨迹 速度的变化

@override
void initState() {
  super.initState();
  animation = Tween(begin: 0.2, end: 1.0).animate(_controller)
    ..addStatusListener((status) {
      // dismissed 动画在起始点停止
      // forward 动画正在正向执行
      // reverse 动画正在反向执行
      // completed 动画在终点停止
      if (status == AnimationStatus.completed) {
        _controller.reverse(); //反向执行 100-0
      } else if (status == AnimationStatus.dismissed) {
        _color.value = _color2.value;
        // 获取一个随机彩虹色
        _color2.value = getRandomColor();
        _controller.forward(); //正向执行 0-100
        // 豆子已经被吃了 从新加载豆子动画
        _controller2.forward(from: 0); //正向执行 0-100
      }
    });
animation2 = Tween(begin: 0.2, end: 1.0).animate(_controller2);
  // 启动动画 正向执行
  _controller.forward();
  // 启动动画 0-1循环执行
  _controller2.forward();
  // 这里这样重复调用会导致两次动画执行时间不一致 时间长了就不对应了
  // _controller2.repeat();
}

@override
void dispose() {
  _controller.dispose();
  _controller2.dispose();

  super.dispose();
}

@override
Widget build(BuildContext context) {
  return Center(
      child: CustomPaint(
    size: Size(50, 50),
    painter: Pain2Painter(
        _color,
        _color2,
        animation,
        animation2,
        Listenable.merge([
          animation,
          animation2,
          _color,
        ]),
        ddSize: 8),
  ));
}

// 获取一个随机颜色
Color getRandomColor() {
  Random random = Random.secure();
  int randomInt = random.nextInt(6);
  var colors = <Color>[
    Colors.red,
    Colors.orange,
    Colors.yellow,
    Colors.green,
    Colors.blue,
    Colors.indigo,
    Colors.purple,
  ];
  Color color = colors[randomInt];
  while (color == _color2.value) {
    // 重复再选一个
    color = colors[random.nextInt(6)];
  }
  return color;
}

绘制吃豆人源码:

class Pain2Painter extends CustomPainter {
  final ValueNotifier<Color> color; // 吃豆人的颜色
  final ValueNotifier<Color> color2; // 豆子的的颜色
  final Animation<double> angle; // 吃豆人
  final Animation<double> angle2; // 豆
  final double ddSize; // 豆豆大小
  final Listenable listenable;

  Pain2Painter(
      this.color, this.color2, this.angle, this.angle2, this.listenable,
      {this.ddSize = 6})
      : super(repaint: listenable);
  Paint _paint = Paint();

  @override
  void paint(Canvas canvas, Size size) {
    canvas.clipRect(Offset.zero & size);
    canvas.translate(size.width / 2, size.height / 2);
    // 画豆豆
    canvas.drawOval(
        Rect.fromCenter(
            center: Offset(
                size.width / 2 +
                    ddSize -
                    angle2.value * (size.width / 2 + ddSize),
                0),
            width: ddSize,
            height: ddSize),
        _paint..color = color2.value);
    //画头
    _paint
      ..color = color.value
      ..style = PaintingStyle.fill;

    var rect = Rect.fromCenter(
        center: Offset(0, 0), width: size.width, height: size.height);

    /// 起始角度
    /// angle.value 动画控制器的值 0.2~1 0是完全闭合就是 起始0~360° 1是完全张开 起始 40°~ 280° 顺时针
    var a = angle.value * 40 / 180 * pi;
    // 绘制圆弧
    canvas.drawArc(rect, a, 2 * pi - a * 2, true, _paint);
    //画眼睛
    canvas.drawOval(
        Rect.fromCenter(
            center: Offset(0, -size.height / 3), width: 8, height: 8),
        _paint..color = Colors.black87);
canvas.drawOval(
    Rect.fromCenter(
        center: Offset(-1.5, -size.height / 3 - 1.5), width: 3, height: 3),
    _paint..color = Colors.white);
  }

  @override
  bool shouldRepaint(covariant Pain2Painter oldDelegate) {
    return oldDelegate.listenable != listenable;
  }
}

至此,一个简单的吃豆人加载Loading就完成啦。再也不要到处都是菊花转的样式了。。。

总结

通过这个加载Loading动画可以重新复习下Flutter中绘制、动画的使用的联动使用、还有多状态重绘机制,通过动画还可以改变吃豆的速度和吃豆的时间运动轨迹,有兴趣可以试试哦。

以上就是基于Flutter制作一个吃豆人加载动画的详细内容,更多关于Flutter加载动画的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android实现仿iOS菊花加载圈动画效果

    常见的实现方式 切图,做旋转动画 自定义View,绘制效果 gif图 1.切图会增加体积,但相对简单,不过在换肤的场景下,会使用不同颜色,需要准备多张图,不够灵活. 2.由于自定义的好处,不同颜色只需要提供自定义属性,换肤时切换属性设置即可,比较灵活. 3.gif图普遍比较大,而且加载gif没有原生支持,需要引入第三方库,而且消耗内存比较大,不推荐. 效果图: 完整代码 自定义属性: <?xml version="1.0" encoding="utf-8"?&

  • android实现加载动画对话框

    本文实例为大家分享了android实现加载动画对话框的具体代码,供大家参考,具体内容如下 先来两张效果图 自定义对话框: public class LoadingProgressDialog extends ProgressDialog { private AnimationDrawable mAnimation; private Context mContext; private ImageView mImageView; private String mLoadingTitle; priva

  • Android实现笑脸进度加载动画

    最近看到豆瓣的笑脸loading很有意思,看一张效果图: 下面分析一下如何实现这样的效果: 1.默认状态是一张笑脸的状态(一个嘴巴,两个眼睛,默认状态) 2.开始旋转,嘴巴追上眼睛(合并状态) 3.追上以后自转一周(自转状态) 4.然后逐渐释放眼睛(分离状态) 5.回到初始笑脸状态(默认状态) 一.默认状态 首先需要确定好嘴巴和眼睛的初始位置,我这里的初始化嘴巴是一个半圆,在横轴下方.眼睛分别与横轴夹角60度,如下图: 这两部分可以使用pathMeasure,我这里使用最简单的两个api:can

  • Android自定义加载圈动画效果

    本文实例为大家分享了Android自定义加载圈动画展示的具体代码,供大家参考,具体内容如下 实现如下效果: 该效果图主要有3个动画: 1.旋转动画 2.聚合动画 3.扩散动画 以上3个动画都是通过ValueAnimator来实现,配合自定义View的onDraw()方法实现不断的刷新和绘制界面. 具体代码如下: package blog.csdn.net.mchenys.myanimationloading; import android.animation.Animator; import a

  • Android开发之自定义加载动画详解

    目录 一.demo简介 二.分析贪吃动画的尺寸比例 三.画圆 四.实现张嘴闭嘴动画 五.小球移动动画 一.demo简介 1.效果展示如下图,我截了三个瞬间,但其实这是一个连续的动画,就是这个大圆不停地吞下小圆. 2.这个动画可以拆分为两部分,首先是大圆张嘴闭嘴的动画,相当于画一个圆弧,规定一下它的角度就好.小圆就是一个从右向左移动的动画.然后不停地刷新界面,让动画的持续时间为永恒,这样就会有一个持续的动态效果. 二.分析贪吃动画的尺寸比例 1.在制作动画之前,我们要先建一个模型,来确定一下大圆和

  • 基于Flutter制作一个吃豆人加载动画

    目录 效果图 绘制静态吃豆人.豆豆.眼睛 加入动画属性 总结 效果图 国际惯例,先看效果图: 具体效果就是吃豆人会根据吃不同颜色的豆子改变身体的颜色. 绘制静态吃豆人.豆豆.眼睛 首先,我们需要将这个静态的吃豆人绘制出来,我们可以把吃豆人看做是一个实心圆弧,豆豆和眼睛就是一个圆. 关键代码: //画头 _paint ..color = color.value ..style = PaintingStyle.fill; var rect = Rect.fromCenter( center: Off

  • 基于Flutter制作一个心碎动画特效

    目录 前言 实现步骤 1.绘制一个心 2.绘制心的裂痕 3.加入动画 完整代码 小结 前言 继续动画探索,今天用Flutter制作一个心碎的感觉,灵感来源于今天的股市,哎,心哇凉哇凉的.废话不多说,开始. 效果图先上: 实现步骤 1.绘制一个心 首先我们使用两段三阶贝塞尔曲线制作一个心型,这里因为需要实现心碎的效果,所以我们需要将心的两段用两段路径path进行绘制出来,效果: 绘制代码: canvas.translate(size.width / 2, size.height / 2); Pai

  • 基于Flutter制作一个火箭发射动画

    目录 总结 前言 北京时间10月16日0时23分,神舟十三号飞船成功发射,目前三名航天员已经顺利进驻空间站,开始为期6个月的“太空差旅”生活. 国家的航天技术的突飞猛进也让岛上码农很自豪,今天看 Flutter 的动画知识,看到了 AnimatedPositioned 这个组件,可以用于控制组件的相对位置移动.结合这个神舟十三号的发射,灵机一动,正好可以使用AnimatedPositioned 这个组件实现火箭发射动画.话不多说,先上效果! 效果说明 这里其实是两张图片叠加,一张是背景地球星空的

  • 如何使用Flutter实现58同城中的加载动画详解

    前言 在应用中执行耗时操作时,为了避免界面长时间等待造成假死的现象,往往会添加一个加载中的动画来提醒用户,在58同城中也不例外,而且我们并没有使用系统默认的加载动画,而是制作了一个具有58特色的加载动画. 在本篇文章中,给大家分享下笔者使用Flutter实现58同城中加载动画的过程.先看一下加载动画的效果: 动画效果乍看比较复杂,难以看出端倪,其实我们可以先调慢动画的速度,这样能够比较清晰地分析出动画的流程. 动画的流程 动画由两个圆弧的动效组成,两个圆弧的起始点角度和扫过的弧度随着时间规律变化

  • 分享8款优秀的 jQuery 加载动画和进度条插件

    加载动画和进度条在网站和 Web 应用中的使用非常流行.虽然网速越来越快,但是我们的网站越来越复杂,同时用户对网站的使用体验的要求也越来越高.在内容加载缓慢的时候,使用时尚的加载动画和进度条告诉用户还有内容正在加载是一种非常好的方式.今天这篇文章向大家推荐10款基于 jQuery 实现的加载动画和进度条插件. Spin.js 最喜欢这款插件了,动画图片的长度.粗细.速度和角度都可以灵活控制,想要做成什么样都可以. 源码下载    在线演示 Percentage Loader 一款轻量的 jQue

  • 详解基于electron制作一个node压缩图片的桌面应用

    基于electron制作一个node压缩图片的桌面应用 下载地址:https://github.com/zenoslin/imagemin-electron/releases 项目源码Github:https://github.com/zenoslin/imagemin-electron 准备工作 我们来整理一下我们需要做什么: 压缩图片模块 获取文件路径 桌面应用生成 压缩图片 我们需要使用imagemin这个库来压缩图片,这里我们把这个库封装成压缩模块. const imagemin = r

  • 利用Flutter制作一个摸鱼桌面版App

    目录 准备工作 开始敲代码 找到资源 思考布局 实现布局 思考动画 实现动画 结语 Win10商店上架了一款名为<摸鱼>的App,在下载打开之后,这个App会让你的电脑进入一个假更新的画面,让别人以为你的电脑正在升级,这时候你就可以休息一下,优雅地喝一杯咖啡.  顿时这个念头划过了我的脑海:好东西,但是我用的是 MacBook,不能用这个应用.但是貌似我可以自己写一个? 准备工作 年轻最需要的就是行动力,想到就干,尽管我此刻正在理顺 DevFest 的讲稿,但丝毫不妨碍我用 10 分钟写一个

  • 基于Python制作一个多进制转换工具

    目录 前言 主要步骤 完整代码 前言 学习资料下载链接 提取码:tha8  进制转换计算工具含源文件 主要步骤 导入模块 import tkinter from tkinter import * import tkinter as tk from tkinter.ttk import * 整个框架的主结构 root = Tk() root.title('贱工坊-进制转换计算') # 程序的标题名称 root.geometry("580x400+512+288") # 窗口的大小及页面的

  • 基于PyQT5制作一个敏感词检测工具

    设计思路:根据敏感词库文件筛选,查看输入的文本中是否包含敏感词汇.从而过滤出相关的敏感词. 导入应用相关的模块. import os import logging import sys 导入UI界面相关的模块. from PyQt5.QtWidgets import QApplication,QWidget,QVBoxLayout,QTextEdit,QGridLayout,QLineEdit,QPushButton,QFileDialog from PyQt5.QtGui import QIc

  • 基于Python制作一个文件去重小工具

    目录 前言 实现步骤 补充 前言 常常在下载网络素材时有很多的重复文件乱七八糟的,于是想实现一个去重的操作. 主要实现思路就是遍历出某个文件夹包括其子文件夹下面的所有文件,最后,将所有文件通过MD5函数的对比筛选出来,最后将重复的文件移除. 实现步骤 用到的第三方库都比较的常见,其中只有hashlib是用来对比文件的不是很常见.其他的都是一些比较常见的第三方库用来做辅助操作. import os # 应用文件操作 import hashlib # 文件对比操作 import logging #

随机推荐