flutter开发实战-flutter播放PAG动画
最近开发过程中,遇到了PAG动画,这里进行记录一下。
一、什么是PAG?
官网:https://pag.art/
Portable Animated Graphics 是一套完整的动效工作流解决方案。
目标是降低或消除动效相关的研发成本,能够一键将设计师在 AE(Adobe After Effects)中制作的动效内容导出成素材文件,并快速上线应用于几乎所有的主流平台。
类似SVGA,PAG通过二进制的方式效率更高。目前PAG sdk有iOS、Android、web端。在flutter端,有一个pag插件。pag插件在原生端使用的是iOS、Android端的PAG sdk,通过texture外接纹理的方式进行播放PAG动画。(很遗憾,目前flutter上PAG的1.0.4版本中源码没看到替换占位图、替换文本图层的代码。)
期待后续的版本
二、使用PAG插件
在工程中的pubspec.yaml文件中引入插件
# pag动画
pag: ^1.0.4
播放网络PAG动画
PAGView.network(
"https://svipwebwx-30096.sz.gfp.tencent-cloud.com/file1647585475981.pag",
repeatCount: PAGView.REPEAT_COUNT_LOOP,
initProgress: 0.25,
autoPlay: true,
key: networkPagKey,
),
可以通过监听onAnimationStart、onAnimationEnd、onAnimationRepeat、onAnimationCancel
通过使用key的state进行主动调用
final GlobalKey<PAGViewState> pagKey = GlobalKey<PAGViewState>();
//传入key值
PAGView.url(key:pagKey)
//播放
pagKey.currentState?.start();
//暂停
pagKey.currentState?.pause();
//停止
pagKey.currentState?.stop();
//设置进度
pagKey.currentState?.setProgress(xxx);
//获取坐标位置的图层名list
pagKey.currentState?.getLayersUnderPoint(x,y);
三、完整示例代码
完整示例代码如下
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pag/pag.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHome(),
);
}
}
class MyHome extends StatefulWidget {
@override
_MyHomeState createState() => _MyHomeState();
}
class _MyHomeState extends State<MyHome> {
GlobalKey<PAGViewState> _fansDanceKey = GlobalKey<PAGViewState>(debugLabel: _assetFans);
GlobalKey<PAGViewState> _assetDanceKey = GlobalKey<PAGViewState>(debugLabel: _assetFans);
GlobalKey<PAGViewState> get assetPagKey => _pagAsset == _assetFans ? _fansDanceKey : _assetDanceKey;
final GlobalKey<PAGViewState> networkPagKey = GlobalKey<PAGViewState>();
final GlobalKey<PAGViewState> bytesPagKey = GlobalKey<PAGViewState>();
Uint8List? bytesData;
// 本地加载资源
static const String _assetFans = 'data/fans.pag';
static const String _assetDance = 'data/dance.pag';
static const String _assetError = 'data/error.pag';
String _pagAsset = _assetFans;
void changeAsset() {
setState(() {
_pagAsset = _pagAsset == _assetFans ? _assetDance : _assetFans;
});
}
@override
void initState() {
rootBundle.load("data/fans.pag").then((data) {
setState(() {
bytesData = Uint8List.view(data.buffer);
});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('PAGView example app'),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
///TODO: PAGView加载本地资源
Padding(
padding: EdgeInsets.only(top: 20, left: 12, bottom: 20),
child: Text(
"PAGView加载本地资源:",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Colors.black54),
),
),
SizedBox(
width: 100,
height: 100,
child: PAGView.asset(
_pagAsset,
repeatCount: PAGView.REPEAT_COUNT_LOOP,
initProgress: 0.25,
autoPlay: true,
key: assetPagKey,
),
),
Padding(
padding: EdgeInsets.only(left: 12, top: 10),
child: Row(
children: [
IconButton(
iconSize: 30,
icon: const Icon(
Icons.pause_circle,
color: Colors.black54,
),
onPressed: () {
// 暂停
assetPagKey.currentState?.pause();
},
),
IconButton(
iconSize: 30,
icon: const Icon(
Icons.play_circle,
color: Colors.black54,
),
onPressed: () {
//播放
assetPagKey.currentState?.start();
},
),
IconButton(
iconSize: 30,
icon: const Icon(
Icons.published_with_changes_sharp,
color: Colors.black54,
),
onPressed: changeAsset),
Text(
"<= 请点击控制动画(可切换)",
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black54),
),
],
),
),
/// TODO: PAGView加载网络资源
Padding(
padding: EdgeInsets.only(top: 50, left: 12, bottom: 20),
child: Text(
"PAGView加载网络资源:",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Colors.black54),
),
),
PAGView.network(
"https://svipwebwx-30096.sz.gfp.tencent-cloud.com/file1647585475981.pag",
repeatCount: PAGView.REPEAT_COUNT_LOOP,
initProgress: 0.25,
autoPlay: true,
key: networkPagKey,
),
Padding(
padding: EdgeInsets.only(left: 12, top: 10),
child: Row(
children: [
IconButton(
iconSize: 30,
icon: const Icon(
Icons.pause_circle,
color: Colors.black54,
),
onPressed: () {
// 暂停
networkPagKey.currentState?.pause();
},
),
IconButton(
iconSize: 30,
icon: const Icon(
Icons.play_circle,
color: Colors.black54,
),
onPressed: () {
// 播放
networkPagKey.currentState?.start();
},
),
Text(
"<= 请点击控制动画",
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black54),
),
],
),
),
/// TODO: PAGView加载二进制资源
Padding(
padding: EdgeInsets.only(top: 50, left: 12, bottom: 20),
child: Text(
"PAGView加载二进制资源:",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Colors.black54),
),
),
Visibility(
visible: bytesData?.isNotEmpty == true,
child: PAGView.bytes(
bytesData,
repeatCount: PAGView.REPEAT_COUNT_LOOP,
initProgress: 0.25,
autoPlay: true,
key: bytesPagKey,
)),
Padding(
padding: EdgeInsets.only(left: 12, top: 10),
child: Row(
children: [
IconButton(
iconSize: 30,
icon: const Icon(
Icons.pause_circle,
color: Colors.black54,
),
onPressed: () {
// 暂停
bytesPagKey.currentState?.pause();
},
),
IconButton(
iconSize: 30,
icon: const Icon(
Icons.play_circle,
color: Colors.black54,
),
onPressed: () {
// 播放
bytesPagKey.currentState?.start();
},
),
Text(
"<= 请点击控制动画",
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black54),
),
],
),
),
/// TODO: PAGView加载二进制资源
Padding(
padding: EdgeInsets.only(top: 20, left: 12, bottom: 20),
child: Text(
"PAGView加载失败的默认占位图:",
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Colors.black54),
),
),
SizedBox(
width: 100,
height: 100,
child: PAGView.asset(
_assetError,
repeatCount: PAGView.REPEAT_COUNT_LOOP,
initProgress: 0.25,
autoPlay: true,
defaultBuilder: (context){
return Container(
color: Colors.grey,
alignment: Alignment.center,
margin: EdgeInsets.all(16),
child: Text("load fail"),
);
},
),
),
],
),
));
}
}
期待flutter上的PAG插件的更丰富的功能,暂时功能还不太全。
四、小结
flutter开发实战-flutter播放PAG动画
学习记录,每天不停进步。