首頁 > 軟體

Android Flutter實現3D動畫效果範例詳解

2022-03-23 19:00:13

前言

上一篇我們介紹了 Animation 和 AnimationController 的使用,這是最基本的動畫構建類。但是,如果我們想構建一個可複用的動畫元件,通過外部引數來控制其動畫效果的時候,上一篇的方法就不太合適了。在 Flutter 中提供了 AnimatedWidget 元件用於構建可複用的動畫元件。本篇我們用 AnimatedWidget 來實現元件的3D 旋轉效果,如下圖所示。

AnimatedWidget 簡介

AnimatedWidget是一個抽象的 StatefulWidget, 構造方法如下所示。

const AnimatedWidget({
    Key? key,
    required this.listenable,
  }) : assert(listenable != null),
       super(key: key);

主要在於接收一個 listenable 引數,通常會是 Animation 物件。在 AnimatedWidget 內部的_AnimatedState 類中,會新增該物件變化監聽回撥,進而重新整理介面。

class _AnimatedState extends State<AnimatedWidget> {
  @override
  void initState() {
    super.initState();
    widget.listenable.addListener(_handleChange);
  }

  @override
  void didUpdateWidget(AnimatedWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.listenable != oldWidget.listenable) {
      oldWidget.listenable.removeListener(_handleChange);
      widget.listenable.addListener(_handleChange);
    }
  }

  @override
  void dispose() {
    widget.listenable.removeListener(_handleChange);
    super.dispose();
  }

  void _handleChange() {
    setState(() {
      // The listenable's state is our build state, and it changed already.
    });
  }
  
  // ...
}

可以看到,只需要將 Animation 物件傳給 AnimatedWidget 物件後,就不需要我們之前那樣自己寫 addListener 之類的處理了。而整個動畫可以交給外部其他物件來控制,從而實現動畫元件的複用。

3D 旋轉動畫的實現

3D 旋轉的實現比較簡單,在 Container 元件有兩個引數控制轉換(transform),分別是:

  • transformMatrix4物件,可以實現圍繞 X、Y、Z軸的旋轉、平移,以及變形等效果。關於 Matrix4涉及到很多矩陣運算和線性代數的知識,可以參考 Matrix4的原始碼自行溫習一下大學的數學知識。
  • transformAlignment:轉換的對齊方式,可以理解為起點位置,可以使用 Alignment 物件來設定。

有了這個基礎,我們就可以定義3D 旋轉動效了,我們定義一個通用的元件,ThreeDAnimatedWidget

class ThreeDAnimatedWidget extends AnimatedWidget {
  final Widget child;
  const ThreeDAnimatedWidget(
      {Key? key, required Animation<double> animation, required this.child})
      : super(key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    final animation = listenable as Animation<double>;

    return Center(
      child: Container(
        transform: Matrix4.identity()
          ..rotateY(2 * pi * animation.value)
          ..setEntry(1, 0, 0.01),
        transformAlignment: Alignment.center,
        child: child,
      ),
    );
  }
}

這裡我們設定的是圍繞中心點繞 Y 軸旋轉,並使用 setEntry 設定了一定的傾斜角 (這會更有立體感)。實際我們也可以設定圍繞 X 軸或 Z 軸旋轉。接下來就是這個動畫元件的應用了,我們構建一個帶有陰影的文字(看起來像立體字)作為這個動畫的子元件,其他的控制和上一篇的是類似的,完整程式碼如下:

class AnimatedWidgetDemo extends StatefulWidget {
  const AnimatedWidgetDemo({Key? key}) : super(key: key);

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

class _AnimatedWidgetDemoState extends State<AnimatedWidgetDemo>
    with SingleTickerProviderStateMixin {
  late Animation<double> animation;
  late AnimationController controller;

  @override
  void initState() {
    super.initState();
    controller =
        AnimationController(duration: const Duration(seconds: 3), vsync: this);
    animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('AnimatedWidget 動畫'),
      ),
      body: ThreeDAnimatedWidget(
        animation: animation,
        child: Text(
          '島上碼農',
          style: TextStyle(
            fontSize: 42.0,
            color: Colors.blue,
            fontWeight: FontWeight.bold,
            shadows: [
              Shadow(
                  blurRadius: 2,
                  offset: Offset(2.0, 1.0),
                  color: Colors.blue[900]!),
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow, color: Colors.white),
        onPressed: () {
          if (controller.status == AnimationStatus.completed) {
            controller.reverse();
          } else {
            controller.forward();
          }
        },
      ),
    );
  }

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

可以看到,這個 ThreeDAnimatedWidget 可以做到複用了,在需要這樣動效的場景裡,按照上面的方式給它傳入 Animation 物件和子元件就可以了。例如我們將文字修改為一張圖片。

//...
body: ThreeDAnimatedWidget(
  animation: animation,
  child: Image.asset(
    'images/avatar.jpg',
    width: 100,
    height: 100,
  ),
),
//...

圖片旋轉.gif

總結

本篇介紹了 AnimatedWidget 的使用,通過 AnimatedWidget 可以構建可複用的動畫元件。同時,還通過 Container 的 transform 屬性加上 AnimatedWidget 實現了三維空間的旋轉效果。實際開發過程中,我們可以基於 AnimatedWidget 構建很多個性化的、可複用的動畫元件,提升應用的趣味性。

以上就是Android Flutter實現3D動畫效果範例詳解的詳細內容,更多關於Flutter 3D動畫的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com