[原]模拟自然动画的精髓——TimeInterpolator与TypeEvaluator

   2016-09-29 0
核心提示:模拟自然动画的精髓——TimeInterpolator与TypeEvaluator在今天的文章开始之前,有个忙想请大家帮一下,希望在京东、淘宝、当当、亚马逊购买了我的书《Android群英传:神兵利器》的朋友们,帮忙去网店上给个简短的评价,举手之劳,还是多谢大家啦~~ 本文绘图软

模拟自然动画的精髓——TimeInterpolator与TypeEvaluator

在今天的文章开始之前,有个忙想请大家帮一下,希望在京东、淘宝、当当、亚马逊购买了我的书《Android群英传:神兵利器》的朋友们,帮忙去网店上给个简短的评价,举手之劳,还是多谢大家啦~~

本文绘图软件 https://www.desmos.com/calculator

通过属性动画,我们可以模拟各种属性的动画效果,但对于这些属性来说,动画变化的速率和范围,是实现一个更加『真实、自然』的动画的基础,这两件事情,就是通过TimeInterpolator与TypeEvaluator来实现的。

TimeInterpolator与TypeEvaluator共同作用在ValueAnimator上,通过复合的方式产生最后的数据,这也就是数学上的『复合函数』,TimeInterpolator控制在何时取值,而TypeEvaluator控制在当前时间点需要取多少值。

由于这里涉及到两个变量,所以,这里我们通常使用『控制变量法』来进行这两个属性的研究,因为通常情况下,这两个属性的作用效果是殊途同归的。

TimeInterpolator

首先,我们研究TimeInterpolator,所以,将TypeEvaluator设置为默认,不产生任何修改。

TimeInterpolator,中文常常翻译成插值器。一个最简单的属性动画,示例如下:

ObjectAnimator animator = ObjectAnimator.ofFloat(mTextView, "translationX", 0, mDistance);
animator.setDuration(mDuration);
animator.setInterpolator(new BounceInterpolator());
animator.start();

通过setInterpolator方法,可以给Animator设置插值器,默认的插值器是AccelerateDecelerateInterpolator,即加速减速插值器。

理解TimeInterpolator的作用原理

TimeInterpolator是作用在时间参数上,例如我们有一个动画,时间从0到1,取值也从0到1,我们通过下面三条曲线来看同一时间点,取到的数值的不同。

[原]模拟自然动画的精髓——TimeInterpolator与TypeEvaluator

当时间取0.5时,我们对应的y=x这条曲线,取出的是0.5,y=sqrt(x)这条曲线,取出的是0.25,y=x^2 这条曲线,取出的是0.7。也就是说,同一个真实的时间节点0.5,我们通过设置不同的函数曲线,取出了不同的数值,那么TimeInterpolator正是通过这种方式,来对时间参数进行修改,即,真实的时间0.5,对于其它两个函数,分别取出了模拟时间0.25和0.7所对应的值,从而达到了『篡改』时间的目的。

Android中的TimeInterpolator

Android中已经给我们实现了很多TimeInterpolator,例如前面我们举的例子——AccelerateDecelerateInterpolator。我们打开AccelerateDecelerateInterpolator的源码。

[原]模拟自然动画的精髓——TimeInterpolator与TypeEvaluator

