Как правильно обновить значения анимации в анимации Flutter? - PullRequest
0 голосов
/ 10 ноября 2018

Поэтому я пытаюсь создать анимацию во Flutter, которая требует другого результата каждый раз, когда пользователь нажимает кнопку.

Я реализовал следующий код в соответствии с учебником Flutter Animations и создал функцию для его обновления.

class _RoulettePageWidgetState extends State<RoulettePageWidget>
with SingleTickerProviderStateMixin {
   Animation<double> _animation;
   Tween<double> _tween;
   AnimationController _animationController;

   int position = 0;

   @override
   void initState() {
      super.initState();
      _animationController =
          AnimationController(duration: Duration(seconds: 2), vsync: this);
      _tween = Tween(begin: 0.0, end: 100.0);
      _animation = _tween.animate(_animationController)
          ..addListener(() {
              setState(() {});
          });
   }

   void setNewPosition(int newPosition) {
      _tween = Tween(
        begin: 0.0,
        end: math.pi*2/25*newPosition);
      _animationController.reset();
      _tween.animate(_animationController);
      _animationController.forward();
   }

   @override
   Widget build(BuildContext context) {
      return Container(
         child: Column(
            children: <Widget>[
               Center(
                  child: Transform.rotate(
                     angle: _animationController.value,
                     child: Icon(
                        Icons.arrow_upward,
                     size: 250.0,
                  ),
               )),
               Expanded(
                  child: Container(),
               ),
               RaisedButton(
                  child: Text('SPIN'),
                  onPressed: () {
                     setState(() {
                        setNewPosition(math.Random().nextInt(25));
                     });
                  },
               )
            ],
         )
      );
   }
}

Как видите, я обновляю _tween begin: и end:, но, похоже, это не меняет анимацию.

Так что же мне делать, чтобы создать «другую» анимацию каждый раз, когда пользователь нажимает кнопку?

Общая идея состоит в том, чтобы анимации строились друг на друга со случайным новым значением, например:

  • первый спин: 0 -> 10
  • второй спин: 10 -> 13
  • третье вращение: 13 -> 18
  • ... и т. Д.

Так что мне стало интересно, могу ли я обновить анимацию или мне каждый раз создавать новую анимацию? Еще одна вещь, о которой я мог подумать, это отслеживать позиции и использовать одну и ту же анимацию каждый раз (0.0 -> 100.0), чтобы действовать как процент от передачи.

Поэтому вместо создания новой анимации из 10 -> 15 я бы делал что-то вроде: currentValue = 10 + (15-10)/100*_animationController.value

Ответы [ 2 ]

0 голосов
/ 11 ноября 2018

Во время работы вы ни в коем случае не захотите сделать эти анимации в пределах вашего макета, как объясняет @filleduchaos.

Это недостаточно оптимизировано, поскольку вы перестраиваете намного больше, чем нужно для анимации.И писать себе тяжело.

Для этого вам понадобится семья AnimatedWidget.Они делятся на два вида:

  • XXTransition
  • AnimatedXX

Первый - это низкий слой, который потребляет Animation и слушает его так,что вам не нужно делать это безобразно:

..addListener(() {
  setState(() {});
});

Второй обрабатывает оставшиеся части: AnimationController, TickerProvider и Tween.

Это делает использование анимаций намного проще, поскольку это почти полностью автоматически.

В вашем случае пример поворота будет следующим:

class RotationExample extends StatefulWidget {
  final Widget child;

  const RotationExample({
    Key key,
    this.child,
  }) : super(key: key);

  @override
  RotationExampleState createState() {
    return new RotationExampleState();
  }
}

class RotationExampleState extends State<RotationExample> {
  final _random = math.Random();
  double rad = 0.0;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _rotate,
      child: AnimatedTransform(
        duration: const Duration(seconds: 1),
        alignment: Alignment.center,
        transform: Matrix4.rotationZ(rad),
        child: Container(
          color: Colors.red,
          height: 42.0,
          width: 42.0,
        ),
      ),
    );
  }

  void _rotate() {
    setState(() {
      rad = math.pi * 2 / 25 * _random.nextInt(25);
    });
  }
}

Проще, правда?

Ирония в том, что Флаттер забыл предоставить AnimatedTransform (хотяу нас много других!).Но не беспокойтесь, я сделал это для вас!

Реализация AnimatedTransform выглядит следующим образом:

class AnimatedTransform extends ImplicitlyAnimatedWidget {
  final Matrix4 transform;
  final AlignmentGeometry alignment;
  final bool transformHitTests;
  final Offset origin;
  final Widget child;

