Флаттер-анимация с нуля

Flutter
Флаттер-анимация с нуля

предисловие

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

Анимации обычно можно разделить на две категории:

"промежуточная анимация": Tween-анимация — это разновидность анимации, которая заранее определяет начальную и конечную точки движения объекта, метод движения объекта, время движения, временную кривую, а затем осуществляет переход от начальной точки к конечной точке.

"Анимация на основе физики": Анимация на основе физики — это анимация, которая имитирует реальное движение путем создания модели движения. Например, когда баскетбольный мяч 🏀 падает с высоты, необходимо создать модель движения в соответствии с высотой его падения, гравитационным ускорением, силой отскока от земли и другими влияющими факторами.

Анимация во флаттере

Во Flutter есть много типов анимации, начиная с простого примера, используяAnimatedContainerуправления, затем установите продолжительность анимацииduration, наконец звонитsetStateМетод изменяет значение свойства, которое необходимо изменить, и создается анимация.

animated-container
animated-container

код показывает, как показано ниже

import 'package:flutter/material.dart';

class AnimatedContainerPage extends StatefulWidget {
  @override
  _AnimatedContainerPageState createState() => _AnimatedContainerPageState();
}

class _AnimatedContainerPageState extends State<AnimatedContainerPage> {
  // 初始的属性值
  double size = 100;
  double raidus = 25;
  Color color = Colors.yellow;

  void _animate() {
    // 改变属性值
    setState(() {
      size = size == 100 ? 200 : 100;
      raidus = raidus == 25 ? 100 : 25;
      color = color == Colors.yellow ? Colors.greenAccent : Colors.yellow;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Animated Container')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 在 AnimatedContainer 上应用属性值
            AnimatedContainer(
              width: size,
              height: size,
              curve: Curves.easeIn,
              padding: const EdgeInsets.all(20.0),
              decoration: BoxDecoration(
                color: color,
                borderRadius: BorderRadius.circular(raidus),
              ),
              duration: Duration(seconds: 1),
              child: FlutterLogo(),
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _animate,
        child: Icon(Icons.refresh),
      ),
    );
  }
}

Это имплицитная анимация, помимо явной анимации, Хрео-анимация, чересстрочная анимация.

Основные понятия

Флаттер-анимация построена на следующих концепциях.

Animation

Система анимации на основе флаттераAnimationОбъект, представляющий собой абстрактный класс, который содержит значение и состояние текущей анимации (запуск, пауза, перемотка вперед, перемотка назад), но не записывает то, что отображается на экране. Элементы пользовательского интерфейса считываютсяAnimationЗначение объекта и прослушивание изменений состояния для запускаbuildфункция, а затем отображается на экране для создания эффекта анимации.

ОдинAnimationОбъекты продолжают генерировать значения между двумя значениями в течение определенного периода времени, более распространенными типами являютсяAnimation<double>,УдалитьdoubleВ дополнение к типуAnimation<Color>илиAnimation<Size>Ждать.

abstract class Animation<T> extends Listenable implements ValueListenable<T> {
  /// ...
}

AnimationController

с методом управленияAnimationОбъект, используемый для управления запуском, паузой, окончанием анимации, установкой времени выполнения анимации и т. д.

class AnimationController extends Animation<double>
  with AnimationEagerListenerMixin, AnimationLocalListenersMixin, AnimationLocalStatusListenersMixin {
  /// ...
}

AnimationController controller = AnimationController(
  vsync: this,
  duration: Duration(seconds: 10),
);

Tween

Используется для генерации значений анимации разных типов и диапазонов.

class Tween<T extends dynamic> extends Animatable<T> {
  Tween({ this.begin, this.end });
  /// ...
}

// double 类型
Tween<double> tween = Tween<double>(begin: -200, end: 200);

// color 类型
ColorTween colorTween = ColorTween(begin: Colors.blue, end: Colors.yellow);

// border radius 类型
BorderRadiusTween radiusTween = BorderRadiusTween(
  begin: BorderRadius.circular(0.0),
  end: BorderRadius.circular(150.0),
);

Curve

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

class CurvedAnimation extends Animation<double> with AnimationWithParentMixin<double> {
  /// ...
}

Animation animation = CurvedAnimation(parent: controller, curve: Curves.easeIn);

Ticker

TickerФункция обратного вызова, используемая для добавления каждого обновления экранаTickerCallback, который называется каждым обновлением экрана. похоже на вебrequestAnimationFrameметод.

class Ticker {
  /// ...
}

Ticker ticker = Ticker(callback);

Неявная анимация

Неявные анимации создаются с помощью встроенных виджетов анимации фреймворка Flutter и запускаются путем установки начального и конечного значений анимации. когда используешьsetStateКогда метод изменяет значение свойства анимации виджета, платформа автоматически вычисляет анимацию, которая переходит от старого значения к новому значению.

НапримерAnimatedOpacityкомпонент, изменяя егоopacityЗначение может запускать анимацию.

opacity-toggle
opacity-toggle
import 'package:flutter/material.dart';

class OpacityChangePage extends StatefulWidget {
  @override
  _OpacityChangePageState createState() => _OpacityChangePageState();
}

class _OpacityChangePageState extends State<OpacityChangePage> {
  double _opacity = 1.0;

