22

【译】用户与Flutter交互时的粒子动画

 4 years ago
source link: https://juejin.im/post/5e1ecf0d5188254bfd45a67b
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
2020年01月22日 阅读 1466

【译】用户与Flutter交互时的粒子动画

原文链接:medium.com/@felixblasc…

依旧是 simple_animations 的漂亮动画应用。

simple_animations 已经完成对 Flutter Web 的支持,并且其背景动画效果(文章) 的演示例子成为了 Flutter Web 的官方示例。

在继《带有Flutter的粒子动画》 之后,今天这一篇将介绍如何使用粒子动画与用户交互的。

16fa85e502c01949?imageslim

如上图所示,动画是一种“廉价”的点击效果,“地鼠”在 6 个不同的地方短暂出现,当单击一个地鼠(“圆”)时,它将分裂成多个颗粒,然后经过一段随机的时间后,它们再次出现,类似一个简单的反应游戏(打地鼠)。

1

如下代码所示是一个简化的地鼠控件代码:

class Mole extends StatefulWidget {
  @override
  _MoleState createState() => _MoleState();
}

class _MoleState extends State<Mole> {
  final List<MoleParticle> particles = [];

  bool _moleIsVisible = false;
  
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100,
      height: 100,
      child: _buildMole(),
    );
  }

  Rendering _buildMole() {
    return Rendering(
      onTick: (time) => _manageParticleLifecycle(time),
      builder: (context, time) {
        return Stack(
          overflow: Overflow.visible,
          children: [
            if (_moleIsVisible)
              GestureDetector(onTap: () => _hitMole(time), child: _mole()),
            ...particles.map((it) => it.buildWidget(time))
          ],
        );
      },
    );
  }

  Widget _mole() {
    return Container(
      decoration: BoxDecoration(
          color: Colors.brown, borderRadius: BorderRadius.circular(50)),
    );
  }

  _hitMole(Duration time) {
    _setMoleVisible(false);
    Iterable.generate(50).forEach((i) => particles.add(MoleParticle(time)));
  }

  _manageParticleLifecycle(Duration time) {
    particles.removeWhere((particle) {
      return particle.progress.progress(time) == 1;
    });
  }

}
复制代码

对于“地鼠”,我们使用 GestureDetector 替换 Container 来将用户的点击放到 _hitMole 方法中,这些“地鼠”与颗粒通过 Stack 堆叠到一起。

在该 _hitMole() 方法的内部,我们隐藏了“地鼠”容器,并生成了 50 个 MoleParticle 粒子,该类还提供了 buildWidget() 用于构建小“地鼠”粒子。

最后通过 Rendering 控件实现绘制,它采用一个 onTick 可以让我们更方便的处理粒子的生命周期,在我们的 _manageParticleLifecycle() 方法中,我们在动画完成后删除了所有粒子。

我们的粒子动画一开始覆盖在原始的“地鼠”容器上,然后随着时间的流逝,粒子容器随机向四周方向飞行直到粒子的容器大小缩减为 0。

1

如下代码所示, MoleParticle 类实现了这个行为效果:

class MoleParticle {
  Animatable tween;
  AnimationProgress progress;

  MoleParticle(Duration time) {
    final random = Random();
    final x = (100 + 200) * random.nextDouble() * (random.nextBool() ? 1 : -1);
    final y = (100 + 200) * random.nextDouble() * (random.nextBool() ? 1 : -1);

    tween = MultiTrackTween([
      Track("x").add(Duration(seconds: 1), Tween(begin: 0.0, end: x)),
      Track("y").add(Duration(seconds: 1), Tween(begin: 0.0, end: y)),
      Track("scale").add(Duration(seconds: 1), Tween(begin: 1.0, end: 0.0))
    ]);
    progress = AnimationProgress(
        startTime: time, duration: Duration(milliseconds: 600));
  }

  buildWidget(Duration time) {
    final animation = tween.transform(progress.progress(time));
    return Positioned(
      left: animation["x"],
      top: animation["y"],
      child: Transform.scale(
        scale: animation["scale"],
        child: Container(
          width: 100,
          height: 100,
          decoration: BoxDecoration(
              color: Colors.brown, borderRadius: BorderRadius.circular(50)),
        ),
      ),
    );
  }
}
复制代码

在创建粒子时,我们通过计算出随机目标位置,并将这些信息存储到补间动画里。这里使用了 simple_animationsMultiTrackTween ,因为可以实现一次添加 3 个不同的补间动画属性。

然后这里创建一个 AnimationProgress 用来跟踪动画的进度,我们这里只需要向他提供当前时间即可。

最后通过 buildWidget() 将动画补间值和进度结合 Transform.scalePositioned 来达到移动和缩放的效果。

16fa8724a0ab1f3b?imageslim

这里排除了一些重生相关的逻辑,但是您可以在 Simple Animations Example App 中找到完整的演示代码。

这是应用到 CarGuo/gsy_github_app_flutter 项目启动页的效果:

16fa893d2147359d?imageslim


1

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK