Interpolator
插值器
先上图吸引人,:p
在Android动画中有一个很重要的工具,Interpolator,翻译过来就是插值器,这个词相信对上过数值分析这门课的同学都不会感到陌生,当初这门课是折腾的我欲仙欲死,然后发现工作了还是要被折磨 。好了,废话不多说,那么这个插值器的作用是什么呢?简单的来说就是,给你两个点A( x 1 x_1 x1, y 1 y_1 y1),B( x 2 x_2 x2, y 2 y_2 y2),你给我描绘出从A点到B点的曲线,说白了,就是给出A到B的函数曲线的表达式。而Android中View的动画,一般是由一个开始状态,和一个结束状态,Interpolator就是用来描述从开始到结束这中间的View的状态的。比如位移动画TranslationAnimation,从(0,0)水平移动到(100,0)的动画,如果使用线性插值器(LinearInterpolator),这种插值器View动画的属性(x轴坐标)随时间线性增长,所以是均速移动。
package android.animation;
/**
* A time interpolator defines the rate of change of an animation. This allows animations
* to have non-linear motion, such as acceleration and deceleration.
*/
public interface TimeInterpolator {
/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end
* @return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.
*/
float getInterpolation(float input);
}
好多人都把Interpolator叫做加速器,TimeInterpolator上也有一句说明
A time interpolator defines the rate of change of an animation.This allows animations to have non-linear motion, such as acceleration and deceleration.
翻译过来就是一个定义了动画改变速率的时间插值器,允许动画能做加速运动和加速运动等非线性运动, 看到动画改变速率似乎叫加速器好像没啥毛病,但是我们不能只看到改变速率 这几个字就认为这是加速器。事实上,getInterpolation返回的就是你需要定义的插值函数的返回值,它是一个动画执行的进度(也可以认为它是一种状态),插值函数
y
=
f
(
x
)
y=f(x)
y=f(x) ,
x
∈
[
0
,
1
]
x\in[0,1]
x∈[0,1]。
x
=
0
x=0
x=0代表动画开始执行,
x
=
1
x=1
x=1代表动画执行结束。
y
=
0
y=0
y=0代表动画开始执行View的状态,
y
=
1
y=1
y=1代表动画结束执行View的状态。例如,TranslationAnimation,将一个View从(0,0)水平移动到(100,0),那么动画开始View的状态就是View在(0,0)的位置,动画结束View的状态就是View在(100,0)的位置。
所以Interpolator压根就不是动画改变的速率。那动画改变的速率是谁来决定的?是由你定义的插值函数决定的!动画改变的速率就是插值函数的一阶导数!所以它才是加速器。
比如你的插值函数是线性函数
y
=
k
x
+
b
y=kx+b
y=kx+b,那么动画的改变速率就是插值函数的一阶导数
y
′
y'
y′ = k,就是常量,所以动画是匀速变化的;同样如果你定义一个二次函数
y
=
x
2
y=x^2
y=x2,那么动画的改变速率就是
y
′
=
2
∗
x
y'=2*x
y′=2∗x,它随着x的增加而增加,动画也会随着时间越变越快。
Android中的所有Interpolator都要实现android.animation.TimeInterpolator接口,TimeInterpolator有一个接口函数float getInterpolation(float input),这个函数就是插值的函数曲线的表达式(
y
=
F
(
x
)
y = F(x)
y=F(x)的 F),但是需要你自己去实现。输入值就是自变量
x
x
x,范围是[0,1]。
x
x
x你可以认为它就是动画的执行时间,只不过android在这里把它归一化了。返回值就是y,这个y就是某一时间点
x
0
x_0
x0 对应的View的动画执行进度
y
0
y_0
y0 ,当然这里也是归一化之后的,至于如何将这个归一化的值映射到具体的属性上去,那就是Evaluator要干的活了,这个我们后面会再讲。注意返回值允许小于0,或者大于1。也就意味着,动画可以超出起始状态和结束状态。我们可以根据需求实现各种Interpolator,但是一般用android给我提供的也就够用了。
其实最简单的Evaluator也非常简单,这里可以简介一下。我们已经知道Interpolator返回的是一个动画的进度(百分比),那么这个进度其实就是
f
r
a
c
t
i
o
n
=
(
V
i
e
w
当
前
状
态
−
动
画
开
始
V
i
e
w
的
状
态
)
(
动
画
结
束
V
i
e
w
的
状
态
−
动
画
开
始
V
i
e
w
的
状
态
)
fraction =\frac{(View当前状态−动画开始View的状态)}{(动画结束View的状态 - 动画开始View的状态)}
fraction=(动画结束View的状态−动画开始View的状态)(View当前状态−动画开始View的状态)
再来看一下Evaluator的源码,
public interface TypeEvaluator<T> {
/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction The fraction from the starting to the ending values
* @param startValue The start value.
* @param endValue The end value.
* @return A linear interpolation between the start and end values, given the
* <code>fraction</code> parameter.
*/
public T evaluate(float fraction, T startValue, T endValue);
}
文档写的很清楚了,而且都把代码给你说出来了,就是
r
e
s
u
l
t
=
s
t
a
r
t
V
a
l
u
e
+
f
r
a
c
t
i
o
n
∗
(
e
n
d
V
a
l
u
e
−
s
t
a
r
t
V
a
l
u
e
)
result= startValue + fraction*(endValue - startValue)
result=startValue+fraction∗(endValue−startValue)
其中startValue和endValue分别是动画开始和结束View的状态, fraction就是Interpolator传过来的动画进度值,返回的就是真实的View的状态了。所以Evaluator的作用就是接收Interpolator传递过来的fraction,并根据这个fraction以及View开始状态和结束时状态来计算动画进行中的状态。(其实这里我一直有个疑惑的,如果再Interpolator里面什么也不做,就是不设置Interpolator,在Evaluator里面去做一些对fraction的变换,比如二次方之类的,好像Evaluator就把Interpolator的活给干了,那还要Interpolator做什么?我猜测这样分开是为了各司其职吧。)
Android已经给我提供了许多现成的Interpolator,常见的有:
name | 作用 | 资源id |
---|---|---|
LinearInterpolator | 线性插值器,均匀变化 | @android:interpolator/linear |
AccelerateInterpolator | 加速插值器,加速运动 | @android:interpolator/accelerate_cubic |
DecelerateInterpolator | 减速插值器,减速运动 | @android:interpolator/decelerate_cubic |
AccelerateDecelerateInterpolator | 加减速插值器,先加速,后减速 | @android:interpolator/accelerate_decelerate |
BounceInterpolator | 回弹插值器,会产生类似乒乓球落地的弹跳效果 | @android:interpolator/bounce |
OvershootInterpolator | 会越过终点然后回弹回来 | @android:interpolator/overshoot |
AnticipateOvershootInterpolator | 会先反向越过起点,然后再折返,再越过终点,在这返回终点 | @android:interpolator/anticipate_overshoot |
CycleInterpolator | 循环运动 | @android:interpolator/cycle |
PathInterpolator | 贝塞尔曲线的效果,支持二阶三阶贝塞尔曲线 | – |
下面给出每个插值器的效果图,为了能更加清楚的演示出每种插值器的效果,我给目标View一个水平运动的动画,并且将水平运动的动画的interpolator设置为线性插值器,这样目标View就能沿着水平匀速运动。然后我给目标View一个垂直位移运动的动画,并且给动画设定不同的插值器,并将View的运动轨迹画出来,这样就能清楚的看到各种interpolator的效果。注意最终的轨迹是水平方向的运动和垂直方向运动的叠加,坐标系中绘制的是Interpolator的 y=F(x) 的曲线。(是不是发现曲线和运动轨迹好像一样啊?思考一下为什么。)
一个有意思的贝塞尔演示网站
https://cubic-bezier.com/#.55,-0.83,.4,1.87
1、LinearInterpolator
2、AccelerateInterpolator
3、AccelerateDecelerateInterpolator
4、BounceInterpolator
5、OvershootInterpolator
6、AnticipateOvershootInterpolator
7、CycleInterpolator
8、PathInterpolator
9、Other & 源码
Android LOLLIPOP(21)之后又引入FastOutLinearInInterpolator、FastOutSlowInInterpolator、LinearOutSlowInInterpolator三个插值器,其实原理都是一样的,都是实现了Interpolator接口,以上三个插值器本质上都是用的三阶贝塞尔曲线。
源码依旧在全球最大同性交友网站github,目录是me.fresh.lee.kotlintest.activity.InterpolatorActivity,代码主要是用Kotlin写的。