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ì)象。
聽(tīng)起來(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ō)明是不是覺(jué)得還有些難以理解,我們來(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ù)的方法示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08Android實(shí)現(xiàn)可拖拽帶有坐標(biāo)尺進(jìn)度條的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Android實(shí)現(xiàn)可拖拽帶有坐標(biāo)尺進(jìn)度條的效果,文中的示例代碼講解詳細(xì),需要的小伙伴可以參考一下2023-06-06Anroid四大組件service之本地服務(wù)的示例代碼
本篇文章主要介紹了Anroid四大組件service之本地服務(wù)的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10Android開(kāi)發(fā)之圖形圖像與動(dòng)畫(四)AnimationListener簡(jiǎn)介
就像Button控件有監(jiān)聽(tīng)器一樣,動(dòng)畫效果也有監(jiān)聽(tīng)器,只需要實(shí)現(xiàn)AnimationListener就可以實(shí)現(xiàn)對(duì)動(dòng)畫效果的監(jiān)聽(tīng),感興趣的朋友可以了解下啊,希望本文對(duì)你有所幫助2013-01-01Android利用Intent實(shí)現(xiàn)記事本功能(NotePad)
這篇文章主要為大家詳細(xì)介紹了Android利用Intent實(shí)現(xiàn)簡(jiǎn)單記事本功能(NotePad)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06Android開(kāi)發(fā)技巧之ViewStub控件惰性裝載
布局文件中的控件并不一定在程序啟動(dòng)時(shí)全都用到,有一些控件只在特定的情況下才會(huì)被使用到;我們急需一種機(jī)制來(lái)改變<include>標(biāo)簽的這種行為,只在需要時(shí)裝載控件。這種機(jī)制就是本節(jié)要介紹的ViewStub控件2013-01-01Android如何給Textview添加菜單項(xiàng)詳解(Java)
TextView是android里面用的最多的控件,TextView類似一般UI中的Label,TextBlock等控件,只是為了單純的顯示一行或多行文本,下面這篇文章主要給大家介紹了關(guān)于Android如何給Textview添加菜單項(xiàng)的相關(guān)資料,需要的朋友可以參考下2022-01-01