【问题标题】:Create very custom animations in flutter在颤振中创建非常自定义的动画
【发布时间】:2018-12-19 15:40:29
【问题描述】:

我已经关注了 flutter.io 上的各种动画教程(补间、交错、过渡等),它们都很棒。

我想探索的是如何根据 UI 对象的组成实际制作自定义动画。

让我们举一个简单的例子,暂停 -> 播放动画。

首先我们有一个暂停图标,两个竖线。

假设我愿意

  1. 通过在最右侧垂直边的中心添加一个额外的角并将其向右移动,将右侧的条形变成一个三角形。
  2. 然后将该三角形从第 1 步稍微向左移动,因此它现在粘在最左边的垂直条上,变成一个更大的“三角形”(实际上是一个五边形)。

这看起来像一个播放按钮,而不是暂停按钮了。

我将如何实现这种自定义动画?我假设我不能使用图标类。而且我很确定我不应该对 Widget 这样做并只是移动它们。

我应该从哪里开始探索动画中的这种精确度?

【问题讨论】:

  • youtube.com/watch?v=46zeFyiMBS4 你在说这个吗?
  • 例如是的,类似的。但这可能是其他任何事情。如果它已经存在,它很酷,但我希望能够使用点/顶点/等手动绘制/编码我的动画。如果我的例子碰巧建立在它的盲目运气:)

标签: animation flutter


【解决方案1】:

@Alaric 的回答为您指出了几个软件包,但并没有真正说明您为什么要使用它们。

目前的问题是,您所说的动画在实际工作方式方面相当复杂。有多个项目会随着时间而变化,甚至可能变成一个更大的项目。

您可以采用两种方法来解决此问题。第一种是使用外部动画工具来创建此动画,使用动画工具必须进行项目更改和合并的任何功能。然后,一旦您的动画运行得令您满意,您就必须以某种方式将其导入您的项目中。这就是fluttie和flare_flutter插件的用武之地——如果你使用Aftereffects,你可以使用Lottie导出文件,然后使用fluttie插件来显示它。 Flare 稍微简单一些,因为它用于颤振,但您仍然在外部创建动画,然后将文件添加到要渲染的资产中。


另一种方法是自己制作动画。这需要三件事:

  1. 创建一个包含动画的小部件。
  2. 创建一个 CustomPainter 来实际绘制结果。
  3. (可选)另一个类作为控制器来启动/停止/等动画。

如果您使用 GlobalKey 访问它并公开启动/停止方法,则包含动画的小部件也可能是控制器,但这有点混乱。最好有一个外部对象作为控制器 - 你甚至可以按原样使用 AnimationController,尽管它会不那么“干净”。

如果您不传递它,您的小部件中可能会有一个 AnimationController,您可以从控制器或类启动和停止它。它基本上只是从 0 到 1 再返回,并负责重建 CustomPainter(可能使用 AnimatedBuilder)。

这是一个非常基本的示例,不需要外部控制器,因为手势检测发生在小部件内。请注意,我不是在每次设置“started”成员时都调用 setState,因为我实际上并不希望它在更改时重建。

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: StartStop(),
        ),
      ),
    );
  }
}

class StartStop extends StatefulWidget {
  @override
  StartStopState createState() {
    return new StartStopState();
  }
}

class StartStopState extends State<StartStop> with TickerProviderStateMixin<StartStop> {
  bool started = false;

  AnimationController animationController;

  @override
  void initState() {
    animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 300));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        started ? animationController.forward() : animationController.reverse();
        started = !started;
      },
      child: SizedBox(
        width: 100,
        height: 100,
        child: AnimatedBuilder(
          animation: animationController,
          builder: (context, child) {
            return CustomPaint(
              painter: StartStopPainter(animationController.value),
              size: Size.infinite,
              child: child,
            );
          },
        ),
      ),
    );
  }
}

class StartStopPainter extends CustomPainter {
  final double percentAnimated;

  StartStopPainter(this.percentAnimated) : assert(percentAnimated >= 0 && percentAnimated <= 1);

  @override
  void paint(Canvas canvas, Size size) {
    var pausePaint = Paint()..color = Colors.black.withOpacity(1 - percentAnimated);

    canvas.drawRect(Rect.fromLTRB(20, 10, 40, 90), pausePaint);
    canvas.drawRect(Rect.fromLTRB(60, 10, 80, 90), pausePaint);

    var playPaint = Paint()..color = Colors.black.withOpacity(percentAnimated);

    canvas.drawPath(Path()..addPolygon([Offset(20, 10), Offset(20, 90), Offset(80, 50)], true), playPaint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

我会将动画的实际自定义部分(将矩形更改为三角形等)留给您。您只需使用输入 percentAnimated 来决定要绘制的路径或多边形,而不是使用不透明度和一些不同的绘制调用。

【讨论】:

    猜你喜欢
    • 2020-02-18
    • 1970-01-01
    • 2022-06-15
    • 2020-11-01
    • 2021-02-13
    • 2021-11-29
    • 2020-09-28
    • 2020-04-15
    • 2020-02-27
    相关资源
    最近更新 更多