一、帧动画
1.在drawable下创建donghua.xml
2.在布局中调用
3.java中启动动画
AnimationDrawable animationDrawable;
animationDrawable = (AnimationDrawable)imageView.getDrawable();
animationDrawable.start();
二、属性动画ObjectAnimator
(https://www.cnblogs.com/yongdaimi/p/7943226.html)
1.传统的视图动画相对于属性动画的局限性
1)它仅仅是重绘了控件,改变了其显示的位置。但真正事件响应的位置,却并没有发生改变。
因此传统动画不适合做具有交互的动画效果。仅仅能做一些显示的动画效果。
2)传统动画是不断通过 onDraw() 方法重绘界面,必然会十分耗费GPU资源。
3)传统动画所支持的动画类型少,仅有旋转、缩放、位移、透明度这四种动画效果。虽然通过组合可以实现丰富的效果,
但相比直接通过改变属性来实现的属性动画来说,还是有很大的局限性的。
2.传统的视图动画实现平移效果
TranslateAnimation animation = new TranslateAnimation(0,200,0,0); // 平移动画x轴移动200,y轴不动
animation.setDuration(1000); // 动画时长
animation.setFillAfter(true); // 使动画结束后停留在结束的位置
mIvPicture.startAnimation(animation);
3.ObjectAnimator原理与使用全解析
1)实现平移(和上面视图动画的平移效果)
ObjectAnimator.ofFloat(mIvPicture,"translationX",0F,200F)
.setDuration(1000)
.start();
第一个参数是动画需要操纵的目标,在这里是我们的 ImageView。
第二个参数是所需要操纵的目标所具备的属性名称。
第三个参数是可变长参数,这个就跟ValueAnimator中的可变长参数的意义一样了,就是指这个属性值是从哪变到哪。
2)旋转
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,180,0)
3)setter函数
我们再回来看构造改变rotation值的ObjectAnimator的方法
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,180,0)
TextView控件有rotation这个属性吗?没有,不光TextView没有,连它的父类View中也没有这个属性。那它是怎么来改变这个值的呢?其实,ObjectAnimator做动画,并不是根据控件xml中的属性来改变的,而是通过指定属性所对应的set方法来改变的。比如,我们上面指定的改变rotation的属性值,ObjectAnimator在做动画时就会到指定控件(TextView)中去找对应的setRotation()方法来改变控件中对应的值。同样的道理,当我们在最开始的示例代码中,指定改变”alpha”属性值的时候,ObjectAnimator也会到TextView中去找对应的setAlpha()方法。那TextView中都有这些方法吗,有的,这些方法都是从View中继承过来的,在View中有关动画,总共有下面几组set方法:
//1、透明度:alpha (view.setAlpha,把第一个字母改成小写,去掉set,即alpha)
public void setAlpha(float alpha)
//2、旋转度数:rotation、rotationX、rotationY
public void setRotation(float rotation)
public void setRotationX(float rotationX)
public void setRotationY(float rotationY)
//3、平移:translationX、translationY
public void setTranslationX(float translationX)
public void setTranslationY(float translationY)
//4、缩放:scaleX、scaleY
public void setScaleX(float scaleX)
public void setScaleY(float scaleY)
可以看到在View中已经实现了有关alpha,rotaion,translate,scale相关的set方法。所以我们在构造ObjectAnimator时可以直接使用。
在开始逐个看这些函数的使用方法前,我们先做一个总结:
1、要使用ObjectAnimator来构造对画,要操作的控件中,必须存在对应的属性的set方法
2、setter 方法的命名必须以骆驼拼写法命名,即set后每个单词首字母大写,其余字母小写,即类似于setPropertyName所对应的属性为propertyName .
下面我们就来看一下上面中各个方法的使用方法及作用。
有关alpha的用法,上面已经讲过了,下面我们来看看其它的
(1)、setRotationX、setRotationY与setRotation
setRotationX(float rotationX):表示围绕X轴旋转,rotationX表示旋转度数
setRotationY(rotationY):表示围绕Y轴旋转,rotationY表示旋转度数
setRotation(float rotation):表示围绕Z旋转,rotation表示旋转度数
(2)、setTranslationX与setTranslationY
setTranslationX(float translationX) :表示在X轴上的平移距离,以当前控件为原点,向右为正方向,参数translationX表示移动的距离。
setTranslationY(float translationY) :表示在Y轴上的平移距离,以当前控件为原点,向下为正方向,参数translationY表示移动的距离。
(3)、setScaleX与setScaleY
setScaleX(float scaleX):在X轴上缩放,scaleX表示缩放倍数
setScaleY(float scaleY):在Y轴上缩放,scaleY表示缩放倍数
可以看到ObjectAnimator的动画流程中,也是首先通过加速器产生当前进度的百分比,然后再经过Evaluator生成对应百分比所对应的数字值。
这两步与ValueAnimator是完全一样的,唯一不同的是最后一步,在ValueAnimator中,我们要通过添加监听器来监听当前数字值。而在ObjectAnimator中,则是先根据属性值拼装成对应的set函数的名字,比如这里的scaleY的拼装方法就是将属性的第一个字母强制大写后,与set拼接,所以就是setScaleY。
然后通过反射找到对应控件的setScaleY(float scaleY)函数,将当前数字值做为setScaleY(float scale)的参数将其传入。
这就是ObjectAnimator的流程,最后一步总结起来就是调用对应属性的set方法,将动画当前数字值做为参数传进去。
讲了这么多,就是为了强调一点:ObjectAnimator只负责把当前运动动画的数值传给set函数。至于set函数里面怎么来做,是我们自己的事了。
4、自定义ObjectAnimator属性
上面我们已经看了使用View自带的set函数所对应属性的方法,而且理解了ObjectAnimator的动画实现原理,下面我们来自定义一个属性来看看实现效果吧。
我们在开始之前再来捋一下ObjectAnimator的动画设置流程:ObjectAnimator需要指定操作的控件对象,在开始动画时,到控件类中去寻找设置属性所对应的set函数,然后把动画中间值做为参数传给这个set函数并执行它。
所以,我们说了,控件类中必须所要设置属性所要对应的set函数。所以为了自由控制控件的实现,我们这里自定义一个控件。大家知道在这个自定义控件中,肯定存在一个set函数与我们自定义的属性相对应。
实现效果:控件中存在一个圆形,也是在动画时先将这个圆形放大,然后再将圆形还原。
1)保存圆形信息类——Point
为了,保存圆形的信息,我们先定义一个类:(Point.java)
public class Point {
private int mRadius;
public Point(int radius){
mRadius = radius;
}
public int getRadius() {
return mRadius;
}
public void setRadius(int radius) {
mRadius = radius;
}
}
2)自定义控件——MyPointView
然后我们自定义一个控件MyPointView,完整代码如下
public class MyPointView extends View {
private Point mPoint = new Point(100);
public MyPointView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
if (mPoint != null){
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(300,300,mPoint.getRadius(),paint);
}
super.onDraw(canvas);
}
void setPointRadius(int radius){
mPoint.setRadius(radius);
invalidate();
}
}
在这段代码中,首先来看我们前面讲到的set函数:
void setPointRadius(int radius){
mPoint.setRadius(radius);
invalidate();
}
第一点,这个set函数所对应的属性应该是pointRadius或者PointRadius。前面我们已经讲了第一个字母大小写无所谓,后面的字母必须保持与set函数完全一致。
第二点,在setPointRadius中,先将当前动画传过来的值保存到mPoint中,做为当前圆形的半径。然后强制界面刷新
在界面刷新后,就开始执行onDraw()函数:
3)MainActivity中调用
private void doPointViewAnimation(){
ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius", 0, 300, 100);
animator.setDuration(2000);
animator.start();
}
pointRadius--这个属性就是在自定义View中设置的setPointRadius方法
在这段代码中,着重看ObjectAnimator的构造方法,首先要操作的控件对象是mPointView,然后对应的属性是pointRadius,然后值是从0到300再到100;
所以在动画开始以后,ObjectAnimator就会实时地把动画中产生的值做为参数传给MyPointView类中的setPointRadius(int radius)函数,然后调用
setRadius(radius);由于我们在setPointRadius(int radius)中实时地设置圆形的半径值然后强制重绘当前界面,所以可以看到圆形的半径会随着动画的进行而改变。
5、常用函数
/**
* 设置动画时长,单位是毫秒
*/
ValueAnimator setDuration(long duration)
/**
* 获取ValueAnimator在运动时,当前运动点的值
*/
Object getAnimatedValue();
/**
* 开始动画
*/
void start()
/**
* 设置循环次数,设置为INFINITE表示无限循环
*/
void setRepeatCount(int value)
/**
* 设置循环模式
* value取值有RESTART,REVERSE,
*/
void setRepeatMode(int value)
/**
* 取消动画
*/
void cancel()
6、监听器相关
/**
* 监听器一:监听动画变化时的实时值
*/
public static interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator animation);
}
//添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)
/**
* 监听器二:监听动画变化时四个状态
*/
public static interface AnimatorListener {
void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
//添加方法为:public void addListener(AnimatorListener listener)
7、插值器--Android 为我们内置了插值器,使我们的动画更为自然。比如可以让我们的平移动画像物体的重力加速度由快到慢的 Accelerate
Android中内置了七种插值器,分别是
Accelerate
Decelerate
Accelerate/Decelerate
Anticipate
Overshoot
Anticipate/Overshoot
Bounce
要应用插值器,可以调用 ObjectAnimator 的 setInterpolator 方法, new 出对应的插值器作为参数(xxxInterpolator)。比如下面这段代码:
animator.setInterpolator(new AccelerateInterpolator());
8、多种属性动画同时作用
1)通过PropertyValuesHolder
我们可以使用 PropertyValuesHolder 来实现。其构造函数仅仅比 ObjectAnimator 少了一个作用对象参数。之后通过ObjectAnimator 的 ofPropertyValuesHolder 方法,传入作用对象以及要同时作用的 PropertyValuesHolder 即可执行。
可以看到下面的代码示例:
PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("translationX",0F,200F);
PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("rotationX",0F,360F);
PropertyValuesHolder p3 = PropertyValuesHolder.ofFloat("translationY",0F,200F);
ObjectAnimator.ofPropertyValuesHolder(mIvPicture,p1,p2,p3).setDuration(1000).start();
2)通过AnimatorSet 属性集合
我们其实还可以通过 AnimatorSet,来实现同样的效果。这里我们调用了 set 的 playTogether 方法,使得这些方法同时执行:
AnimatorSet set = new AnimatorSet();
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mIvPicture, "translationX", 0F, 200F);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mIvPicture, "rotationX", 0F, 360F);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mIvPicture, "translationY", 0F, 200F);
set.playTogether(animator1,animator2,animator3);
set.setDuration(1000);
set.start();
除了 playTogether 方法外,AnimatorSet 还提供了 playSequentially 方法,它可以使得动画按顺序执行。
具体顺序取决于调用时的参数顺序。
AnimatorSet set = new AnimatorSet();
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mIvPicture, "translationX", 0F, 200F);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mIvPicture, "rotationX", 0F, 360F);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mIvPicture, "translationY", 0F, 200F);
set.playSequentially(animator1,animator2,animator3);
set.setDuration(1000);
set.start();
我们除了可以用上述方法来让动画按顺序执行外,也可以通过 AnimatorSet 的 play、with、after、before 等方法相组合来控制动画播放关系。
例如如下的代码就可以实现先平移,再旋转的效果
set.play(animator1).with(animator3);
set.play(animator2).after(animator1);
9、动画监听事件
1)AnimatorListener
通过下面的代码,我们可以实现按钮按下后渐隐的效果。
mBtnPress.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator animator = ObjectAnimator.ofFloat(mBtnPress,"alpha",1F,0F);
animator.setDuration(1000);
animator.start();
}
});
但如果我们想要在动画播放完成后再执行一些操作的话,又该如何实现呢?
我们可以使用 ObjectAnimator 的 addListener方法,传入一个AnimatorListener,为动画设置监听事件。
一个AnimatorListener,需要实现四个方法,分别是:
onAnimationStart
onAnimationEnd
onAnimationCancel
onAnimationRepeat
它们的回调时机我们根据字面意思便可以理解。大部分时候,我们需要实现的是onAnimationEnd方法。
animator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
Toast.makeText(MainActivity.this,"动画结束",Toast.LENGTH_SHORT).show();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
2)AnimatorListenerAdapter
如果每次监听都需要实现这么多方法,未免太麻烦了一点。因此 Android 为我们提供了另一种方法来添加动画的监听事件:在添加 AnimatorListener 的时候,传入 AnimatorListenerAdapter 即可。这样我们就只需要实现自己需要的方法即可。
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
Toast.makeText(MainActivity.this,"Animation End",Toast.LENGTH_SHORT).show();
}
});
三、属性动画ValueAnimator(https://www.jianshu.com/p/7c95342f4bc2)
1、简介
ValueAnimator 本身不作用于任何一个属性,也不提供任何一种动画。它就是一个数值发生器,可以产生想要的各种数值。Android 系统为它提供了很多计算数值的方法,如 int、float 等等。我们也可以自己实现计算数值的方法。其实,在属性动画中,如何产生每一步的动画效果,都是通过 ValueAnimator 计算出来的。比如我们要实现一个从 0-100 的位移动画。随着动画时间的持续,它产生的值也会从 0-100 递增。通过这个 ValueAnimator 产生的值,再进行属性的设置即可。
2、原理
通过不断控制值的变化再不断手动赋给对象的属性从而实现动画效果。
3、如何产生这些值
首先 ValueAnimator会根据会根据动画已进行的时间与它持续的总时间的比值,产生一个0-1的时间因子。有了这样的时间因子,经过相应的变换,就可以根据初始值和最终值来生成中间的相应值。同时,通过插值器的使用,我们还可以进一步控制每一个时间因子产生值的变化速率。如果我们使用的是线性插值器,那么它生成值的时候就会呈一个线性变化。如果我们使用一个加速度插值器,那么它生成值时便会呈一个二次曲线,增长率越来越快。
由于 ValueAnimator 不作用于任何一个属性,也不提供任何一种动画。因此并没有 ObjectAnimator 使用得广泛。
实际上,ObjectAnimator 就是基于 ValueAnimator 进行的一次封装。我们可以查看 ObjectAnimator 的源码,会发现它继承自 ValueAnimator,是它的一个子类。正是 ValueAnimator 产生的变化值,才使得 ObjectAnimator 可以将它应用于各个属性。
4.使用方法
我们可以通过 ValueAnimator 的 ofXXX 产生一个 XXX 类型的值(如ofInt),然后为 ValueAnimator 添加一个更新的回调事件。
在回调事件中,通过参数 animation 的 getAnimationValue() 方法,来获取对应的 value。有了这个值,我们就可以实现我们所有想要的动画效果。
比如此处就通过 ValueAnimator 实现了一个计时器的动画效果。
ValueAnimator animator = ValueAnimator.ofInt(0,100);
animator.setDuration(5000);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
mButton.setText(""+value);
}
});
animator.start();
5.自定义数值生成器
前面提到,ValueAnimator 可以创建自定义的数值生成器,做法就是调用 ValueAnimator 的 ofObject 方法,创建一个 TypeEvaluator 作为参数。之后我们可以通过重写 TypeEvaluator 的 evaluate 方法,来按照自己的规则返回具体的值。
ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator() {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
//计算
return null; //返回值
}
});
我们来看一下 evaluate 方法的几个参数
float fraction:前面提到的时间因子
Object startValue:起始值
Object endValue:结束值
其实,通过 TypeEvaluator,我们不光能产生普通的数据,还能结合泛型,我们还能定义更加复杂的数据:
我们可以在创建 TypeEvaluator 时指定具体类型,来达到更丰富的效果。比如这里就用到了一个名为 PointF 的数据类型:
ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator() {
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
//计算
return null; //返回值
}
});
6.估值器(TypeEvaluator)
作用:设置动画 如何从初始值 过渡到 结束值 的逻辑
插值器(Interpolator)决定 值 的变化模式(匀速、加速blabla)
估值器(TypeEvaluator)决定 值 的具体变化数值
从上面可知:
ValueAnimator.ofFloat()实现了 将初始值 以浮点型的形式 过渡到结束值 的逻辑,那么这个过渡逻辑具体是怎么样的呢?
其实是系统内置了一个 FloatEvaluator估值器,内部实现了初始值与结束值 以浮点型的过渡逻辑
ValueAnimator 类是先改变值,然后 手动赋值 给对象的属性从而实现动画;是 间接 对对象属性进行操作;
ValueAnimator 类本质上是一种 改变 值 的操作机制
而ObjectAnimator类是先改变值,然后 自动赋值 给对象的属性从而实现动画;是 直接 对对象属性进行操作;
可以理解为:ObjectAnimator更加智能、自动化程度更高
7.自定义估值器
// 实现TypeEvaluator接口
public class PointEvaluator implements TypeEvaluator {
// 复写evaluate()
// 在evaluate()里写入对象动画过渡的逻辑
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 将动画初始值startValue 和 动画结束值endValue 强制类型转换成Point对象
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
// 根据fraction来计算当前动画的x和y的值
// fraction:表示动画完成度(根据它来计算当前动画的值)
// 初始值 过渡 到结束值 的算法是:
// 1. 用结束值减去初始值,算出它们之间的差值
// 2. 用上述差值乘以fraction系数
// 3. 再加上初始值,就得到当前动画的值
float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
// 将计算后的坐标封装到一个新的Point对象中并返回
Point point = new Point(x, y);
return point;
}
}
8.使用
// 步骤1:创建初始动画时的对象点 & 结束动画时的对象点
Point startPoint = new Point(RADIUS, RADIUS);// 初始点为圆心(70,70)
Point endPoint = new Point(700, 1000);// 结束点为(700,1000)
// 步骤2:创建动画对象 & 设置初始值 和 结束值
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
// 参数说明
// 参数1:TypeEvaluator 类型参数 - 使用自定义的PointEvaluator(实现了TypeEvaluator接口)
// 参数2:初始动画的对象点
// 参数3:结束动画的对象点
四.Lottie动画
1.依赖
在项目的 build.gradle 文件添加依赖
dependencies { compile 'com.airbnb.android:lottie:2.1.0'}
2.动画文件
将我们所需要的动画文件loading.json保存在app/src/main/assets文件里。
3.使用
在布局文件中使用
在代码中使用
lottieFiggerGuide.setVisibility(View.VISIBLE);
lottieFiggerGuide.bringToFront();
lottieFiggerGuide.setImageAssetsFolder("make_money_guide");//加载需要用到的资源图片
lottieFiggerGuide.setAnimation("makeMoneyGuide.json");//加载json文件
lottieFiggerGuide.loop(true);
lottieFiggerGuide.playAnimation();
makeMoneyGuide.json中的内容如下 :
{"v":"5.5.9","fr":30,"ip":0,"op":55,"w":500,"h":500,"nm":"多赚赚引导","ddd":0,"assets":[{"id":"image_0","w":132,"h":135,"u":"images/","p":"img_0.png","e":0},{"id":"image_1","w":600,"h":600,"u":"images/","p":"img_1.png","e":0}],"layers":[{"ddd":0,"ind":1,"ty":2,"nm":"手小@3x.png","cl":"png","refId":"image_0","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0],"e":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":12,"s":[100],"e":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":16,"s":[100],"e":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":40,"s":[100],"e":[0]},{"t":48}],"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[-24.346],"e":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":12,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":16,"s":[0],"e":[-13.958]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":20,"s":[-13.958],"e":[0]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":24,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":26,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":30,"s":[0],"e":[-13.958]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":34,"s":[-13.958],"e":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":38,"s":[0],"e":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":40,"s":[0],"e":[14.963]},{"t":48}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[409.5,458.5,0],"e":[346.5,325.5,0],"to":[-10.5,-22.167,0],"ti":[10.5,22.167,0]},{"i":{"x":0.667,"y":0.667},"o":{"x":0.167,"y":0.167},"t":12,"s":[346.5,325.5,0],"e":[346.5,325.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.573,"y":1},"o":{"x":0.189,"y":0},"t":16,"s":[346.5,325.5,0],"e":[344.5,325.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.704,"y":1},"o":{"x":0.336,"y":0},"t":27,"s":[344.5,325.5,0],"e":[346.5,325.5,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0},"t":40,"s":[346.5,325.5,0],"e":[418.5,209.5,0],"to":[12,-19.333,0],"ti":[-12,19.333,0]},{"t":48}],"ix":2,"x":"var $bm_rt;\nvar amp, freq, decay, n, n, t, t, v;\namp = 0.1;\nfreq = 2;\ndecay = 10;\n$bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = t = 0;\n} else {\n $bm_rt = t = $bm_sub(time, key(n).time);\n}\nif (n > 0) {\n v = velocityAtTime($bm_sub(key(n).time, $bm_div(thisComp.frameDuration, 10)));\n $bm_rt = $bm_sum(value, $bm_div($bm_mul($bm_mul(v, amp), Math.sin($bm_mul($bm_mul($bm_mul(freq, t), 2), Math.PI))), Math.exp($bm_mul(decay, t))));\n} else {\n $bm_rt = value;\n}"},"a":{"a":0,"k":[81.889,116.182,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[50,50,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":12,"s":[100,100,100],"e":[100,100,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":16,"s":[100,100,100],"e":[100,100,100]},{"t":40}],"ix":6,"x":"var $bm_rt;\nvar amp, freq, decay, n, n, t, t, v;\namp = 0.1;\nfreq = 2;\ndecay = 8;\n$bm_rt = n = 0;\nif (numKeys > 0) {\n $bm_rt = n = nearestKey(time).index;\n if (key(n).time > time) {\n n--;\n }\n}\nif (n == 0) {\n $bm_rt = t = 0;\n} else {\n $bm_rt = t = $bm_sub(time, key(n).time);\n}\nif (n > 0) {\n v = velocityAtTime($bm_sub(key(n).time, $bm_div(thisComp.frameDuration, 10)));\n $bm_rt = $bm_sum(value, $bm_div($bm_mul($bm_mul(v, amp), Math.sin($bm_mul($bm_mul($bm_mul(freq, t), 2), Math.PI))), Math.exp($bm_mul(decay, t))));\n} else {\n $bm_rt = value;\n}"}},"ao":0,"ef":[{"ty":34,"nm":"操控","np":5,"mn":"ADBE FreePin3","ix":1,"en":1,"ef":[{"ty":7,"nm":"人偶引擎","mn":"ADBE FreePin3 Puppet Engine","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":7,"nm":"在透明背景上","mn":"ADBE FreePin3 On Transparent","ix":2,"v":{"a":0,"k":0,"ix":2}},{"ty":30,"nm":"arap","np":3,"mn":"ADBE FreePin3 ARAP Group","ix":3,"en":1,"ef":[{"ty":6,"nm":"自动追踪形状","mn":"ADBE FreePin3 Outlines","ix":1,"v":0},{"ty":1,"nm":"网格","np":2,"mn":"ADBE FreePin3 Mesh Group","ix":2,"en":1,"ef":[{"nm":"网格 1","np":7,"mn":"ADBE FreePin3 Mesh Atom","ix":1,"en":1,"ef":[{"ty":6,"nm":"网格","mn":"ADBE FreePin3 Mesh","ix":1,"v":0},{"ty":0,"nm":"三角形","mn":"ADBE FreePin3 Mesh Tri Count","ix":2,"v":{"a":0,"k":350,"ix":2}},{"ty":0,"nm":"扩展","mn":"ADBE FreePin3 Mesh Expansion","ix":3,"v":{"a":0,"k":3,"ix":3}},{"nm":"变形","np":8,"mn":"ADBE FreePin3 PosPins","ix":4,"en":1,"ef":[{"nm":"操控点 7","np":7,"mn":"ADBE FreePin3 PosPin Atom","ix":1,"en":1,"ef":[{"ty":3,"nm":"顶点位移","mn":"ADBE FreePin3 PosPin Vtx Offset","ix":1,"v":{"a":0,"k":[4.485,0.943],"ix":1}},{"ty":0,"nm":"顶点索引","mn":"ADBE FreePin3 PosPin Vtx Index","ix":2,"v":{"a":0,"k":62,"ix":2}},{"ty":7,"nm":"固定类型","mn":"ADBE FreePin3 PosPin Type","ix":3,"v":{"a":0,"k":1,"ix":3}},{"ty":3,"nm":"位置","mn":"ADBE FreePin3 PosPin Position","ix":4,"v":{"a":0,"k":[116.778,49.856],"ix":4}},{"ty":0,"nm":"缩放","mn":"ADBE FreePin3 PosPin Scale","ix":5,"v":{"a":0,"k":100,"ix":5}},{"ty":0,"nm":"旋转","mn":"ADBE FreePin3 PosPin Rotation","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"nm":"操控点 6","np":7,"mn":"ADBE FreePin3 PosPin Atom","ix":2,"en":1,"ef":[{"ty":3,"nm":"顶点位移","mn":"ADBE FreePin3 PosPin Vtx Offset","ix":1,"v":{"a":0,"k":[1.625,-4.583],"ix":1}},{"ty":0,"nm":"顶点索引","mn":"ADBE FreePin3 PosPin Vtx Index","ix":2,"v":{"a":0,"k":58,"ix":2}},{"ty":7,"nm":"固定类型","mn":"ADBE FreePin3 PosPin Type","ix":3,"v":{"a":0,"k":1,"ix":3}},{"ty":3,"nm":"位置","mn":"ADBE FreePin3 PosPin Position","ix":4,"v":{"a":0,"k":[103.312,29.633],"ix":4}},{"ty":0,"nm":"缩放","mn":"ADBE FreePin3 PosPin Scale","ix":5,"v":{"a":0,"k":100,"ix":5}},{"ty":0,"nm":"旋转","mn":"ADBE FreePin3 PosPin Rotation","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"nm":"操控点 5","np":7,"mn":"ADBE FreePin3 PosPin Atom","ix":3,"en":1,"ef":[{"ty":3,"nm":"顶点位移","mn":"ADBE FreePin3 PosPin Vtx Offset","ix":1,"v":{"a":0,"k":[-0.734,-5.997],"ix":1}},{"ty":0,"nm":"顶点索引","mn":"ADBE FreePin3 PosPin Vtx Index","ix":2,"v":{"a":0,"k":53,"ix":2}},{"ty":7,"nm":"固定类型","mn":"ADBE FreePin3 PosPin Type","ix":3,"v":{"a":0,"k":1,"ix":3}},{"ty":3,"nm":"位置","mn":"ADBE FreePin3 PosPin Position","ix":4,"v":{"a":0,"k":[76.429,24.61],"ix":4}},{"ty":0,"nm":"缩放","mn":"ADBE FreePin3 PosPin Scale","ix":5,"v":{"a":0,"k":100,"ix":5}},{"ty":0,"nm":"旋转","mn":"ADBE FreePin3 PosPin Rotation","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"nm":"操控点 4","np":7,"mn":"ADBE FreePin3 PosPin Atom","ix":4,"en":1,"ef":[{"ty":3,"nm":"顶点位移","mn":"ADBE FreePin3 PosPin Vtx Offset","ix":1,"v":{"a":0,"k":[2.282,-3.083],"ix":1}},{"ty":0,"nm":"顶点索引","mn":"ADBE FreePin3 PosPin Vtx Index","ix":2,"v":{"a":0,"k":50,"ix":2}},{"ty":7,"nm":"固定类型","mn":"ADBE FreePin3 PosPin Type","ix":3,"v":{"a":0,"k":1,"ix":3}},{"ty":3,"nm":"位置","mn":"ADBE FreePin3 PosPin Position","ix":4,"v":{"a":0,"k":[59.432,23.74],"ix":4}},{"ty":0,"nm":"缩放","mn":"ADBE FreePin3 PosPin Scale","ix":5,"v":{"a":0,"k":100,"ix":5}},{"ty":0,"nm":"旋转","mn":"ADBE FreePin3 PosPin Rotation","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"nm":"操控点 3","np":7,"mn":"ADBE FreePin3 PosPin Atom","ix":5,"en":1,"ef":[{"ty":3,"nm":"顶点位移","mn":"ADBE FreePin3 PosPin Vtx Offset","ix":1,"v":{"a":0,"k":[-0.484,-5.506],"ix":1}},{"ty":0,"nm":"顶点索引","mn":"ADBE FreePin3 PosPin Vtx Index","ix":2,"v":{"a":0,"k":47,"ix":2}},{"ty":7,"nm":"固定类型","mn":"ADBE FreePin3 PosPin Type","ix":3,"v":{"a":0,"k":1,"ix":3}},{"ty":3,"nm":"位置","mn":"ADBE FreePin3 PosPin Position","ix":4,"v":{"a":0,"k":[45.213,30.308],"ix":4}},{"ty":0,"nm":"缩放","mn":"ADBE FreePin3 PosPin Scale","ix":5,"v":{"a":0,"k":100,"ix":5}},{"ty":0,"nm":"旋转","mn":"ADBE FreePin3 PosPin Rotation","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"nm":"操控点 2","np":7,"mn":"ADBE FreePin3 PosPin Atom","ix":6,"en":1,"ef":[{"ty":3,"nm":"顶点位移","mn":"ADBE FreePin3 PosPin Vtx Offset","ix":1,"v":{"a":0,"k":[0.269,0.843],"ix":1}},{"ty":0,"nm":"顶点索引","mn":"ADBE FreePin3 PosPin Vtx Index","ix":2,"v":{"a":0,"k":94,"ix":2}},{"ty":7,"nm":"固定类型","mn":"ADBE FreePin3 PosPin Type","ix":3,"v":{"a":0,"k":1,"ix":3}},{"ty":3,"nm":"位置","mn":"ADBE FreePin3 PosPin Position","ix":4,"v":{"a":1,"k":[{"i":{"x":0,"y":0},"o":{"x":0,"y":0},"t":0,"s":[14.058,8.622],"e":[3.883,10.719],"to":[-1.696,0.35],"ti":[0,0]},{"i":{"x":0,"y":0},"o":{"x":0,"y":0},"t":0,"s":[3.883,10.719],"e":[14.058,8.622],"to":[0,0],"ti":[-1.696,0.35]},{"i":{"x":0.667,"y":0},"o":{"x":0.333,"y":0},"t":0,"s":[14.058,8.622],"e":[14.058,8.622],"to":[0,0],"ti":[0,0]},{"i":{"x":0,"y":0},"o":{"x":0,"y":0},"t":0,"s":[14.058,8.622],"e":[3.883,10.719],"to":[-1.696,0.35],"ti":[0,0]},{"i":{"x":0,"y":0},"o":{"x":0,"y":0},"t":0,"s":[3.883,10.719],"e":[14.058,8.622],"to":[0,0],"ti":[-1.696,0.35]},{"t":null}],"ix":4}},{"ty":0,"nm":"缩放","mn":"ADBE FreePin3 PosPin Scale","ix":5,"v":{"a":0,"k":100,"ix":5}},{"ty":0,"nm":"旋转","mn":"ADBE FreePin3 PosPin Rotation","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"nm":"操控点 1","np":7,"mn":"ADBE FreePin3 PosPin Atom","ix":7,"en":1,"ef":[{"ty":3,"nm":"顶点位移","mn":"ADBE FreePin3 PosPin Vtx Offset","ix":1,"v":{"a":0,"k":[-0.797,-3.373],"ix":1}},{"ty":0,"nm":"顶点索引","mn":"ADBE FreePin3 PosPin Vtx Index","ix":2,"v":{"a":0,"k":195,"ix":2}},{"ty":7,"nm":"固定类型","mn":"ADBE FreePin3 PosPin Type","ix":3,"v":{"a":0,"k":1,"ix":3}},{"ty":3,"nm":"位置","mn":"ADBE FreePin3 PosPin Position","ix":4,"v":{"a":0,"k":[51.308,80.306],"ix":4}},{"ty":0,"nm":"缩放","mn":"ADBE FreePin3 PosPin Scale","ix":5,"v":{"a":0,"k":100,"ix":5}},{"ty":0,"nm":"旋转","mn":"ADBE FreePin3 PosPin Rotation","ix":6,"v":{"a":0,"k":0,"ix":6}}]}]},{"nm":"重叠","np":1,"mn":"ADBE FreePin3 HghtPins","ix":5,"en":1,"ef":[]},{"nm":"硬度","np":1,"mn":"ADBE FreePin3 StarchPins","ix":6,"en":1,"ef":[]}]}]}]}]}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":2,"nm":"圆形波纹.png","cl":"png","refId":"image_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":34,"s":[100],"e":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":38,"s":[100],"e":[0]},{"t":42}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.5,245.25,0],"ix":2},"a":{"a":0,"k":[300,282.25,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":34,"s":[28.881,28.881,100],"e":[80,80,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":38,"s":[80,80,100],"e":[100,100,100]},{"t":42}],"ix":6}},"ao":0,"ef":[{"ty":21,"nm":"填充","np":9,"mn":"ADBE Fill","ix":1,"en":1,"ef":[{"ty":10,"nm":"填充蒙版","mn":"ADBE Fill-0001","ix":1,"v":{"a":0,"k":0,"ix":1}},{"ty":7,"nm":"所有蒙版","mn":"ADBE Fill-0007","ix":2,"v":{"a":0,"k":0,"ix":2}},{"ty":2,"nm":"颜色","mn":"ADBE Fill-0002","ix":3,"v":{"a":0,"k":[1,1,1,1],"ix":3}},{"ty":7,"nm":"反转","mn":"ADBE Fill-0006","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"水平羽化","mn":"ADBE Fill-0003","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":0,"nm":"垂直羽化","mn":"ADBE Fill-0004","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"不透明度","mn":"ADBE Fill-0005","ix":7,"v":{"a":0,"k":1,"ix":7}}]}],"ip":34,"op":634,"st":34,"bm":0},{"ddd":0,"ind":4,"ty":2,"nm":"圆形波纹.png","cl":"png","refId":"image_1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":20,"s":[100],"e":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":24,"s":[100],"e":[0]},{"t":28}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250.5,245.25,0],"ix":2},"a":{"a":0,"k":[300,282.25,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[28.881,28.881,100],"e":[80,80,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":24,"s":[80,80,100],"e":[100,100,100]},{"t":28}],"ix":6}},"ao":0,"ef":[{"ty":21,"nm":"填充","np":9,"mn":"ADBE Fill","ix":1,"en":1,"ef":[{"ty":10,"nm":"填充蒙版","mn":"ADBE Fill-0001","ix":1,"v":{"a":0,"k":0,"ix":1}},{"ty":7,"nm":"所有蒙版","mn":"ADBE Fill-0007","ix":2,"v":{"a":0,"k":0,"ix":2}},{"ty":2,"nm":"颜色","mn":"ADBE Fill-0002","ix":3,"v":{"a":0,"k":[1,1,1,1],"ix":3}},{"ty":7,"nm":"反转","mn":"ADBE Fill-0006","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"水平羽化","mn":"ADBE Fill-0003","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":0,"nm":"垂直羽化","mn":"ADBE Fill-0004","ix":6,"v":{"a":0,"k":0,"ix":6}},{"ty":0,"nm":"不透明度","mn":"ADBE Fill-0005","ix":7,"v":{"a":0,"k":1,"ix":7}}]}],"ip":20,"op":620,"st":20,"bm":0}],"markers":[]}
解析json外部结构
LottieComposition封装整个动画的信息,包括动画大小,动画时长,帧率,用到的图片,字体,图层等等。
{
"v": "4.6.0", //bodymovin的版本
"fr": 29.9700012207031, //帧率
"ip": 0, //起始关键帧
"op": 141.000005743048, //结束关键帧
"w": 800, //动画宽度
"h": 800, //动画高度
"ddd": 0,
"assets": [...] //资源信息
"layers": [...] //图层信息
}
解析图片资源
"assets": [ //资源信息
{ //第一张图片
"id": "image_0", //图片id
"w": 58, //图片宽度
"h": 31, //图片高度
"u": "images/", //图片路径
"p": "img_0.png" //图片名称
},
{...} //第n张图片
]
解析图层
"layers": [ //图层信息
{ //第一层动画
"ddd": 0,
"ind": 0, //layer id 图层 id
"ty": 4, //图层类型
"nm": "center_circle",
"ks": {...}, //动画
"ao": 0,
"shapes": [...],
"ip": 0, //inFrame 该图层起始关键帧
"op": 90, //outFrame 该图层结束关键帧
"st": 0, //startFrame 开始
"bm": 0,
"sr": 1
},
{...} //第n层动画
]