小菜之前嘗試了 Flare 和 Lottie 動畫,實現效果都很高效;今天小菜嘗試另一種思路 SVGA 動畫;SVGA 是一種同時兼容 iOS / Android / Flutter / Web 多個平臺的動畫格式;
SVGA
基本簡介
小菜首先讚美一下 SVGA 官網,非常簡潔而且主要信息都容易查到,同時看着非常舒服;設計師通過 AE 等工具設計生成好 SVGA 動畫後,可直接交付給開發同學通過 SVGAPlayer 直接調用即可,集成和應用都很簡單;
SVGA 提供了在線動畫素材預覽以及素材元素拆分,還可以將 SVGA 動畫轉化爲 SVG 矢量圖元素,非常靈活方便;
案例嘗試
SVGA 提供了多種方式完整的集成方案,小菜簡單嘗試一下 Flutter 版本應用;
1. 集成 svgaplayer_flutter
與所有插件使用相同,小菜引入對應版本的 svgaplayer_flutter;目前 svgaplayer_flutter 已支持 Flutter 2.0 空安全;
svgaplayer_flutter: ^2.1.2
2. 應用播放 SVGA
2.1 SVGASimpleImage 加載動畫
svgaplayer_flutter 支持播放本地動畫和網絡線上動畫,與 Image 加載本地和網絡圖片類似;SVGA 提供了封裝好 SVGAAnimationController 控制器的 SVGASimpleImage;根據文件類型,通過不同參數進行展示,默認動畫效果爲重複播放;
class SVGASimpleImage extends StatefulWidget {
final String resUrl;
final String assetsName;
final File file;
SVGASimpleImage({Key key, this.resUrl, this.assetsName, this.file}) : super(key: key);
@override
State<StatefulWidget> createState() => _SVGASimpleImageState();
}
簡單分析源碼可得,SVGASimpleImage 根據傳遞的不同動畫路徑進行不同方式的展示,通過 SVGAParser.shared 加載和解碼不同類型的網絡資源、本地資源以及 File 資源等;
class _SVGAPageState extends State<SVGAPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('SVGA Page')),
body: Column(children: [
_itemSVGA01(false, 'images/posche.svga'),
_itemSVGA01(true, 'https://github.com/yyued/SVGA-Samples/blob/master/angel.svga?raw=true')
]));
}
_itemSVGA01(isUrl, svgaUrl) {
return Expanded( flex: 1,
child: SVGASimpleImage(assetsName: isUrl ? null : svgaUrl, resUrl: isUrl ? svgaUrl : null));
}
}
2.2 SVGAImage & SVGAAnimationController
SVGASimpleImage 是將 SVGAImage 和 SVGAAnimationController 封裝好的動畫播放器,若我們想自由控制動畫的播放、暫停、重播等操作的話,需要配合 SVGAAnimationController 控制器調節動畫的播放過程;
SVGAImage(
this._controller, {
this.fit = BoxFit.contain,
this.clearsAfterStop = true,
})
簡單分析源碼可得,SVGAImage 主要是通過 SVGAAnimationController 來進行動畫播放;與圖片類似,可以通過 BoxFit 設置動畫的佈局樣式;
SVGAAnimationController 是對 AnimationController 進一層封裝與應用,調用的方法和狀態回調基本是一致的;
enum AnimationStatus {
/// 動畫開始時結束
dismissed,
/// 動畫開始
forward,
/// 逆向動畫
reverse,
/// 動畫完成結束
completed,
}
this.animationController ?.addStatusListener((status) => print('---status---$status'));
SVGAAnimationController 提供了常用的播放方法,小菜簡單嘗試幾種常用的;
- reset 動畫重置;
- forward 動畫播放,小菜建議若動畫從頭開始播放先調用 reset 使動畫重置,防止其他操作影響動畫起始位置;
- stop 動畫停止,與 Lottie 動畫不同,SVGAAnimationController 沒有提供對應的暫停方法,小菜將 stop 理解爲暫停和停止,若繼續播放則調用 forward 即可;
- reverse 動畫反轉,即反向播放動畫;
- repeat 動畫重複;
- fling 使用臨界阻尼彈簧和初始速度驅動動畫;小菜簡單理解在正向播放時,fling 會按起始速度播放完成;
@override
void initState() {
super.initState();
this.animationController = SVGAAnimationController(vsync: this)
..addListener(() {
if (mounted) { setState(() {}); }
});
this._loadAnimation();
}
@override
void dispose() {
this.animationController?.clear();
this.animationController?.dispose();
this.animationController = null;
super.dispose();
}
void _loadAnimation() async {
final videoItem = await _loadSVGA(false, 'images/posche.svga');
if (mounted)
setState(() {
this.isLoading = false;
this.animationController?.videoItem = videoItem;
this.animationController ?.addStatusListener((status) => print('---status---$status'));
});
}
Widget _itemBtn(str) => Expanded(
child: Container(
margin: EdgeInsets.all(1.0),
child: FlatButton(
color: Colors.lightBlueAccent,
child: Text(str),
onPressed: () {
if (str == 'start') {
animationController?.reset();
animationController?.forward();
} else if (str == 'reverse') {
animationController?.reverse();
} else if (str == 'repeat') {
animationController?.repeat();
} else if (str == 'resume') {
animationController?.forward();
} else if (str == 'stop') {
animationController?.stop();
} else if (str == 'fling') {
animationController?.fling();
}
setState(() {});
})));
SVAG & Lottie
小菜查閱了一些資料,簡單瞭解了 SVGA 與 Lottie 動畫實現方式的差異;SVGA 是將 SVGA 矢量圖逐幀繪製,通過設置幀率,來生成一個配置文件,使得每一幀都有一個配置,每一幀都是關鍵幀,通過幀率去刷每一幀的畫面,這個思路跟 GIF 很像,但是通過配置使得動畫過程中圖片都可以得到複用;
而 Lottie 動畫是逐層繪製,將所有的動畫拆成多個層級,每個層級 layer 都有一個動畫配置,播放時解析多 0 個 layer 的配置,並給每個 layer 做相應的動畫;
兩種動畫模式都是很成熟且應用範圍很廣的動畫,小菜因未找到完全相同的動畫元素,未能進行準確的數據分析,但查閱資料兩者性能基本持平,具體選用哪種根據實際情況而定;
小菜對 SVGA 的研究還很淺顯,有很多方法未研究到;如有錯誤,請多多指導!
來源: 阿策小和尚