FaceBook POP原始碼解析四

醬了裡個醬發表於2019-03-14

FaceBook POP原始碼解析一

FaceBook POP原始碼解析二

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方法計算時間點對應下的屬性值。

FaceBook POP原始碼解析四
至於後面的具體求解過程可以參考: FaceBook POP原始碼解析一

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也是類似,只不過所採用的計算方式不同。

相關文章