<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 動畫基本結構 我們通過實現一個圖片逐漸放大的示例來演示一下Flutter中動畫的基本結構: ``` class ScaleAnimationRoute extends StatefulWidget { @override _ScaleAnimationRouteState createState() => new _ScaleAnimationRouteState(); } //需要繼承TickerProvider,如果有多個AnimationController,則應該使用TickerProviderStateMixin。 class _ScaleAnimationRouteState extends State<ScaleAnimationRoute> with SingleTickerProviderStateMixin{ Animation<double> animation; AnimationController controller; initState() { super.initState(); controller = new AnimationController( duration: const Duration(seconds: 3), vsync: this); //圖片寬高從0變到300 animation = new Tween(begin: 0.0, end: 300.0).animate(controller) ..addListener(() { setState(()=>{}); }); //啟動動畫(正向執行) controller.forward(); } @override Widget build(BuildContext context) { return new Center( child: Image.asset("images/avatar.png", width: animation.value, height: animation.value ), ); } dispose() { //路由銷毀時需要釋放動畫資源 controller.dispose(); super.dispose(); } } ``` 上面代碼中`addListener()`函數調用了`setState()`,所以每次動畫生成一個新的數字時,當前幀被標記為臟(dirty),這會導致widget的`build()`方法再次被調用,而在`build()`中,改變Image的寬高,因為它的高度和寬度現在使用的是`animation.value` ,所以就會逐漸放大。值得注意的是動畫完成時要釋放控制器(調用`dispose()`方法)以防止內存泄漏。 上面的例子中并沒有指定Curve,所以放大的過程是線性的(勻速),下面我們指定一個Curve,來實現一個類似于彈簧效果的動畫過程,我們只需要將`initState`中的代碼改為下面這樣即可: ``` initState() { super.initState(); controller = new AnimationController( duration: const Duration(seconds: 3), vsync: this); //使用彈性曲線 animation=CurvedAnimation(parent: controller, curve: Curves.bounceIn); //圖片寬高從0變到300 animation = new Tween(begin: 0.0, end: 300.0).animate(animation) ..addListener(() { setState(() { }); }); //啟動動畫 controller.forward(); } ``` ### 使用AnimatedWidget簡化 細心的讀者可能已經發現上面示例中通過`addListener()`和`setState()` 來更新UI這一步其實是通用的,如果每個動畫中都加這么一句是比較繁瑣的。AnimatedWidget類封裝了調用`setState()`的細節,并允許我們將Widget分離出來,重構后的代碼如下: ``` class AnimatedImage extends AnimatedWidget { AnimatedImage({Key key, Animation<double> animation}) : super(key: key, listenable: animation); Widget build(BuildContext context) { final Animation<double> animation = listenable; return new Center( child: Image.asset("images/avatar.png", width: animation.value, height: animation.value ), ); } } class ScaleAnimationRoute extends StatefulWidget { @override _ScaleAnimationRouteState createState() => new _ScaleAnimationRouteState(); } class _ScaleAnimationRouteState extends State<ScaleAnimationRoute> with SingleTickerProviderStateMixin { Animation<double> animation; AnimationController controller; initState() { super.initState(); controller = new AnimationController( duration: const Duration(seconds: 3), vsync: this); //圖片寬高從0變到300 animation = new Tween(begin: 0.0, end: 300.0).animate(controller); //啟動動畫 controller.forward(); } @override Widget build(BuildContext context) { return AnimatedImage(animation: animation,); } dispose() { //路由銷毀時需要釋放動畫資源 controller.dispose(); super.dispose(); } } ``` ### 用AnimatedBuilder重構 用AnimatedWidget可以從動畫中分離出widget,而動畫的渲染過程(即設置寬高)仍然在AnimatedWidget中,假設如果我們再添加一個widget透明度變化的動畫,那么我們需要再實現一個AnimatedWidget,這樣不是很優雅,如果我們能把渲染過程也抽象出來,那就會好很多,而AnimatedBuilder正是將渲染邏輯分離出來, 上面的build方法中的代碼可以改為: ``` @override Widget build(BuildContext context) { //return AnimatedImage(animation: animation,); return AnimatedBuilder( animation: animation, child: Image.asset("images/avatar.png"), builder: (BuildContext ctx, Widget child) { return new Center( child: Container( height: animation.value, width: animation.value, child: child, ), ); }, ); } ``` 上面的代碼中有一個迷惑的問題是,`child`看起來像被指定了兩次。但實際發生的事情是:將外部引用child傳遞給AnimatedBuilder后AnimatedBuilder再將其傳遞給匿名構造器, 然后將該對象用作其子對象。最終的結果是AnimatedBuilder返回的對象插入到Widget樹中。 也許你會說這和我們剛開始的示例差不了多少,其實它會帶來三個好處: 1. 不用顯式的去添加幀監聽器,然后再調用`setState()` 了,這個好處和AnimatedWidget是一樣的。 2. 動畫構建的范圍縮小了,如果沒有builder,setState()將會在父widget上下文調用,這將會導致父widget的build方法重新調用,而有了builder之后,只會導致動畫widget的build重新調用,這在復雜布局下性能會提高。 3. 通過AnimatedBuilder可以封裝常見的過渡效果來復用動畫。下面我們通過封裝一個GrowTransition來說明,它可以對子widget實現放大動畫: ``` class GrowTransition extends StatelessWidget { GrowTransition({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) { return new Container( height: animation.value, width: animation.value, child: child ); }, child: child ), ); } } ``` 這樣,最初的示例就可以改為: ``` ... Widget build(BuildContext context) { return GrowTransition( child: Image.asset("images/avatar.png"), animation: animation, ); } ``` **Flutter中正是通過這種方式封裝了很多動畫,如:FadeTransition、ScaleTransition、SizeTransition、FractionalTranslation等,很多時候都可以復用這些預置的過渡類。** ## 動畫狀態監聽 上面說過,我們可以通過Animation的`addStatusListener()`方法來添加動畫狀態改變監聽器。Flutter中,有四種動畫狀態,在AnimationStatus枚舉類中定義,下面我們逐個說明: 枚舉值含義`dismissed`動畫在起始點停止`forward`動畫正在正向執行`reverse`動畫正在反向執行`completed`動畫在終點停止#### 示例 我們將上面圖片放大的示例改為先放大再縮小再放大……這樣的循環動畫。要實現這種效果,我們只需要監聽動畫狀態的改變即可,即:在動畫正向執行結束時反轉動畫,在動畫反向執行結束時再正向執行動畫。代碼如下: ``` initState() { super.initState(); controller = new AnimationController( duration: const Duration(seconds: 1), vsync: this); //圖片寬高從0變到300 animation = new Tween(begin: 0.0, end: 300.0).animate(controller); animation.addStatusListener((status) { if (status == AnimationStatus.completed) { //動畫執行結束時反向執行動畫 controller.reverse(); } else if (status == AnimationStatus.dismissed) { //動畫恢復到初始狀態時執行動畫(正向) controller.forward(); } }); //啟動動畫(正向) controller.forward(); } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看