  // 改变目标值
  void _toggle() {
    _opacity = _opacity > 0 ? 0.0 : 1.0;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('隐式动画')),
      body: Center(
        child: AnimatedOpacity(
          // 传入目标值
          opacity: _opacity,
          duration: Duration(seconds: 1),
          child: Container(
            width: 200,
            height: 200,
            color: Colors.blue,
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _toggle,
        child: Icon(Icons.play_arrow),
      ),
    );
  }
}

КромеAnimatedOpacityКроме того, есть другие встроенные виджеты неявной анимации, такие как:AnimatedContainer, AnimatedPadding, AnimatedPositioned, AnimatedSwitcher,AnimatedAlignЖдать.

явная анимация

Явная анимация относится к анимации, для которой требуется ручная настройка времени анимации, кривой движения и диапазона значений. Передайте значения виджетам анимации, например:RotationTransition, и, наконец, используйтеAnimationControllerУправляет началом и концом анимации.

explicit-animation
explicit-animation
import 'dart:math';
import 'package:flutter/material.dart';

class RotationAinmationPage extends StatefulWidget {
  @override
  _RotationAinmationPageState createState() => _RotationAinmationPageState();
}

class _RotationAinmationPageState extends State<RotationAinmationPage>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation<double> _turns;
  bool _playing = false;

  // 控制动画运行状态
  void _toggle() {
    if (_playing) {
      _playing = false;
      _controller.stop();
    } else {
      _controller.forward()..whenComplete(() => _controller.reverse());
      _playing = true;
    }
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    // 初始化动画控制器,设置动画时间
    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 10),
    );

    // 设置动画取值范围和时间曲线
    _turns = Tween(begin: 0.0, end: pi * 2).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeIn),
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('显示动画')),
      body: Center(
        child: RotationTransition(
          // 传入动画值
          turns: _turns,
          child: Container(
            width: 200,
            height: 200,
            child: Image.asset(
              'assets/images/fan.png',
              fit: BoxFit.cover,
            ),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _toggle,
        child: Icon(_playing ? Icons.pause : Icons.play_arrow),
      ),
    );
  }
}

КромеRotationTransitionКроме того, есть другие части анимации отображения, такие как:FadeTransition, ScaleTransition, SizeTransition, SlideTransitionЖдать.

Анимация героя

Анимация героя относится к анимации элемента, перемещающегося со старой страницы на новую страницу при переключении страницы. Анимация героя требует использования двухHeroРеализация управления: один используется на старой странице, а другой — на новой странице. дваHeroЭлементы управления должны использовать те жеtagсвойств и не может сочетаться с другимиtagповторить.

