左右抖动的动画

目标效果

触发后,金币左右抖动多次,向上移动并消失,文字向下移动显示

实现方式似乎很简单,依次实现上述三个动画即可

而本文主要展示我在第一个动画的探索过程

版本1

我们需要保持动画结束时的状态,故采用属性动画。

1
2
3
4
5
ObjectAnimator.ofFloat(iv_coin, "translationX",  -100f,  100f).apply {
duration = 1000
repeatCount = 2
repeatMode = ValueAnimator.REVERSE
}.start()

存在问题,动画播放开始时,金币直接跳到左边,而非缓慢移动到左边,导致动画开始时和结束时很突兀。

版本2

针对发现的原因,我们把中点到左边的过程也纳入动画中,
改变如下

原本: left -> right
现在: mid -> left -> mid -> right -> mid

同时,重复模式也要改成RESTART, 原因,动画重复时,当前定义的路径是不能翻转的。

1
2
3
4
5
ObjectAnimator.ofFloat(iv_coin, "translationX",  0f, -100f, 0f, 100f, 0f).apply {
duration = 1000
repeatCount = 2
repeatMode = ValueAnimator.RESTART
}.start()

存在问题,重复播放到中点时,金币摆动的速度会变得很慢,后才逐渐加快

版本3

一直误以为插值器是匀速线性,即LinearInterpolator,导致的这个错误认知的原因应该来源于这个方法。
当设的值为空时,便设为LinearInterpolator

1
2
3
4
5
6
7
8
@Override
public void setInterpolator(TimeInterpolator value) {
if (value != null) {
mInterpolator = value;
} else {
mInterpolator = new LinearInterpolator();
}
}

而实际上默认的插值器是非线性的,即AccelerateDecelerateInterpolator,先加速后减速。仔细想想使用这个插值器更加合理,更加接近自然效果。 生活中,当我们用力推动一个小球一段时间,加速到减速的过程更加符合实际。

而这里,我们尝试将插值器改为线性的。

1
2
3
4
5
6
ObjectAnimator.ofFloat(iv_coin, "translationX",  0f, -100f, 0f, 100f, 0f).apply {
duration = 1000
repeatCount = 2
interpolator=LinearInterpolator()
repeatMode = ValueAnimator.RESTART
}.start()

存在问题,摆动的整个过程很僵硬,不符合自然效果。

版本4

为了让金币摆动自然,我的方案是将金币摆动的过程分为三个动画实现,每个动画使用默认的插值器。

动画1:mid -> left
动画2:left -> right
动画3:right -> mid

注意duration时间的分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
AnimatorSet().apply {

val xDuration = 1000L
val xOffset = 100f

playSequentially(
ObjectAnimator.ofFloat(iv_coin, "translationX", -xOffset)
.setDuration((xDuration / 2)),
ObjectAnimator.ofFloat(iv_coin, "translationX", -xOffset, xOffset).apply {
duration = xDuration
repeatMode = ValueAnimator.REVERSE
repeatCount = 2
},
ObjectAnimator.ofFloat(iv_coin, "translationX", 0f).setDuration((xDuration / 2))
)
}.start

效果已经符合我的需求,不知道有没有更好的实现方式?