flutter动画:抖动,背景闪烁,圆角变化

写在前面

适用于对flutter动画有一定了解的同学,本案例是为了模拟一个角色对象根据不同状态变换不同效果

先看下效果图


0jjg5-ur642.gif

实现&代码

定义状态&动画

enum CellStatus { none, mot, pen, def }
  • non: none/常规状态,无动画
  • mot: motion/行动状态,背景闪烁
  • pen: pending/待选状态,抖动
  • def: defense/防守状态,尖角变圆角

定义角色对象

class Cell {
  CellStatus status;
  Notify1 onStatusChanged;
}

本例中只包含两个属性

  • status: CellStatus,表示当前状态
  • onStatusChanged: Notify1,表示状态变化的回调
    这里的Notify1是自定义的一个带一个泛型参数的方法对象

当需要变化状态时,比如转换到行动状态

cell.onStatusChanged?.call(CellStatus.mot)

其实更常见的办法是让Cell继承于ChangeNotifier,然后通过addListener来添加监听变化,相比于onStatusChanged: Notify1,后者只针对单一属性,具有指向性。

页面布局

上方是4个按钮,模拟状态变化,下方是展示角色对象的CellWidget

class CellApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    /// 这里创建角色对象
    Cell cell = Cell();
    var children = [
      Spacer(),
      RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.none), child: Text("None")),
      Spacer(),
      RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.mot), child: Text("Mot")),
      Spacer(),
      RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.pen), child: Text("Pen")),
      Spacer(),
      RaisedButton(onPressed: () => cell.onStatusChanged?.call(CellStatus.def), child: Text("Def")),
      Spacer(),
    ];
    return Scaffold(
        body: SafeArea(
      child: Column(children: [
        Expanded(child: Center(child: Row(children: children, mainAxisSize: MainAxisSize.min))),
        Expanded(child: Container(alignment: Alignment.topCenter, child: CellWidget(cell)))
      ]),
    ));
  }
}

CellWidget

这是一个需要应用动画的widget,传入Cell对象。其State中加入AnimationController

class CellWidget extends StatefulWidget {
  final Cell cell;

  CellWidget(this.cell);

  @override
  _CellWidgetState createState() => _CellWidgetState();
}

class _CellWidgetState extends State with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation _animation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
    _controller.addListener(() => setState(() {}));
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    ...
  }

监听状态变化

就是为cell.onStatusChanged赋值

  @override
  void initState() {
    super.initState();
    ...
    widget.cell.onStatusChanged = onStatusChanged;
  }

  void onStatusChanged(CellStatus newStatus) {
    ...
  }

处理状态变化

其实就是创建根据状态创建Animation

  • 当为motion时,创建背景闪烁动画,使用ColorTween创建开始和结束的颜色,并调用controller.repeat(reverse: true)来实现重复
  • 当为pending时,抖动动画是通过四向随机改变widget位置来实现的,所以只需在每次重绘时通过Random#nextInt()来随机方向即可,此处不用创建任何动画,只需让controller重复即可
  • 当为defense时,尖角变圆角,使用Tween创建开始和结束时圆角的半径即可,不需重复
  • 而none状态,需要回归正常,由于正常值是预定义的,所以此处仅仅需要关闭动画
  void onStatusChanged(CellStatus newStatus) {
    print("cell status change from ${widget.cell.status.toString()} to ${newStatus.toString()}");
    widget.cell.status = newStatus;
    setState(() {});
    _controller.reset();
    _controller.duration = Duration(milliseconds: 200);
    if (widget.cell.status == CellStatus.mot) {
      _animation = ColorTween(begin: Colors.white, end: Colors.red)
          .animate(CurvedAnimation(parent: _controller, curve: Curves.easeInCubic));
      _controller.repeat(reverse: true);
    } else if (widget.cell.status == CellStatus.none) {
      _controller.stop();
    } else if (widget.cell.status == CellStatus.pen) {
      _controller.repeat(reverse: true);
    } else if (widget.cell.status == CellStatus.def) {
      _animation = Tween(begin: 0.0, end: 16.0).animate(_controller);
      _controller.duration = Duration(milliseconds: 800);
      _controller.forward();
    }
  }

build不同状态下的widget

正常情况下,应该是一个Container,宽高为64.0,居中一个Text,decoration为白底,边框为尖角
由于抖动动画需要改变位置,所以外层应当加Transform.translate(offset, widget)

  @override
  Widget build(BuildContext context) {
    var color = Colors.white;
    var offset = Offset.zero;
    var radius = 0.0;
    if (widget.cell.status == CellStatus.mot) {
      color = _animation.value;
    } else if (widget.cell.status == CellStatus.pen) {
      var random = new Random().nextInt(4);
      /// left:0 right:1 top:2 bottom:3
      double x = 0, y = 0;
      switch (random) {
        case 0: x = -2; break;
        case 1: x = 2; break;
        case 2: y = -2; break;
        case 3: y = 2; break;
      }
      offset = offset.translate(x, y);
    } else if (widget.cell.status == CellStatus.def) {
      radius = _animation.value;
    }
    var decoration = BoxDecoration(color: color, border: Border.all(color: Colors.blue), borderRadius: BorderRadius.circular(radius));
    var container = Container(width: 64.0, height: 64.0, decoration: decoration, alignment: Alignment.center, child: Text("Cell"));
    return Transform.translate(offset: offset, child: container);
  }

最后

本人热爱编程,热爱游戏,热爱flutter,一直想用flutter写一个RPG类型的游戏,有喜欢flutter开发游戏的朋友可以加我好友互相交流哈!

你可能感兴趣的:(flutter动画:抖动,背景闪烁,圆角变化)