Flutter學習(六) 動畫以及動效相關

Jacksonl發表於2019-11-22

Flutter學習 (一) dart基本語法

Flutter 學習(二) dart基礎語法

Flutter 學習(三) dart基礎

Flutter 學習(四) 基礎控制元件

Flutter學習(五) 基礎控制元件 以及ios的Cupertino風格

前幾篇文章講了一些基礎widget 的使用方式,這篇文章會介紹flutter 動畫部分的簡單使用.

1. Animation

1.AnimationController

AnimationController會產生數值0--1區間內的數值變換,也可以控制動畫 reverse(),forward(),animateTo()等 構造引數中vsync 繫結管理動畫執行的定時器,widget不顯示時暫停動畫,顯示時回覆動畫,Duration 設定動畫時間

AnimationController _animController =
        AnimationController(vsync: this, duration: Duration(seconds: 2))
          ..reverse();
複製程式碼

2.Tween 補間動畫

如果你想產生 0--1 之外的數值變換 可以使用Tween 來實現,當前Tween 不僅僅只能是數值型別的 可以是顏色 曲線等型別ColorTween CurveTween 具體可以檢視官網API文件說明. 使用時需要呼叫animate()方法去傳入控制器,這裡傳入的引數是 Animation所以 除了控制器也可以去傳入一個 Animation的動畫,另外也可以使用 addListener() addStatusListener() 去監聽動畫執行過程 以及狀態,通過 value() 去獲取當前的數值 作用到 widget上


//產生數值
AnimationController  _animController =
        AnimationController(vsync: this, duration: Duration(seconds: 2));
    _animtion = Tween<double>(begin:100.0, end:
       final Animation curve =new CurvedAnimation(parent: _animController, curve: Curves.easeOut);
200.0).animate(curve)
      ..addListener(() {
        setState(() {
          _currentValue = _animtion.value;
        });
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          _animController.reverse();
        } else if (status == AnimationStatus.forward) {
          _animController.forward();
        }
      });
      
      
      
      //使用
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(onPressed: () {
        _animController.forward();
      }),
      body: Center(
        child: Container(
          width: _currentValue,
          height: _currentValue,
          color: Colors.blue,
          child: Text('$_currentValue'),
        ),
      ),
    );
  }
複製程式碼

2.AnimatedWidget

使用時繼承這個Widget,在使用 Tween 動畫時 在數值產生時需要去呼叫setState((){}) 去 重繪佈局, 如果使用AnimatedWidget 可以省略這部 它內部會直接對狀態更新進行管理. controller 和 animation 從外部傳入,建立方式跟Twee一樣 ,在構造中傳入 anim 物件


//控制器物件建立
  @override
  void initState() {
    super.initState();
    _animController =
        AnimationController(vsync: this, duration: Duration(seconds: 2));
    _animtion = Tween<double>(begin: 100.0, end: 200.0).animate(_animController)
      ..addListener(() {
        setState(() {
          _currentValue = _animtion.value;
        });
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          _animController.reverse();
        } else if (status == AnimationStatus.forward) {
          _animController.forward();
        }
      });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(onPressed: () {
        _animController.forward();
      }),
      body: MyLogo(
        anim: _animtion,
      ),
    );
  }
  
  
  //動畫widget
class MyLogo extends AnimatedWidget {
  MyLogo({Key key, @required Animation anim}) : super(listenable: anim);

  final Tween<double> _rotateAnim = Tween<double>(begin: 0.0, end: 100.0);
  final Tween<double> _scaleAnim = Tween<double>(begin: 1.0, end: 10.0);

  @override
  Widget build(BuildContext context) {
    Animation anim = listenable;
    return Center(
      child: Transform.rotate(
        angle: _rotateAnim.evaluate(anim),
        child: Transform.scale(
          scale: _scaleAnim.evaluate(anim),
          child: Container(
            /*  width: anim.value,
            height: anim.value,*/
            child: FlutterLogo(),
          ),
        ),
      ),
    );
  }
}

複製程式碼

3.AnimatedBuilder

