Flutter——動畫基礎(補間動畫)

前言

看過不少文章關於寫Flutter動畫的,可是大多數就是照着文檔抄的文章,也有的就是你轉發個人,我轉發你的。不少文章沒有加入本身對知識的理解,不知道爲何要這作。爲此我打算寫一篇本身理解的Flutter動畫。bash


動畫概述

flutter中動畫分爲兩類:基於tween或基於物理的。app

補間(Tween)動畫

「介於二者之間」的簡稱。在補間動畫中,定義了開始點和結束點、時間線以及定義轉換時間和速度的曲線。而後由框架計算如何從開始點過渡到結束點。
框架

基於物理的動畫

在基於物理的動畫中,運動被模擬爲與真實世界的行爲類似。例如,當你擲球時,它在何處落地,取決於拋球速度有多快、球有多重、距離地面有多遠。 相似地,將鏈接在彈簧上的球落下(並彈起)與鏈接到繩子上的球放下的方式也是不一樣。
less


基礎概念

1,Animation對象

  • 它是Flutter動畫庫中的一個核心類,它生成指導動畫的值。這個值包括顏色,大小等。
  • 它知道當前動畫的狀態(開始,中止,反轉,完成),可是它不知道屏幕上渲染的UI是什麼,也就是說它不關心你的動畫對象UI渲染狀況。
  • 它是一個抽象類,具體功能實現由 其子類完成。
  • 一個比較經常使用的Animation類是Animation<double>,它默認產生的值範圍是(0.0-1.0)


2,CurvedAnimation

它是Animation<double>的子類, 將動畫過程定義爲一個非線性曲線,也就是說咱們的動畫運行的一個路徑是怎樣的,或者說以一種什麼樣的形式去表現咱們的動畫。ide


3,AnimationController

它是Animation<double>的子類,是一個特殊的Animation對象,在屏幕刷新的每一幀,就會生成一個新的值。默認狀況下,AnimationController在給定的時間段內會線性的生成從0.0到1.0的數字。從類名能夠知道其意思:動畫控制器,它能夠控制動畫的狀態,監聽動畫的執行狀態,監聽動畫過程當中產生的值。
動畫

當建立一個AnimationController時,須要傳遞一個vsync參數,參數的做用就是避免動畫的UI不在當前屏幕時消耗沒必要要的資源。 經過將SingleTickerProviderStateMixin添加到類定義中,能夠將stateful對象做爲vsync的值。舉例:當咱們手機接到來電,界面就跳轉到接通電話界面,那麼來電以前正在運行的動畫會暫停。
ui


4,Tween

默認狀況下,AnimationController對象產生值的範圍是(0.0,1.0)。若是您須要不一樣的範圍或不一樣的數據類型,則可使用Tween來配置動畫以生成不一樣的範圍或數據類型的值。
this


概念總結:

  • Animation是動畫的核心抽象類,它經常使用的類對象是Animation<double>。
  • CurvedAnimation和AnimationController都是派生自Animation<double>,前者管理動畫的表現形式,後者管理動畫插值,狀態等。
  • 若是默認值知足不了咱們須要,此時咱們用Tween自定義插值範圍


案例

放大動畫

  1,main.dart,在body裏面放了一個執行動畫的組件,主要代碼在該組件中spa

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(

        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Flutter動畫"),
      ),
      body:Animation1() ,
    );
  }
}

複製代碼

2,Animation1.dart組件文件debug

import 'package:flutter/material.dart';

///create by:Administrator
///create at:2019-09-28 14:43
///des:

class Animation1 extends StatefulWidget {
  @override
  _Animation1State createState() => _Animation1State();
}

class _Animation1State extends State<Animation1>
    with SingleTickerProviderStateMixin {
  Animation animation;
  AnimationController animationController;
  
  @override
  void initState() {
    super.initState();
    animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 2000));//建立動畫控制器
    animation = Tween(begin: 10.0, end: 200.0).animate(animationController); //自定義插值範圍
    animation.addListener((){   //設置插值監聽器,經過setState()從新渲染UI
      setState(() {

      });
    });
    animationController.forward(); //啓動動畫
    
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child:Container(
        width: animation.value,
        height: animation.value,
        color: Colors.redAccent,
      ),
    );
  }

  @override
  void dispose() {
    super.dispose();
    animationController.dispose(); //釋放資源
  }
}複製代碼

3,說明:

  • 整個動畫效果就是讓Container長寬從10.0放大到200.0,動畫時長爲2秒。
  • 經過Tween建立了自定義的插值,範圍是(10.0,200.0),也就是Container在動畫執行過程當中改變值的範圍。而後經過animate()方法傳入動畫控制器對象animationController,獲取到Animation對象,前面咱們說過,這個對象對動畫有控制權,咱們須要經過該對象知道動畫的狀態和插值狀況
  • 經過設置插值監聽器,調用setState()方法從新渲染UI。
  • 經過animationController(動畫控制器)啓動動畫。


