一、前言
上一章节介绍了POPAnimator的作用,里面使用了很多POPAnimationState中的方法用来判断动画状态。这章节将介绍POPAnimationState如何更新动画状态,以及如何确定动画属性的变化值。
二、POPAnimationState
POPAnimationState用于记录动画的状态以及动画的相关属性,我们在解析二中也提及到了如何将POPAnimation OC对象转化为POPAnimationState struct结构。
1.启动动画
- startIfNeeded方法:用于判断动画是否已经开始
bool startIfNeeded(id obj, CFTimeInterval time, CFTimeInterval offset)
{
bool started = false;
// detect start based on time
if (0 == startTime && time >= beginTime + offset) {
// activate & unpause
active = true;
setPaused(false);
// note start time
startTime = lastTime = time;
started = true;
}
// ensure values for running animation
bool running = active && !paused;
if (running) {
willRun(started, obj);
}
// handle start
if (started) {
handleDidStart();
}
return started;
}
复制代码
该方法通过time来判断动画是否开始,并更新active和pause属性的状态,若动画正在执行,则会调用willRun
方法。
POPAnimationState:
virtual void willRun(bool started, id obj) {}
POPPropertyAnimationState:
virtual void willRun(bool started, id obj) {
//读取初始值
if (NULL == fromVec) {
readObjectValue(&fromVec, obj);
}
// 保证toValue被初始化
if (NULL == toVec) {
if (kPOPAnimationDecay == type) {
[self toValue];
} else {
readObjectValue(&toVec, obj);
}
}
if (started) {
if (!currentVec) {
currentVec = VectorRef(Vector::new_vector(valueCount, NULL));
//将初始值赋值给currentVec
if (currentVec && fromVec) {
*currentVec = *fromVec;
}
}
if (!velocityVec) {
velocityVec = VectorRef(Vector::new_vector(valueCount, NULL));
}
if (!originalVelocityVec) {
originalVelocityVec = VectorRef(Vector::new_vector(valueCount, NULL));
}
}
if (NULL == distanceVec) {
VectorRef fromVec2 = NULL != currentVec ? currentVec : fromVec;
if (fromVec2 && toVec) {
Vector4r distance = toVec->vector4r();
distance -= fromVec2->vector4r();
if (0 != distance.squaredNorm()) {
distanceVec = VectorRef(Vector::new_vector(valueCount, distance));
}
}
}
}
复制代码
该方法主要是读取fromValue和toValue的属性,并更新currentVec和distanceVec。我们看下readObjectValue
方法:
void readObjectValue(VectorRef *ptrVec, id obj)
{
POPAnimatablePropertyReadBlock read = property.readBlock;
if (NULL != read) {
Vector4r vec = read_values(read, obj, valueCount);
*ptrVec = VectorRef(Vector::new_vector(valueCount, vec));
if (tracing) {
[tracer readPropertyValue:POPBox(*ptrVec, valueType, true)];
}
}
}
NS_INLINE Vector4r read_values(POPAnimatablePropertyReadBlock read, id obj, size_t count)
{
Vector4r vec = Vector4r::Zero();
if (0 == count)
return vec;
read(obj, vec.data());
return vec;
}
复制代码
其实,这里就是通过获取到POPAnimatablePropertyReadBlock
,然后从block中读取到动画的属性值。
2. 更新动画变化值
- advancedTime:通过该方法计算动画属性的变化值
bool advanceTime(CFTimeInterval time, id obj) {
bool advanced = false;
bool computedProgress = false;
CFTimeInterval dt = time - lastTime;
switch (type) {
case kPOPAnimationSpring:
advanced = advance(time, dt, obj);
break;
case kPOPAnimationDecay:
advanced = advance(time, dt, obj);
break;
case kPOPAnimationBasic: {
advanced = advance(time, dt, obj);
computedProgress = true;
break;
}
case kPOPAnimationCustom: {
customFinished = [self _advance:obj currentTime:time elapsedTime:dt] ? false : true;
advanced = true;
break;
}
default:
break;
}
if (advanced) {
if (!computedProgress) {
computeProgress();
}
delegateProgress();
lastTime = time;
}
return advanced;
}
复制代码
这里,我们主要研究下kPOPAnimationBasic情况:
bool advance(CFTimeInterval time, CFTimeInterval dt, id obj) {
if (!timingFunction) { //获取当前对应的动画缓冲函数
((POPBasicAnimation *)self).timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
}
CGFloat p = 1.0f;
if (duration > 0.0f) {
CFTimeInterval t = MIN(time - startTime, duration) / duration; //将时间标准化,即时间间隔/时间周期
p = POPTimingFunctionSolve(timingControlPoints, t, SOLVE_EPS(duration)); //计算对应的进度
timeProgress = t;
} else {
timeProgress = 1.;
}
//根据进度,计算动画新的属性值
interpolate(valueType, valueCount, fromVec->data(), toVec->data(), currentVec->data(), p);
progress = p;
clampCurrentValue();
return true;
}
复制代码
这里为了更好地求解缓冲函数(三次贝塞尔曲线)的值,将数据标准化,然后利用标准化后的数据调用POPTimingFunctionSolve
进行求解。
double POPTimingFunctionSolve(const double vec[4], double t, double eps)
{
WebCore::UnitBezier bezier(vec[0], vec[1], vec[2], vec[3]);
return bezier.solve(t, eps);
}
double solve(double x, double epsilon)
{
return sampleCurveY(solveCurveX(x, epsilon));
}
复制代码
这里的solveCurveX
方法是求解三次贝塞尔曲线方程的系数t,然后根据系数t,调用sampleCurveY
方法计算时间点对应下的属性值。
3. 暂停动画
- stop:通过该方法暂停动画的执行
void stop(bool removing, bool done) {
if (active)
{
//更新进度值
if (done) {
delegateProgress();
}
if (removing) {
active = false;
}
handleDidStop(done);
} else {
if (!isStarted()) {
handleDidStart();
handleDidStop(false);
}
}
setPaused(true);
}
复制代码
三、总结
本小节主要讲解了POPAnimationState在动画不同状态下的操作,主要关注advance方法,该方法通过不同的动画类型来更新动画的属性值。本文只是简单分析了POPBasicAnimation计算变化值的方式,至于POPSpringAnimation和POPDecayAnimation也是类似,只不过所采用的计算方式不同。