  const AnimatedTransform({
    Key key,
    @required this.transform,
    @required Duration duration,
    this.alignment,
    this.transformHitTests = true,
    this.origin,
    this.child,
    Curve curve = Curves.linear,
  })  : assert(transform != null),
        assert(duration != null),
        super(
          key: key,
          duration: duration,
          curve: curve,
        );

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

class _AnimatedTransformState
    extends AnimatedWidgetBaseState<AnimatedTransform> {
  Matrix4Tween _transform;

  @override
  void forEachTween(TweenVisitor<dynamic> visitor) {
    _transform = visitor(_transform, widget.transform,
        (dynamic value) => Matrix4Tween(begin: value));
  }

  @override
  Widget build(BuildContext context) {
    return Transform(
      alignment: widget.alignment,
      transform: _transform.evaluate(animation),
      transformHitTests: widget.transformHitTests,
      origin: widget.origin,
      child: widget.child,
    );
  }
}

Я отправлю запрос на извлечение, чтобы в будущем вам не понадобилсяэтот бит кода.

0 голосов
/ 11 ноября 2018

Я собираюсь немного пропустить ваш код и сосредоточиться на том, что вы действительно спрашиваете:

Общая идея состоит в том, чтобы анимации строились друг на друга со случайным новым значением, например:

  • первый спин: 0 -> 10

  • второе вращение: 10 -> 13

  • третье вращение: 13 -> 18

  • ... и т. Д.

С такой явной анимацией есть три объекта, которые вас интересуют:

  • a контроллер , который представляет собой особый вид анимации, который просто генерирует значения линейно от своей нижней до верхней границы (оба значения удваиваются, обычно 0,0 и 1,0). Вы можете контролировать поток анимации - отправить его вперед, повернуть вспять, остановить или сбросить.

  • a tween , который не анимация, а Animatable . Твин определяет интерполяцию между двумя значениями, которые даже не должны быть числами. Он реализует метод transform под капотом, который принимает текущее значение анимации и выделяет фактическое значение, с которым вы хотите работать: другое число, цвет, линейный градиент, даже целый виджет. Это то, что вы должны использовать для генерации ваших углов поворота.

  • и анимация , которая представляет собой анимацию, со значением которой вы на самом деле собираетесь работать (так что именно здесь вы можете получить значения для построения). Вы получаете это, предоставляя вашему подростку родительскую анимацию для преобразования - это может быть непосредственно ваш контроллер, но также может быть какая-то другая анимация, которую вы построили на ней (например, CurvedAnimation, которая даст вам кривые замедления или упругих / упругих и скоро). Анимации Флаттера очень хорошо сочетаются.

Ваш код в основном терпит неудачу, потому что вы на самом деле не используете анимацию верхнего уровня, созданную в вашем методе сборки и , вы создаете новую анимацию и анимацию каждый раз, когда вызываете setNewPosition. Вы можете использовать одну и ту же анимацию и анимацию для нескольких анимационных «циклов» - просто измените свойства begin и end существующей анимации, и она будет пузыриться до анимации. Это заканчивается примерно так:

class _RoulettePageWidgetState extends State<RoulettePageWidget>
with SingleTickerProviderStateMixin {
   Animation<double> _animation;
   Tween<double> _tween;
   AnimationController _animationController;
   math.Random _random = math.Random();

   int position = 0;

   double getRandomAngle() {
      return math.pi * 2 / 25 * _random.nextInt(25);
   }

   @override
   void initState() {
      super.initState();
      _animationController =
          AnimationController(duration: Duration(seconds: 2), vsync: this);
      _tween = Tween(begin: 0.0, end: getRandomAngle());
      _animation = _tween.animate(_animationController)
          ..addListener(() {
              setState(() {});
          });
   }

   void setNewPosition() {
      _tween.begin = _tween.end;
      _animationController.reset();
      _tween.end = getRandomAngle();
      _animationController.forward();
   }

   @override
   Widget build(BuildContext context) {
      return Container(
         child: Column(
            children: <Widget>[
               Center(
                  child: Transform.rotate(
                     angle: _animation.value,
                     child: Icon(
                        Icons.arrow_upward,
                     size: 250.0,
                  ),
               )),
               Expanded(
                  child: Container(),
               ),
               RaisedButton(
                  child: Text('SPIN'),
                  onPressed: setNewPosition,
               )
            ],
         )
      );
   }
}

Надеюсь, это поможет!

...