Android Flutter實(shí)現(xiàn)GIF動(dòng)畫效果的方法詳解
前言
我們之前介紹了不少有關(guān)動(dòng)畫的篇章。前面介紹的動(dòng)畫都是只有一個(gè)動(dòng)畫效果,那如果我們想對(duì)某個(gè)組件實(shí)現(xiàn)一組動(dòng)效,比如下面的效果,該怎么辦?

staggered animation
這個(gè)時(shí)候我們需要用到組合動(dòng)效, Flutter 提供了交錯(cuò)動(dòng)畫(Staggered Animation)的方式實(shí)現(xiàn)。對(duì)于多個(gè) Anmation 對(duì)象,可以共用一個(gè) AnimationController,然后在不同的時(shí)間段執(zhí)行動(dòng)畫效果。這就有點(diǎn)像 GIF 圖片一樣,一幀幀圖像播放實(shí)現(xiàn)連續(xù)的動(dòng)畫。
交錯(cuò)動(dòng)畫機(jī)制
交錯(cuò)動(dòng)畫的實(shí)現(xiàn)基于以下幾個(gè)要點(diǎn):
- 所有的 animation對(duì)象使用同一個(gè)
AnimationController驅(qū)動(dòng); - 不管實(shí)際動(dòng)畫持續(xù)的時(shí)間長(zhǎng)度多長(zhǎng),動(dòng)畫控制器
controller的值必須在0-1之間; - 每個(gè)動(dòng)畫對(duì)象都有一個(gè)0-1范圍內(nèi)的間隔(
Interval); - 在間隔時(shí)間內(nèi),
Tween對(duì)象從起始值過(guò)渡到結(jié)束值。 - 由
AnimationController統(tǒng)一管理這些Tween 產(chǎn)生的Animation對(duì)象。
聽起來(lái)有點(diǎn)抽象,我們以一張圖來(lái)表述就清晰多了,假設(shè)我們有4個(gè)動(dòng)畫對(duì)象,分別控制組件的透明度(Opacity),寬度(Width),高度(Height)和顏色(Color),交錯(cuò)動(dòng)畫過(guò)程如下:

時(shí)序示意圖
controller 是一個(gè)從0到1的歸一化的動(dòng)畫控制,其實(shí)對(duì)應(yīng)的就是動(dòng)畫時(shí)長(zhǎng)的歸一化。然后 Opacity 透明度動(dòng)效占據(jù)了0-0.25區(qū)間;Width 占據(jù)了0.25-0.5區(qū)間;Height 占據(jù)了0.5-0.75區(qū)間;最后是 Color 占據(jù)了0.75-1.0的區(qū)間。區(qū)間對(duì)應(yīng)就是動(dòng)畫的時(shí)間間隔,只是每個(gè)區(qū)間內(nèi)的Tween 動(dòng)畫對(duì)象的取值范圍都是0-1以控制從起始值到結(jié)束值。我們可以理解為是 AnimationController 將多個(gè) Animation 對(duì)象按序(也可以重合)拼接起來(lái)形成復(fù)合形式的動(dòng)畫。
代碼實(shí)現(xiàn)
看上面的說(shuō)明是不是覺得還有些難以理解,我們來(lái)一段示例代碼就很容易明白了。下面的代碼我們定義了一個(gè)共用的_controller,然后四段動(dòng)畫對(duì)象_opaticy,_width,_height 和_color。其中關(guān)鍵的實(shí)現(xiàn)是使用了 Tween 對(duì)象的 animate 方法,并指定了一個(gè) CurvedAnimation 對(duì)象作為 其parent 參數(shù)。而這個(gè)CurvedAnimation實(shí)際使用 Interval 來(lái)切分_controller 的動(dòng)畫時(shí)間,從而可以將多個(gè) Animation 對(duì)象組合起來(lái)。
import?'package:flutter/material.dart';
class?StaggeredAnimationDemo?extends?StatefulWidget?{
??StaggeredAnimationDemo({Key??key})?:?super(key:?key);
??@override
??_StaggeredAnimationDemoState?createState()?=>?_StaggeredAnimationDemoState();
}
class?_StaggeredAnimationDemoState?extends?State<StaggeredAnimationDemo>
????with?SingleTickerProviderStateMixin?{
??late?AnimationController?_controller;
??late?Animation<double>?_opacity;
??late?Animation<double>?_width;
??late?Animation<double>?_height;
??late?Animation<Color?>?_color;
??@override
??void?initState()?{
????_controller?=
????????AnimationController(duration:?Duration(seconds:?2),?vsync:?this)
??????????..addListener(()?{
????????????setState(()?{});
??????????});
????_opacity?=?Tween<double>(begin:?0.5,?end:?1.0).animate(
??????CurvedAnimation(
????????parent:?_controller,
????????curve:?Interval(
??????????0.0,
??????????0.25,
??????????curve:?Curves.easeIn,
????????),
??????),
????);
????_width?=?Tween<double>(begin:?0.0,?end:?2.0).animate(
??????CurvedAnimation(
????????parent:?_controller,
????????curve:?Interval(
??????????0.25,
??????????0.5,
??????????curve:?Curves.easeIn,
????????),
??????),
????);
????_height?=?Tween<double>(begin:?0.0,?end:?2.0).animate(
??????CurvedAnimation(
????????parent:?_controller,
????????curve:?Interval(
??????????0.5,
??????????0.75,
??????????curve:?Curves.easeIn,
????????),
??????),
????);
????_color?=?ColorTween(begin:?Colors.green,?end:?Colors.blue).animate(
??????CurvedAnimation(
????????parent:?_controller,
????????curve:?Interval(
??????????0.75,
??????????1.0,
??????????curve:?Curves.easeIn,
????????),
??????),
????);
????super.initState();
??}
??@override
??Widget?build(BuildContext?context)?{
????return?Scaffold(
??????appBar:?AppBar(
????????title:?const?Text('交錯(cuò)動(dòng)畫'),
??????),
??????body:?Center(
????????child:?Opacity(
??????????opacity:?_opacity.value,
??????????child:?Container(
????????????width:?100?+?100?*?_width.value,
????????????height:?100?+?100?*?_height.value,
????????????color:?_color.value,
??????????),
????????),
??????),
??????floatingActionButton:?FloatingActionButton(
????????child:?Icon(Icons.play_arrow),
????????onPressed:?()?{
??????????if?(_controller.isCompleted)?{
??????????????_controller.reverse();
????????????}?else?if?(!_controller.isAnimating)?{
??????????????_controller.forward();
??????????}
????????},
??????),
????);
??}
}我們來(lái)看一下運(yùn)行效果,可以看到運(yùn)行的動(dòng)畫過(guò)程其實(shí)就是4段動(dòng)畫效果拼接來(lái)的,先是透明度改變,然后是寬度改變,再之后是高度改變,最后是顏色的改變。