hero-animation
hero-animation
// 页面 1
import 'package:flutter/material.dart';

import 'hero_animation_page2.dart';

String cake1 = 'assets/images/cake01.jpg';
String cake2 = 'assets/images/cake02.jpg';

class HeroAnimationPage1 extends StatelessWidget {
  GestureDetector buildRowItem(context, String image) {
    return GestureDetector(
      onTap: () {
        // 跳转到页面 2
        Navigator.of(context).push(
          MaterialPageRoute(builder: (ctx) {
            return HeroAnimationPage2(image: image);
          }),
        );
      },
      child: Container(
        width: 100,
        height: 100,
        child: Hero(
          // 设置 Hero 的 tag 属性
          tag: image,
          child: ClipOval(child: Image.asset(image)),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('页面 1')),
      body: Column(
        children: <Widget>[
          SizedBox(height: 40.0),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: <Widget>[
              buildRowItem(context, cake1),
              buildRowItem(context, cake2),
            ],
          ),
        ],
      ),
    );
  }
}

// 页面 2
import 'package:flutter/material.dart';

class HeroAnimationPage2 extends StatelessWidget {
  final String image;

  const HeroAnimationPage2({@required this.image});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CustomScrollView(
        slivers: <Widget>[
          SliverAppBar(
            expandedHeight: 400.0,
            title: Text('页面 2'),
            backgroundColor: Colors.grey[200],
            flexibleSpace: FlexibleSpaceBar(
              collapseMode: CollapseMode.parallax,
              background: Hero(
                // 使用从页面 1 传入的 tag 值
                tag: image,
                child: Container(
                  decoration: BoxDecoration(
                    image: DecorationImage(
                      image: AssetImage(image),
                      fit: BoxFit.cover,
                    ),
                  ),
                ),
              ),
            ),
          ),
          SliverList(
            delegate: SliverChildListDelegate(
              <Widget>[
                Container(height: 600.0, color: Colors.grey[200]),
              ],
            ),
          ),
        ],
      ),
    );
  }
}


Чередующаяся анимация

Чересстрочная анимация — это анимация, состоящая из серии небольших анимаций. Каждая небольшая анимация может быть непрерывной или прерывистой, а также может накладываться друг на друга. Ключевым моментом является использованиеIntervalВиджет устанавливает временной интервал для каждой небольшой анимации и диапазон значений для каждой анимации.Tween, и, наконец, используйтеAnimationControllerУправляет общим состоянием анимации.

IntervalунаследовалCurveкласс, установив свойстваbeginа такжеendчтобы определить рабочий диапазон этой маленькой анимации.

class Interval extends Curve {
  /// 动画起始点
  final double begin;
  /// 动画结束点
  final double end;
  /// 动画缓动曲线
  final Curve curve;

  /// ...
}

staggered-animation
staggered-animation

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

staggered-animation-timeline
staggered-animation-timeline
import 'package:flutter/material.dart';

class StaggeredAnimationPage extends StatefulWidget {
  @override
  _StaggeredAnimationPageState createState() => _StaggeredAnimationPageState();
}

