FaceBook POP原始碼解析四
上一章節介紹了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也是類似,只不過所採用的計算方式不同。