深圳幻海软件技术有限公司 欢迎您!

动画:Interpolator插值器使用和自定义详解

2023-03-01

前言属性动画可以对某个属性做动画,而插值器(TimeInterpolator)和估值器(TypeEvaluator)在其中扮演了重要角色;那么今天我们了解下插值器TimeInterpolator;一、插值器介绍1、Interpolator有什么用Interpolator被用来修饰动画效果,定义动画的

前言

属性动画可以对某个属性做动画,而插值器(TimeInterpolator)和估值器(TypeEvaluator)在其中扮演了重要角色;

那么今天我们了解下 插值器TimeInterpolator;

一、插值器介绍

1、Interpolator有什么用

  • Interpolator 被用来修饰动画效果,定义动画的变化率;
  • 在Android源码中对应的接口类为TimeInterpolator,通过输入均匀变化的0~1之间的值;
  • 可以得到匀速、正加速、负加速、无规则变加速等0~1之间的变化曲线;

2、应用场景

  • 实现非线性运动的动画效果;
  • 非线性运动是指动画改变的速率不是一成不变的,如加速、减速运动的动画效果;
  • 实现复杂的曲线动画,回弹效果自定义等都需要自定义插值器;

3、Android系统提供的插值器类型

  • AccelerateDecelerateInterpolator 在动画开始与结束比较慢,中间加速
  • AccelerateInterpolator 加速
  • AnticipateInterpolator 开始的时候向后然后向前甩
  • AnticipateOvershootInterpolator 开始的时候向后然后向前甩一定值后返回最后的值
  • BounceInterpolator 动画结束的时候弹起
  • CycleInterpolator 动画循环播放特定的次数,速率改变沿着正弦曲线
  • DecelerateInterpolator 减速
  • LinearInterpolator 以常量速率改变
  • OvershootInterpolator 向前甩一定值后再回到原来位置

二、插值器应用

插值器的使用有两种方式:在XML和代码中使用

1、xml

XML动画文件使用插值器时,需要设置系统设置的对应的插值器资源ID

<?xml version="1.0" encoding="utf-8"?> 
<set xmlns:android="http://schemas.android.com/apk/res/android"
    <alpha 
        android:fromAlpha="1.0" 
        android:toAlpha="0.1" 
        android:duration="2000" 
        android:repeatMode="reverse" 
        android:repeatCount="infinite" 
        android:interpolator="@android:anim/linear_interpolator"/> 
</set
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

2、代码中使用

  • 代码使用插值器时,只需创建对应的插值器对象,然后设置给动画对象;也可以加载xml文件中配置的插值器;
  • 利用view的setInterpolator(Context context, @AnimRes @InterpolatorRes int resID)设置插值器;
//创建一个渐变透明度的动画,从透明到完全不透明 
AlphaAnimation alphaAnimation = new AlphaAnimation(0.1f, 1.0f); 
//设置动画时长 
alphaAnimation.setDuration(5000); 
//设置动画重复方式 
alphaAnimation.setRepeatMode(AlphaAnimation.REVERSE); 
//设置动画播放次数 
alphaAnimation.setRepeatCount(AlphaAnimation.INFINITE); 
//设置匀速插值器 
alphaAnimation.setInterpolator(new LinearInterpolator()); 
//为View开启指定类型动画 
imageView.startAnimation(alphaAnimation) 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 使用Android内置的插值器能满足大多数的动画需求;
  • 如果系统提供的插值器无法满足需求,还可以自定义插值器;

三、自定义插值器

1、实现方式

  • 自定义插值器需要实现Interpolator或TimeInterpolator接口,并复写getInterpolation()方法;
  • 补间动画 实现 Interpolator接口;属性动画实现TimeInterpolator接口;
  • TimeInterpolator接口是属性动画中新增的,用于兼容Interpolator接口,这使得所有过去的Interpolator实现类都可以直接在属性动画使用;

Interpolator接口和TimeInterpolator接口说明如下:

