Lottie
是Airbnb
开源的一个支持Android
、iOS
以及ReactNative
, 利用json
文件的方式快速实现动画效果的库。
Android
端库地址:https://github.com/airbnb/lottie-android
iOS
端库地址:https://github.com/airbnb/lottie-ios
ReactNative
端库地址:https://github.com/airbnb/lottie-react-native
1、动效设计人员在Adobe After Effects中设计动画;
2、动效设计人员通过Adobe After Effects的Bodymoving插件导出一份记录动画信息的JSON
文件;
3、开发人员使用Lottie
的开源库读取这份JSON
文件进行解析和渲染。
制作好的JSON
文件可以进行预览:https://svgsprite.com/demo/bm/player.php?render=canvas&bg=fff
如果Composition
中没有masks
或mattes
,那么性能和内存开销应该相当不大。 没有创建位图,大多数操作都是简单的画布操作。
如果使用了图片,那么性能和内存占用会很大,因为图片非常占用内存,这也是Lottie
不建议的。
在直播的礼物实现方案中,Lottie
算是一个终极解决方案。虽然他也有一些限制,但是目前看是最好的解决方案。
Lottie
的坏处是它是开源的,一旦作者不维护这个库了的话,可能会给使用者造成一些影响, 使用他的前提是你能够很好的理解它的原理,并能根据自己的项目需要做二次开发。
动效设计师在制作过程中要注意几点:
1、安装Adobe After Effects2015 CC
版本,不要安装最新版本。 使用最新版本做出来的动画总是无法播放,原因未知。
2、不是所有的Adobe After Effects功能都支持的,Lottie
在同类的库中对Adobe After Effects的功能支持是做多的, 在开始制作前,一定要明白哪些功能是不支持的。可以看https://github.com/airbnb/lottie-android,有详细说明。
3、在使用Adobe After Effects的Bodymoving插件导出JSON
文件的时候, 不要对Bodymoving插件做任何设置,使用默认设置即可。
Bodymovin
主界面如下:
4、在使用Adobe After Effects的Bodymoving插件导出JSON
文件的时候,JSON
文件的名字必须是data.json
,并将所有的礼物都放在一个大文件夹(gifts
), 每个礼物是单独的文件夹,文件夹的名字就是礼物的名字,不要出现中文,全部使用英文,导出多个礼物后的文件夹结构,如下示例:
/Users/liufupin/gifts/
├── cake <============礼物的名称,名字必须唯一
│ └── data.json
├── car <============礼物的名称,名字必须唯一
│ ├── data.json
│ └── images
│ ├── 1.png
│ ├── 2.png
│ └── 3.png
└── xx <============礼物的名称,名字必须唯一
├── data.json
└── images
├── 1.png
├── 2.png
├── 3.png
├── 4.png
├── .....
└── y.png
由于iOS
端的库存在设计上的缺陷,所有图片的名称必须不一样,所以,我们需要对所有礼物的图片做一个重命名, 并将JSON
文件中的图片名称改成我们修改后的名称,这个工作我们让计算机来做。 另外,我们需要将每个礼物使用zip
打包,这个工作我们也让计算机来做。
如果您使用的是macOS或者GNU/Linux操作系统, 我们为您准备了一个Shell脚本,如下:
#!/bin/sh
osType=`uname -s`
echo "osType=$osType"
BUILD_DIR=$PWD/build
SOURCE_DIR=$BUILD_DIR/source
ZIP_DIR=$BUILD_DIR/zip
function main() {
#如果存在build目录,就删除掉
if [ -d $BUILD_DIR ] ; then
rm -rf $BUILD_DIR
fi
#获得所有的礼物文件夹
giftDirs=`ls -F | grep "/$"`
#重新创建build目录及其子目录
mkdir -p $SOURCE_DIR
mkdir -p $ZIP_DIR
#复制所有的礼物文件夹到build/source目录
for giftDir in $giftDirs
do
giftDir=`basename $giftDir`
rm ${giftDir}/.DS_Store
cp -r $giftDir $SOURCE_DIR/
done
cd $SOURCE_DIR
for dir in $giftDirs
do
dir=`basename ${dir}`
for imageFile in `ls ${dir}/images`
do
#修改成新的名字
mv ${dir}/images/${imageFile} ${dir}/images/${dir}_${imageFile}
#将data.json中图片名称替换成新的
if [ $osType = 'Darwin' ] ; then
sed -i "" "s#${imageFile}#${dir}_${imageFile}#g" ${dir}/data.json
else
sed -i "s#${imageFile}#${dir}_${imageFile}#g" ${dir}/data.json
fi
done
#打zip包
zip -r $ZIP_DIR/${dir}.zip $dir
done
echo "Success Done"
}
main
下载这个脚本:
curl -LO https://raw.githubusercontent.com/leleliu008/auto/master/gifts/package-gift.sh
修改他具有可执行权限:
chmod a+x package-gift.sh
将这个脚本放到gifts
目录下:
mv package-gift.sh ~/gifts/
执行命令:
./package-gift.sh
打包完成后的目录结构如下:
/Users/liufupin/gifts/
├── cake
│ └── data.json
├── car
│ ├── data.json
│ └── images
│ ├── 1.png
│ ├── 2.png
│ └── 3.png
├── xx
│ ├── data.json
│ └── images
│ ├── 1.png
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ ├── .....
│ └── y.png
├── build
│ ├── source
│ │ ├── cake
│ │ │ └── data.json
│ │ └── car
│ │ │ ├── data.json
│ │ │ └── images
│ │ │ ├── car_1.png
│ │ │ ├── car_2.png
│ │ │ └── car_3.png
│ │ └── xx
│ │ ├── data.json
│ │ └── images
│ │ ├── xx_1.png
│ │ ├── xx_2.png
│ │ ├── xx_3.png
│ │ ├── xx_3.png
│ │ ├── xx_....png
│ │ └── xx_y.png
│ └── zip <============生成的zip包都在这里(build/zip/)
│ ├── cake.zip
│ ├── car.zip
│ └── xx.zip
└── package-gift.sh
我们不提倡使用Windows做开发和设计工作,所以,没有提供Windows上的批处理脚本。
当然,这个Shell脚本在Windows上也是可以执行的, 只是您需要安装git-for-windows,通过Git bash
运行即可。
在Android
中,任何View
都有如下两对方法:
public void setTag(Object tag)
public Object getTag()
public void setTag(int key, Object tag)
public Object getTag(int key)
LottieAnimationView
是ImageView
的子类,自然也有这两对方法,我们灵活使用这两对方法, 可以为LottieAnimationView
根据情况绑定数据。
LottieAnimationView
提供了如下方法:
public void setImageAssetDelegate(ImageAssetDelegate assetDelegate)
ImageAssetDelegate
接口的定义如下:
public interface ImageAssetDelegate {
Bitmap fetchBitmap(LottieImageAsset asset);
}
LottieImageAsset
实体类的定义如下:
public class LottieImageAsset {
private final int width;
private final int height;
private final String id;
private final String fileName;
private LottieImageAsset(int width, int height, String id, String fileName) {
this.width = width;
this.height = height;
this.id = id;
this.fileName = fileName;
}
static class Factory {
private Factory() {
}
static LottieImageAsset newInstance(JSONObject imageJson) {
return new LottieImageAsset(imageJson.optInt("w"), imageJson.optInt("h"), imageJson.optString("id"), imageJson.optString("p"));
}
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public String getId() {
return id;
}
public String getFileName() {
return fileName;
}
}
我们可以把所有图片放在同一个目录下,为了避免相互覆盖,需要人为的把图片名称设置为互不相同,但是,这么做可能会有一些麻烦,当然我们可以使用脚本帮我们做这个事情。
下面是一个示例:
lottieAnimationView.setImageAssetDelegate(asset -> {
Context context = lottieAnimationView.getContext();
String giftId = (String) lottieAnimationView.getTag();
String imageFilePath = "/sdcard/" + context.getPackageName() + "/gifts/" + giftId + "/images/" + asset.getFileName();
return BitmapFactory.decodeFile(imageFilePath);
});
/**
* 展示礼物
*
* @param lottieAnimationView 展示区
* @param giftBean 礼物
*/
public void showGift(final LottieAnimationView lottieAnimationView, final GiftBean giftBean) {
Context context = lottieAnimationView.getContext();
String giftId = giftBean.getId();
File jsonFile = new File("/sdcard/" + context.getPackageName() + "/gifts/" + giftId + "/data.json");
try {
final InputStream inputStream = new FileInputStream(jsonFile);
//从文件流中加载 json 数据
LottieComposition.Factory.fromInputStream(context, inputStream, composition -> {
//加载完文件后,将文件流关闭
try {
inputStream.close();
} catch (IOException e) {
Logger.e(TAG, "fromInputStream()", e);
}
//让展示区可见
lottieAnimationView.setVisibility(View.VISIBLE);
//给展示区绑定数据,这里是绑定礼物的ID
lottieAnimationView.setTag(giftId);
//给展示区设置Composition(AE中的术语,翻译成中文叫:合成)
lottieAnimationView.setComposition(composition);
//开始播放动画
lottieAnimationView.playAnimation();
//监听动画播放过程的事件
lottieAnimationView.addAnimatorListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
//播放完后,隐藏掉展示区
lottieAnimationView.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
});
} catch (FileNotFoundException e) {
Logger.e(TAG, "showGift()", e);
}
}
说明:将JSON
文件以文件流的方式读出来,转换成LottieComposition
对象,将这个对象设置给LottieAnimationView
对象,同时给LottieAnimationView
对象绑定一些特定的数据,最后开始进行播放, 并设置动画事件监听器。
Lottie
的iOS
库在设计上存在很大的缺陷,对于图片的处理非常不灵活,这是由于是个库的设计者不建议使用图片, 因为使用图片会导致内存占用和CPU性能大幅消耗,但是如果不使用图片,对动效设计师的要求就太高,针对我们国内设计师来说,不使用图片的话,效率会比较低。 所以,我们不得不使用图片。
Lottie
的iOS
库虽然支持图片,但是支持的很勉强,而且要求所有的图片的名称必须不一样,如果图片一样,后面的动画将无法播放。
Lottie
的iOS
库相比Lottie
的Android
库比较落后一些,如果有能力去修正他的一些问题或者bug的话, 建议去积极参与修改。