class _StaggeredAnimationPageState extends State<StaggeredAnimationPage>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;
  Animation<double> _width;
  Animation<double> _height;
  Animation<Color> _color;
  Animation<double> _border;
  Animation<BorderRadius> _borderRadius;

  void _play() {
    if (_controller.isCompleted) {
      _controller.reverse();
    } else {
      _controller.forward();
    }
  }

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 5),
    );

    _width = Tween<double>(
      begin: 100,
      end: 300,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(
          0.0,
          0.2,
          curve: Curves.ease,
        ),
      ),
    );

    _height = Tween<double>(
      begin: 100,
      end: 300,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(
          0.2,
          0.4,
          curve: Curves.ease,
        ),
      ),
    );

    _color = ColorTween(
      begin: Colors.blue,
      end: Colors.yellow,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(
          0.4,
          0.6,
          curve: Curves.ease,
        ),
      ),
    );

    _borderRadius = BorderRadiusTween(
      begin: BorderRadius.circular(0.0),
      end: BorderRadius.circular(150.0),
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(
          0.6,
          0.8,
          curve: Curves.ease,
        ),
      ),
    );

    _border = Tween<double>(
      begin: 0,
      end: 25,
    ).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(0.8, 1.0),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('交织动画')),
      body: Center(
        child: AnimatedBuilder(
          animation: _controller,
          builder: (BuildContext context, Widget child) {
            return Container(
              width: _width.value,
              height: _height.value,
              decoration: BoxDecoration(
                color: _color.value,
                borderRadius: _borderRadius.value,
                border: Border.all(
                  width: _border.value,
                  color: Colors.orange,
                ),
              ),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _play,
        child: Icon(Icons.refresh),
      ),
    );
  }
}

Физическая анимация

Анимация — это аналоговая анимация движения объекта физического реального мира. Необходимо установить спортивную модель объекта с учетом местонахождения объекта, например, на это движение влияют факторы высоты падения объекта, ускорение свободного падения, силы реакции земли и т.д.

throw-animation
throw-animation
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

class ThrowAnimationPage extends StatefulWidget {
  @override
  _ThrowAnimationPageState createState() => _ThrowAnimationPageState();
}

class _ThrowAnimationPageState extends State<ThrowAnimationPage> {
  // 球心高度
  double y = 70.0;
  // Y 轴速度
  double vy = -10.0;
  // 重力
  double gravity = 0.1;
  // 地面反弹力
  double bounce = -0.5;
  // 球的半径
  double radius = 50.0;
  // 地面高度
  final double height = 700;

  // 下落方法
  void _fall(_) {
    y += vy;
    vy += gravity;

    // 如果球体接触到地面,根据地面反弹力改变球体的 Y 轴速度
    if (y + radius > height) {
      y = height - radius;
      vy *= bounce;
    } else if (y - radius < 0) {
      y = 0 + radius;
      vy *= bounce;
    }

    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    // 使用一个 Ticker 在每次更新界面时运行球体下落方法
    Ticker(_fall)..start();
  }

  @override
  Widget build(BuildContext context) {
    double screenWidth = MediaQuery.of(context).size.width;

    return Scaffold(
      appBar: AppBar(title: Text('物理动画')),
      body: Column(
        children: <Widget>[
          Container(
            height: height,
            child: Stack(
              children: <Widget>[
                Positioned(
                  top: y - radius,
                  left: screenWidth / 2 - radius,
                  child: Container(
                    width: radius * 2,
                    height: radius * 2,
                    decoration: BoxDecoration(
                      color: Colors.blue,
                      shape: BoxShape.circle,
                    ),
                  ),
                ),
              ],
            ),
          ),
          Expanded(child: Container(color: Colors.blue)),
        ],
      ),
    );
  }
}

Суммировать

В этой статье представлены различные типы анимации во Flutter, а именно

  • Неявная анимация
  • явная анимация
  • Анимация героя
  • Чередующаяся анимация
  • Анимация на основе физики

Анимация флаттера основана на типизированномAnimationобъект,WidgetsПовторите запуск, прочитав текущее значение объекта анимации и прослушивая изменения состояния.buildФункция, постоянно изменяющая пользовательский интерфейс для формирования эффекта анимации.

Основными элементами анимации являются

  • Animationобъект анимации
  • AnimationControllerконтроллер анимации
  • Tweenдиапазон значений анимации
  • Curveкривая движения анимации

Ссылаться на

Flutter animation basics with implicit animations

Directional animations with built-in explicit animations

Введение в анимационные эффекты

Введение во флаттер-анимацию

Реализация анимационных эффектов в приложениях Flutter

В этой статье используетсяmdniceнабор текста