AnimatedWidget簡化放大動畫

  • AnimatedWidget類容許您從setState()調用中的動畫代碼中分離出widget代碼。AnimatedWidget不須要維護一個State對象來保存動畫。
  • AnimatedWidget是一個抽象類繼承自StateFulWidget。

1,Animation2.dart組件文件

import 'package:flutter/material.dart';

///create by:Administrator
///create at:2019-09-28 14:43
///des:

class Animation2 extends StatefulWidget {
  @override
  _Animation2tate createState() => _Animation2tate();
}

class _Animation2tate extends State<Animation2>
    with SingleTickerProviderStateMixin {
  Animation animation;
  AnimationController animationController;

  @override
  void initState() {
    super.initState();
    animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 2000));//建立動畫控制器
    animation = Tween(begin: 10.0, end: 200.0).animate(animationController); //自定義插值範圍
    animationController.forward(); //啓動動畫
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child:CustomWidget(animation: animation),
    );
  }

  @override
  void dispose() {
    super.dispose();
    animationController.dispose(); //釋放資源
  }
}

//須要執行動畫的組件封裝在這裏
class CustomWidget extends AnimatedWidget{
  CustomWidget({Key key, Animation<double> animation})
      : super(key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;
    return Container(
      width: animation.value,
      height: animation.value,
      color: Colors.redAccent,
    );
  }

}複製代碼

2,說明:

咱們把動畫組件中的child換成了CustomWidget,去掉了動畫的監聽和狀態的刷新。該替換組件是繼承自AnimatedWidget的,它接收一個Animation對象。AnimatedWidget(基類)中會自動調用addListener()和setState()方法,完成UI的刷新,此時動畫執行與以前同樣。

爲何說是簡化了操做呢,咱們能夠這麼理解,原先咱們須要本身監聽動畫執行管理狀態,此時咱們只須要定義出動畫規則,把規則交給AnimatedWidget就好了,其餘的咱們不用管了,讓它本身去完成動畫的狀態管理與UI刷新。

另外,咱們這樣寫的話,達到了動畫複用的效果,從這個層面上來看也能夠理解爲簡化了動畫。

感受從總體上面來看,這個簡化有點牽強附會,我暫時只能這樣理解了,讀者有更好的理解能夠下方留言,謝謝


AnimatedBuilder 重構動畫

前面兩種動畫實現中,咱們的插值都侵入到了組件的內部,看下面代碼。

return Container(
      width: animation.value,
      height: animation.value,
      color: Colors.redAccent,
    );複製代碼

若是此時咱們要更換這個組件怎麼辦,好比咱們想把動畫對象(須要執行動畫的組件)換成一張圖片,又或者是其餘的,那咱們不得不去修改這個動畫對象,是否是很麻煩,並且複用程度很低。那麼咱們有沒有什麼辦法把動畫對象,動畫規則都給分離開來,各司其職。此時就用到了AnimatedBuilder。它經過匿名構造器按照動畫插值從新構建UI,自動管理動畫狀態與刷新。

另外,這種刷新是局部的,它只針對於動畫對象作的刷新,而不是應用到整個頁面,因此我認爲這種刷新比上面兩種節省資源。


Animation3.dart組件文件

import 'package:flutter/material.dart';

///create by:Administrator
///create at:2019-09-28 17:29
///des:

class Animation3 extends StatefulWidget {
  @override
  _Animation3State createState() => _Animation3State();
}

class _Animation3State extends State<Animation3> with SingleTickerProviderStateMixin{
  Animation animation;
  AnimationController controller;

  initState() {
    super.initState();
    controller = new AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); //動畫控制器
    animation = new Tween(begin: 0.0, end: 300.0).animate(controller);  //自定義插值範圍
    controller.forward();//啓動動畫
  }

  @override
  Widget build(BuildContext context) {
    return  GrowBuild(child: MyContainer(), animation: animation);
  }

  dispose() {
    controller.dispose();
    super.dispose();
  }
}

//經過AnimatedBuilder實現動畫規則與動畫對象分離
class GrowBuild extends StatelessWidget {
  GrowBuild({this.child, this.animation});

  final Widget child;
  final Animation<double> animation;

  Widget build(BuildContext context) {
    return new Center(
      child: new AnimatedBuilder(
          animation: animation,
          builder: (BuildContext context, Widget child) {  //匿名構造器根據動畫插值從新構建child,而後從新渲染
            return new Container(
                height: animation.value, width: animation.value, child: child);
          },
          child: child),  //將從新渲染後的child回顯
    );
  }
}

//動畫對象
class MyContainer extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 100.0,
      height: 200.0,
      color: Colors.redAccent,
    );
  }

}
複製代碼


結束語:

以上是我的對Flutter動畫基礎的一點看法,不必定全對。若有錯誤,煩請指正。

另外,讀者在有些地方有本身的看法能夠留言,互相探討,謝謝。

相關文章
相關標籤/搜索