分離物件與Widget,使用AnimatedBuilder將動畫描述為另一個widget的build方法的一部分,controller animation 建立與前面一致


  @override
  Widget build(BuildContext context) {
    double _screenWidth = MediaQuery.of(context).size.width;
    return Scaffold(
      floatingActionButton: FloatingActionButton(onPressed: () {
        _animController.forward();
      }),
      body: Center(
        child: AnimatedBuilder(
            animation: _animtion,
            builder: (context, child) {
              return Transform(
                child: Container(width: 200, height: 200, child: FlutterLogo()),
                transform: Matrix4.translationValues(
                    _screenWidth * _animtion.value, 0, 0),
              );
            }),
      ),
    );
  }
複製程式碼

3.並行動畫

可以對一個widget 同時做動畫 透明度和 寬高改變

class AnimatedLogo extends AnimatedWidget {
  // The Tweens are static because they don't change.
  static final _opacityTween = new Tween<double>(begin: 0.1, end: 1.0);
  static final _sizeTween = new Tween<double>(begin: 0.0, end: 300.0);

  AnimatedLogo({Key key, Animation<double> animation})
      : super(key: key, listenable: animation);

  Widget build(BuildContext context) {
    final Animation<double> animation = listenable;
    return new Center(
      child: new Opacity(
        opacity: _opacityTween.evaluate(animation),
        child: new Container(
          margin: new EdgeInsets.symmetric(vertical: 10.0),
          height: _sizeTween.evaluate(animation),
          width: _sizeTween.evaluate(animation),
          child: new FlutterLogo(),
        ),
      ),
    );
  }
}
複製程式碼

4. 自定義繪製Widget

在原生 java 中 自定義控制元件 需要用到 畫筆paint 畫布canvas 在flutter中也是用這些來繪製頁面, 使用的時候需要用到CustomPaint 包裹自己定義的Painter ,paint canvas的使用與java 類似就不在贅述

//使用方式
 child: CustomPaint(
            size: Size(30, 30),
            painter: HeartPainter(),
          ),
          
          
   //繪製頁面       
class HeartPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    // TODO: implement paint
    Paint paint = Paint();
    paint
      ..color = Color(0xffff0000)
      ..style = PaintingStyle.fill
      ..strokeWidth = 5;

    double width = size.width;
    double height = size.height;

    Path path = Path();
    path.moveTo(0.5 * width, height * 0.35);
    path.cubicTo(0.2 * width, height * 0.1, -0.25 * width, height * 0.6,
        0.5 * width, height);
    path.moveTo(0.5 * width, height * 0.35);
    path.cubicTo(0.8 * width, height * 0.1, 1.25 * width, height * 0.6,
        0.5 * width, height);

    canvas.drawPath(path, paint);
  }

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


複製程式碼

5.Transform 矩陣變換

Transform 可以對子widget 做一些矩陣變化,達到一定的效果

  • Transform.translate 平移
  • Transform.rotate 旋轉
  • Transform.scale 縮放
  • ....

6. Hero

類似原生中的頁面共享動畫,在頁面A用Hero 包裹想要進行動畫的 widget 並設定tag ,在跳轉之後的B頁面 同樣適用Hero 包裹widget 設定相同的 tag

        Hero(
          tag: "avatar", //唯一標記,前後兩個路由頁Hero的tag必須相同
          child: ClipOval(
            child: Image.asset("images/avatar.png",
              width: 50.0,
            ),
          ),
複製程式碼

轉場動畫

 Navigator.push(context, CupertinoPageRoute(  
   builder: (context)=>PageB(),
 ));
 
 
 Navigator.push(
  context,
  PageRouteBuilder(
    transitionDuration: Duration(milliseconds: 500), //動畫時間為500毫秒
    pageBuilder: (BuildContext context, Animation animation,
        Animation secondaryAnimation) {
      return new FadeTransition(
        //使用漸隱漸入過渡,
        opacity: animation,
        child: PageB(), //路由B
      );
    },
  ),
);
複製程式碼

相關文章