一、前言
上一章節介紹了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也是類似,只不過所採用的計算方式不同。