FaceBook POP源码解析四

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源码解析一

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也是类似,只不过所采用的计算方式不同。

猜你喜欢

转载自juejin.im/post/5c8a49b3f265da2d9d1cefe8