// Interpolator接口 
public interface Interpolator {   
    // 内部只有一个方法:getInterpolation() 
     float getInterpolation(float input) {   
        // 参数说明 
        // input值值变化范围是0-1,且随着动画进度(0% - 100% )均匀变化 
        // 即动画开始时,input值 = 0;动画结束时input = 1 
        // 而中间的值则是随着动画的进度(0% - 100%)在0到1之间均匀增加 
      ...// 插值器的计算逻辑 
      return xxx; 
      // 返回的值就是用于估值器继续计算的fraction值,下面会详细说明 
    }   
// TimeInterpolator接口 
public interface TimeInterpolator {   
    float getInterpolation(float input){ 
// input值值变化范围是0-1,且随着动画进度(0% - 100% )均匀变化 
      ...// 插值器的计算逻辑 
    };   
}   
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

自定义插值器的关键在于:对input值根据动画的进度(0%-100%)通过逻辑计算从而计算出当前属性值改变的百分比;

2、自定义插值器

写一个自定义Interpolator:先减速后加速

/* 
 * 根据需求实现Interpolator接口 
 * TestInterpolator.java 
 */ 
public class TestInterpolator implements TimeInterpolator { 
    @Override 
    public float getInterpolation(float input) { 
        float result; 
        if (input <= 0.5) { 
            result = (float) (Math.sin(Math.PI * input)) / 2; 
            // 使用正弦函数来实现先减速后加速的功能,逻辑如下: 
            // 因为正弦函数初始弧度变化值非常大,刚好和余弦函数是相反的 
            // 随着弧度的增加,正弦函数的变化值也会逐渐变小,这样也就实现了减速的效果。 
            // 当弧度大于π/2之后,整个过程相反了过来,现在正弦函数的弧度变化值非常小,渐渐随着弧度继续增加,变化值越来越大,弧度到π时结束,这样从0过度到π,也就实现了先减速后加速的效果 
        } else { 
            result = (float) (2 - Math.sin(Math.PI * input)) / 2; 
        } 
        return result; 
        // 返回的result值 = 随着动画进度呈先减速后加速的变化趋势 
    } 

/* 
 * 步骤设置使用 
 * Test.java 
 */ 
 //  创建动画作用对象:此处以Button为例 
 mButton = (Button) findViewById(R.id.Button); 
 //  获得当前按钮的位置 
float curTranslationX = mButton.getTranslationX(); 
//  创建动画对象 & 设置动画 
ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "translationX", curTranslationX, 300,curTranslationX); 
// 表示的是: 
// 动画作用对象是mButton 
// 动画作用的对象的属性是X轴平移 
// 动画效果是:从当前位置平移到 x=1500 再平移到初始位置 
//  设置步骤1中设置好的插值器:先减速后加速 
animator.setInterpolator(new TestInterpolator()); 
// 启动动画 
animator.start(); 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.

3、贝塞尔曲线的插值器

(1)先使用贝塞尔曲线数值生成工具来获取想要的曲线数值

  • 工具网站:https://cubic-bezier.com/;
  • 拉拽左边图像的2个点,调整出符合效果的图形;
  • 点击Go按键,可看到红色与蓝色的方块运动状态,调节自己想要的效果;
  • 将4个参数运用到下面的代码中;

(2)代码运用

ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "translationX", curTranslationX, 300,curTranslationX); 
EaseCubicInterpolator interpolator = new EaseCubicInterpolator(0.31f, 0.85f,0.77f, 0.14f); 
animator.setInterpolator(interpolator) 
  • 1.
  • 2.
  • 3.

(3)贝塞尔曲线插值器

import android.graphics.PointF; 
import android.view.animation.Interpolator; 
/** 
 * 缓动三次方曲线插值器.(基于三次方贝塞尔曲线) 
 */ 
public class EaseCubicInterpolator implements Interpolator { 
    private final static int ACCURACY = 4096; 
    private int mLastI = 0; 
    private final PointF mControlPoint1 = new PointF(); 
    private final PointF mControlPoint2 = new PointF(); 
    /** 
     * 设置中间两个控制点 
     * 
     * 在线工具: http://cubic-bezier.com 
     * 
     * @param x1 
     * @param y1 
     * @param x2 
     * @param y2 
     */ 
    public EaseCubicInterpolator(float x1, float y1, float x2, float y2) { 
        mControlPoint1.x = x1; 
        mControlPoint1.y = y1; 
        mControlPoint2.x = x2; 
        mControlPoint2.y = y2; 
    } 
    @Override 
    public float getInterpolation(float input) { 
        float t = input; 
        // 近似求解t的值[0,1] 
        for (int i = mLastI; i < ACCURACY; i++) { 
            t = 1.0f * i / ACCURACY; 
            double x = cubicCurves(t, 0, mControlPoint1.x, mControlPoint2.x, 1); 
            if (x >= input) { 
                mLastI = i; 
                break; 
            } 
        } 
        double value = cubicCurves(t, 0, mControlPoint1.y, mControlPoint2.y, 1); 
        if (value > 0.999d) { 
            value = 1; 
            mLastI = 0; 
        } 
        return (float) value; 
    } 
    /** 
     * 求三次贝塞尔曲线(四个控制点)一个点某个维度的值.<br> 
     * <p> 
     * 
     * @param t      取值[0, 1] 
     * @param value0 
     * @param value1 
     * @param value2 
     * @param value3 
     * @return 
     */ 
    public static double cubicCurves(double t, double value0, double value1, 
                                     double value2, double value3) { 
        double value; 
        double u = 1 - t; 
        double tt = t * t; 
        double uu = u * u; 
        double uuu = uu * u; 
        double ttt = tt * t; 
        value = uuu * value0; 
        value += 3 * uu * t * value1; 
        value += 3 * u * tt * value2; 
        value += ttt * value3; 
        return value; 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.

总结

要实现复杂的动画效果时,就要自定义插值器,其实插值器还是的学习算法;

大家一起加油;