其中关键的就是那行数学公式——(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f。我们来绘制下这个公式对应的曲线图(这里input的取值范围是0到1)。

[原]模拟自然动画的精髓——TimeInterpolator与TypeEvaluator

在[0,1]区间内,就是我们的加速减速插值器了,结合字面意义很好理解。

那么在Android中,系统还给我们提供了非常多的TimeInterpolator,例如:AccelerateDecelerateInterpolator, AccelerateInterpolator, AnticipateInterpolator, AnticipateOvershootInterpolator, BounceInterpolator, CycleInterpolator, DecelerateInterpolator, LinearInterpolator, OvershootInterpolator, PathInterpolator。

大家可以通过API文档来找到这些插值器的定义,同时,通过源码来查看他们使用的数学公式。

自定义TimeInterpolator

自定义TimeInterpolator非常简单,我们参考系统自带的TimeInterpolator就可以实现了,即实现Interpolator接口和getInterpolation方法即可,例如:

package com.xys.naturalanim.views.interpolator;

import android.view.animation.Interpolator;

public class CustomInterpolator implements Interpolator {

    @Override
    public float getInterpolation(float input) {
        return (float) Math.sin((input) * Math.PI * 0.5F);
    }
}

其重点就是实现getInterpolation方法中的数学公式。

TypeEvaluator

TypeEvaluator通常被翻译成估值器,在理解了TimeInterpolator之后,再理解TypeEvaluator就很简单了,一个系统自带的简单TypeEvaluator如下:

[原]模拟自然动画的精髓——TimeInterpolator与TypeEvaluator

可见,它和TimeInterpolator基本一样,只不过实现的公式的参数不一样,但简单的换算一下,就通用了,例如:

if (mInterpolator != null) {
    for (int i = 0; i < mViewWidth; i++) {
        mPath.lineTo(i, mViewHeight - mInterpolator.getInterpolation(i * 1.0F / mViewHeight) * mViewHeight);
    }
} else {
    for (int i = 0; i < mViewWidth; i++) {
        mPath.lineTo(i, mViewHeight - (Integer) mTypeEvaluator.evaluate(i * 1.0F / mViewHeight, 0, mViewHeight));
    }
}

但是它们还是有一些细小的区别的,后面再细说,简单的概括,就是:

TimeInterpolator控制动画的速度,而TypeEvaluator控制动画的值,他们可以共同作用,也可以单独作用(让另一个使用默认值)。

实际上,TypeEvaluator中的一个参数fraction,就是『复合函数』中TimeInterpolator计算的结果。即fraction=getInterpolation()。

自定义TypeEvaluator

这里首先讲一下TypeEvaluator的自定义,那么为什么要加呢,这是因为,这种方式限定了TypeEvaluator的类型是Number,那么这种就和TimeInterpolator几乎可以完全转化了,他们的目的都是通过提供的参数来完成曲线的绘制,从而实现对动画运动的控制。而TimeInterpolator只有一个参数,实现起来更加的简单,所以,大部分时候,我们都通过TimeInterpolator来实现这种运动曲线的模拟,所以,TypeEvaluator就这样没落了。

但是,不要以为TypeEvaluator就这样没用了,我们在小标题中也写了,是类型的TypeEvaluator可以进行转换,而TypeEvaluator实际上还有很多其它类型,在动画的坐标控制上,有奇效。

TypeEvaluator控制点的坐标

前面我们说了,Float类型的TypeEvaluator和TimeInterpolator基本是一样的,但TypeEvaluator并不只有Float这样一种,它有一种用的比较多的特性,就是通过TypeEvaluator来对运动坐标进行修改,将原本的直线坐标修改成曲线坐标,它通常会与ValueAnimator进行配合使用,例如下面的这个例子:

这种实现曲线运动的方式,就是通过TypeEvaluator来进行实现的,其中核心原理,就是通过Bezier曲线的De Casteljau算法计算出具体的点坐标,并设置给TypeEvaluator,代码如下所示。

public class BezierEvaluator implements TypeEvaluator<PointF> {

    private PointF mControlPoint;

    public BezierEvaluator(PointF controlPoint) {
        this.mControlPoint = controlPoint;
    }

    @Override
    public PointF evaluate(float t, PointF startValue, PointF endValue) {
        return BezierUtil.CalculateBezierPointForQuadratic(t, startValue, mControlPoint, endValue);
    }
}

Bezier的计算公式如下所示。

/**
 * B(t) = (1 - t)^2 * P0 + 2t * (1 - t) * P1 + t^2 * P2, t ∈ [0,1]
 *
 * @param t  曲线长度比例
 * @param p0 起始点
 * @param p1 控制点
 * @param p2 终止点
 * @return t对应的点
 */
public static PointF CalculateBezierPointForQuadratic(float t, PointF p0, PointF p1, PointF p2) {
    PointF point = new PointF();
    float temp = 1 - t;
    point.x = temp * temp * p0.x + 2 * t * temp * p1.x + t * t * p2.x;
    point.y = temp * temp * p0.y + 2 * t * temp * p1.y + t * t * p2.y;
    return point;
}

所以,综上所述,在作动画速率曲线控制的时候,使用TimeInterpolator即可,如果要改变点的坐标,就可以使用TypeEvaluator。

自然动画

在了解了TimeInterpolator和TypeEvaluator之后,我们就可以来了解下动画展现的优化方式了,普通的动画默认以线性的方式展现,但带来的后果就是动画效果的『僵硬』,动画本来是模拟两个状态的过渡过程的,这个在自然界中是『自然、流畅』的,所以,我们不能通过线性的数据变化来模拟自然动画,这就需要使用TimeInterpolator和TypeEvaluator来设计动画曲线了,通过它们来控制动画的实现过程,从而实现动画的展示,这就是我们来实现自然动画的的基本方式。

缓动函数

既然线性的动画曲线无法满足我们的动画模拟需求,那么就需要通过一定的数学公式来改变这些动画曲线,值得庆幸的是,这些事情有人帮我们做过了,有人专门设计了这样一些动画的曲线库。

http://easings.net/zh-cn

[原]模拟自然动画的精髓——TimeInterpolator与TypeEvaluator

就是这样一些缓动函数库,让我们在设计动画的时候,可以作更加真实的模拟。同时,你也可以设计自己的曲线函数,下面这个网站,就可以实现这样的模拟。

http://inloop.github.io/interpolator/

自然动画的模拟演示

在各位前辈的肩膀上,我这里撸了一个演示的Demo库,界面如图。

[原]模拟自然动画的精髓——TimeInterpolator与TypeEvaluator

这里主要有几个功能:

  • 可以选择不同的TimeInterpolator
  • 可以选择要演示的动画效果,包括位移、缩放、旋转、透明度
  • 演示包含两个View,上面的是设置对应动画模拟效果的View,下面的是对照的线性效果的View

一个动态图简单的了解下:

代码已经开源到Github:

https://github.com/xuyisheng/NaturalAnim

欢迎大家提交自己的函数曲线-插值器。

欢迎大家关注我的微信公众号:

[原]模拟自然动画的精髓——TimeInterpolator与TypeEvaluator

 
标签: 安卓开发
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 安卓中通知功能的具体实现
    安卓中通知功能的具体实现
    通知[Notification]是Android中比较有特色的功能,当某个应用程序希望给用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知实现。使用通知的步骤1、需要一个NotificationManager来获得NotificationManager manager = (NotificationManager
    02-05 安卓开发
  • Android view系统分析-setContentView
    Android view系统分析-setContentView
    第一天上班,列了一下今年要学习的东西。主要就是深入学习Android相关的系统源代码,夯实基础。对于学习Android系统源代码,也没什么大概,就从我们平常使用最基础的东西学起,也就是从view这个切入点开始学习Android的源码,在没分析源码之前,我们有的时候
    02-05 安卓开发
  • 如何进行网络视频截图/获取视频的缩略图
    如何进行网络视频截图/获取视频的缩略图
    小编导读:获取视频的缩略图,截图正在播放的视频某一帧,是在音视频开发中,常遇到的问题。本文是主要用于点播中截图视频,同时还可以获取点播视频的缩略图进行显示,留下一个问题,如下图所示, 如果要获取直播中节目视频缩略图,该怎么做呢?(ps:直播是直
  • Android NDK 层发起 HTTP 请求的问题及解决
    Android NDK 层发起 HTTP 请求的问题及解决
    前言新的一年,大家新年快乐~~鸡年大吉!本次给大家带来何老师的最新文章~虽然何老师还在过节,但依然放心不下广大开发者,在此佳节还未结束之际,给大家带来最新的技术分享~ 事件的起因不说了,总之是需要实现一个 NDK 层的网络请求。为了多端适用,还是选择
  • Android插件化(六): OpenAtlasの改写aapt以防止资源ID冲突
    Android插件化(六): OpenAtlasの改写aapt以防
    引言Android应用程序的编译中,负责资源打包的是aapt,如果不对打包后的资源ID进行控制,就会导致插件中的资源ID冲突。所以,我们需要改写aapt的源码,以达到通过某种方式传递资源ID的Package ID,通过aapt打包时获取到这个Package ID并且应用才插件资源的命名
    02-05 安卓开发
  • Android架构(一)MVP架构在Android中的实践
    Android架构(一)MVP架构在Android中的实践
    为什么要重视程序的架构设计 对程序进行架构设计的原因,归根结底是为了 提高生产力 。通过设计是程序模块化,做到模块内部的 高聚合 和模块之间的 低耦合 (如依赖注入就是低耦合的集中体现)。 这样做的好处是使得程序开发过程中,开发人员主需要专注于一点,
    02-05 安卓开发
  • 安卓逆向系列教程 4.2 分析锁机软件
    安卓逆向系列教程 4.2 分析锁机软件
    安卓逆向系列教程 4.2 分析锁机软件 作者: 飞龙 这个教程中我们要分析一个锁机软件。像这种软件都比较简单,完全可以顺着入口看下去,但我这里还是用关键点来定位。首先这个软件的截图是这样,进入这个界面之后,除非退出模拟器,否则没办法回到桌面。上面那
    02-05 安卓开发
  • Android插件化(二):OpenAtlas插件安装过程分析
    Android插件化(二):OpenAtlas插件安装过程分析
    在前一篇博客 Android插件化(一):OpenAtlas架构以及实现原理概要 中,我们对应Android插件化存在的问题,实现原理,以及目前的实现方案进行了简单的叙述。从这篇开始,我们要深入到OpenAtlas的源码中进行插件安装过程的分析。 插件的安装分为3种:宿主启动时立
    02-05 安卓开发
  • [译] Android API 指南
    [译] Android API 指南
    众所周知,Android开发者有中文网站了,API 指南一眼看去最左侧的菜单都是中文,然而点进去内容还是很多是英文,并没有全部翻译,我这里整理了API 指南的目录,便于查看,如果之前还没有通读,现在可以好好看一遍。注意,如果标题带有英文,说明官方还没有翻
  • 使用FileProvider解决file:// URI引起的FileUriExposedException
    使用FileProvider解决file:// URI引起的FileUri
    问题以下是一段简单的代码,它调用系统的相机app来拍摄照片:void takePhoto(String cameraPhotoPath) {File cameraPhoto = new File(cameraPhotoPath);Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);takePhotoIntent.putExtra(Medi
    02-05 安卓开发
点击排行