運(yùn)行效果
Interval 介紹
我們來(lái)看一下關(guān)鍵的 Interval 類的介紹。
A curve that is 0.0 until [begin], then curved (according to [curve]) from 0.0 at [begin] to 1.0 at [end], then remains 1.0 past [end].
Interval 類繼承自 Curve,所不同的是,在 begin 之前曲線的值一直保持為0.0,而在 end 之后一直保持為1.0。所以可以理解為,在 AnimationController 啟動(dòng)動(dòng)畫后,Interval 曲線其實(shí)也已經(jīng)在繪制,只是有效的取值區(qū)間只在 begin 到 end 之間,下面就是 Interval 的一種示例曲線圖。

image.png
從 Interval 的源碼也能看出來(lái),其中 clamp 方法限制了取值范圍,當(dāng) t <= begin 的時(shí)候取值就是0,當(dāng) t >= end的時(shí)候,取值就是1.0。
@override
double?transformInternal(double?t)?{
??assert(begin?>=?0.0);
??assert(begin?<=?1.0);
??assert(end?>=?0.0);
??assert(end?<=?1.0);
??assert(end?>=?begin);
??t?=?((t?-?begin)?/?(end?-?begin)).clamp(0.0,?1.0);
??if?(t?==?0.0?||?t?==?1.0)
????return?t;
??return?curve.transform(t);
}
總結(jié)
本篇介紹了交錯(cuò)動(dòng)畫的實(shí)現(xiàn)機(jī)制和示例,通過(guò)交錯(cuò)動(dòng)畫給了我們更多動(dòng)效組合的空間,從而可以實(shí)現(xiàn)類似 GIF圖片的那種多幀組合在一起的動(dòng)畫效果。
到此這篇關(guān)于Android Flutter實(shí)現(xiàn)GIF動(dòng)畫效果的方法詳解的文章就介紹到這了,更多相關(guān)Android Flutter GIF動(dòng)畫效果內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android EditText限制輸入整數(shù)和小數(shù)的位數(shù)的方法示例
這篇文章主要介紹了Android EditText限制輸入整數(shù)和小數(shù)的位數(shù)的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
Android實(shí)現(xiàn)可拖拽帶有坐標(biāo)尺進(jìn)度條的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Android實(shí)現(xiàn)可拖拽帶有坐標(biāo)尺進(jìn)度條的效果,文中的示例代碼講解詳細(xì),需要的小伙伴可以參考一下2023-06-06
Anroid四大組件service之本地服務(wù)的示例代碼
本篇文章主要介紹了Anroid四大組件service之本地服務(wù)的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
Android開發(fā)之圖形圖像與動(dòng)畫(四)AnimationListener簡(jiǎn)介
就像Button控件有監(jiān)聽器一樣,動(dòng)畫效果也有監(jiān)聽器,只需要實(shí)現(xiàn)AnimationListener就可以實(shí)現(xiàn)對(duì)動(dòng)畫效果的監(jiān)聽,感興趣的朋友可以了解下啊,希望本文對(duì)你有所幫助2013-01-01
Android利用Intent實(shí)現(xiàn)記事本功能(NotePad)
這篇文章主要為大家詳細(xì)介紹了Android利用Intent實(shí)現(xiàn)簡(jiǎn)單記事本功能(NotePad)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06
Android開發(fā)技巧之ViewStub控件惰性裝載
布局文件中的控件并不一定在程序啟動(dòng)時(shí)全都用到,有一些控件只在特定的情況下才會(huì)被使用到;我們急需一種機(jī)制來(lái)改變<include>標(biāo)簽的這種行為,只在需要時(shí)裝載控件。這種機(jī)制就是本節(jié)要介紹的ViewStub控件2013-01-01
Android如何給Textview添加菜單項(xiàng)詳解(Java)
TextView是android里面用的最多的控件,TextView類似一般UI中的Label,TextBlock等控件,只是為了單純的顯示一行或多行文本,下面這篇文章主要給大家介紹了關(guān)于Android如何給Textview添加菜單項(xiàng)的相關(guān)資料,需要的朋友可以參考下2022-01-01

