CADisplayLink 动画进阶

CADisplayLink 动画进阶

前言

  • 之前有更新过一篇 如何实现一个圆形进度条按钮 的文章。其需求场景是适应于相机拍照录制按钮的,主要介绍的是如何使用 CADisplayLink 实现其进度条的动画效果。然而,写的比较粗糙,没有封装也没有整理代码,仅仅提供实现,dbq。于是在处女座强迫症以及愧疚感的驱使下,最近又重新对该 demo 进行了整理。

  • 在这篇博客你可以了解到的内容有:

    1. 如何基于 CADisplayLink 实现非线性动画效果
    2. 什么是缓动函数
  • 实现的效果举例:


    QQ20190217-141333.gif

正文

需求分析

  • 做任何动画效果,首先要做的就是对动画的需求进行拆分,比如整个动画的过程可以分为哪几个部分。针对本文中要实现的圆形进度条按钮动画来说,整个动画过程可以拆分为按钮放大的动画以及进度条扩充动画两部分。

    对于按钮放大的过程,需要考虑的是在放大前到放大之后的状态,按钮中有哪些部分发生了变化,如何发生变化,是线性过渡还是非线性过渡?

    对于进度条扩充部分,考虑的就是黄色进度条是如何过渡状态的,线性还是非线性?

    显然之前的按钮动画效果都是线性效果,什么是线性,就是 y = ax + b,动画过程是均匀变化的。非线性就是动画过程是根据某个函数进行非均匀的变化。

如何基于 CADisplayLink 实现非线性动画效果

在介绍实现非线性动画效果之前,我们先简单复习一下线性动画效果的实现。假设动画的起始状态变量为 fromValue,最终状态的变量为 toValue,当前动画值进度为 percent,当前状态为 currentValue。那么我们可以得到计算动画当前状态的插值公式为:

currentValue = fromValue + (toValue - fromValue) * percent 

其实整体实现思路还是和 如何实现一个圆形进度条按钮 中提到的一样。不过在这里有需要勘误的一个点是,上篇博客提到 CADisplaylink "iOS 设备屏幕显示每秒刷新60次"。CADisplayLink 的刷新频率确实是60次/秒左右,但是并不固定,由于每次调用 CADisplayLink 的时间间隔都不是平均的,所以我们不能根据调用次数乘以1/60的时间间隔来得到当前经历的时间。正确计算当前经历时间的方法是通过获取当前时间再减去起始时间来得到:

@property (nonatomic, assign) NSTimeInterval beginTime;
@property (nonatomic, assign) NSTimeInterval currentTime;
//在动画开始时记录起始时间
self.beginTime = CACurrentMediaTime();

得到当前的经历时间为

self.currentTime = CACurrentMediaTime() - self.beginTime;

由于我们是线性动画,所以假设动画时间进程为 timePercent,timePercent = self.currentTime / duration,那么当前动画值进度 percent 就等于 timePercent,然后可以得到

currentValue = fromValue + (toValue - fromValue) * (self.currentTime / duration) 

其中 fromValue、toValue、duration 都是已知数,这样我们就可以拿到 currentValue,然后在 CADisplayLink 每次调用的函数中去更新 currentValue 值,就可以实现线性动画的过渡效果了。

基于此思路我们实现一个插值函数如下,percent 为动画值进度

- (CGFloat)interpolateFrom:(CGFloat)from to:(CGFloat)to percent:(CGFloat)percent {
    return from + (to - from) * percent;
}

那么,如何实现非线性的动画效果呢? 我们先了解一下一些实现非线性动画效果的函数 —— 缓动函数

缓动函数

缓动函数是动画时间进程(p)和动画值进程(s)之间的一个映射。什么是动画时间进程,就是 timePercent,而动画值进程就是 percent。两者的定义阈都为[0,1]。在线性关系中,percent = timePercent,而在非线性关系中,percent = f(timePercent)。

我们知道动画一般都有渐进(EaseIn)、渐出(EaseOut)、渐近渐出(EaseInOut)的效果,它们对应的缓动函数图大概是这样:


CADisplayLink 动画进阶_第1张图片
function.png

举例来说,对于 EaseIn 函数的图像分析,y = x * x 的坐标图是完全符合 EaseIn 的效果的,所以我们可以实现一个 y = x * x 的函数作为这种 EaseIn 效果的缓动函数:

- (double)calculate:(double) p {
    return p * p;
}

那么,在线性动画的代码中,调用 interpolateFrom:to:percent: 之前,将 percent(其实是 timePercent)作为参数传入 calculate 方法,计算得到动画值进程 percent,然后再调用 interpolateFrom:to:percent: 计算当前的动画值。这样我们就实现了非线性动画中的 QuadraticEaseIn 动画。

关于各种缓动函数的效果可以参照这个网址,对于其中所有效果的缓动函数都已经在 Demo KiraCircleButton 中的 AnimationFunction 中实现了。Demo 中提供了配置页面,可以方便的选择配置体验不同的动画效果。

既然本文是进阶篇,那么我就不贴具体的实现代码了。其中有关如何绘制,具体如何实现动画的问题,可以直接下载 Demo KiraCircleButton 看源码。
有问题欢迎指出,谢谢。

你可能感兴趣的:(CADisplayLink 动画进阶)