Пример #1
0
void
ElementTransitions::EnsureStyleRuleFor(TimeStamp aRefreshTime)
{
  if (!mStyleRule || mStyleRuleRefreshTime != aRefreshTime) {
    mStyleRule = new css::AnimValuesStyleRule();
    mStyleRuleRefreshTime = aRefreshTime;

    for (PRUint32 i = 0, i_end = mPropertyTransitions.Length(); i < i_end; ++i)
    {
      ElementPropertyTransition &pt = mPropertyTransitions[i];
      if (pt.IsRemovedSentinel()) {
        continue;
      }

      nsStyleAnimation::Value *val = mStyleRule->AddEmptyValue(pt.mProperty);

      double valuePortion = pt.ValuePortionFor(aRefreshTime);
#ifdef DEBUG
      bool ok =
#endif
        nsStyleAnimation::Interpolate(pt.mProperty,
                                      pt.mStartValue, pt.mEndValue,
                                      valuePortion, *val);
      NS_ABORT_IF_FALSE(ok, "could not interpolate values");
    }
  }
}
Пример #2
0
void
Animation::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
                        nsCSSPropertySet& aSetProperties)
{
  ComputedTiming computedTiming = GetComputedTiming();

  // If the time fraction is null, we don't have fill data for the current
  // time so we shouldn't animate.
  if (computedTiming.mTimeFraction == ComputedTiming::kNullTimeFraction) {
    return;
  }

  MOZ_ASSERT(0.0 <= computedTiming.mTimeFraction &&
             computedTiming.mTimeFraction <= 1.0,
             "timing fraction should be in [0-1]");

  for (size_t propIdx = 0, propEnd = mProperties.Length();
       propIdx != propEnd; ++propIdx)
  {
    const AnimationProperty& prop = mProperties[propIdx];

    MOZ_ASSERT(prop.mSegments[0].mFromKey == 0.0, "incorrect first from key");
    MOZ_ASSERT(prop.mSegments[prop.mSegments.Length() - 1].mToKey == 1.0,
               "incorrect last to key");

    if (aSetProperties.HasProperty(prop.mProperty)) {
      // Animations are composed by AnimationPlayerCollection by iterating
      // from the last animation to first. For animations targetting the
      // same property, the later one wins. So if this property is already set,
      // we should not override it.
      return;
    }

    aSetProperties.AddProperty(prop.mProperty);

    MOZ_ASSERT(prop.mSegments.Length() > 0,
               "property should not be in animations if it has no segments");

    // FIXME: Maybe cache the current segment?
    const AnimationPropertySegment *segment = prop.mSegments.Elements(),
                                *segmentEnd = segment + prop.mSegments.Length();
    while (segment->mToKey < computedTiming.mTimeFraction) {
      MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys");
      ++segment;
      if (segment == segmentEnd) {
        MOZ_ASSERT_UNREACHABLE("incorrect time fraction");
        break; // in order to continue in outer loop (just below)
      }
      MOZ_ASSERT(segment->mFromKey == (segment-1)->mToKey, "incorrect keys");
    }
    if (segment == segmentEnd) {
      continue;
    }
    MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys");
    MOZ_ASSERT(segment >= prop.mSegments.Elements() &&
               size_t(segment - prop.mSegments.Elements()) <
                 prop.mSegments.Length(),
               "out of array bounds");

    if (!aStyleRule) {
      // Allocate the style rule now that we know we have animation data.
      aStyleRule = new css::AnimValuesStyleRule();
    }

    double positionInSegment =
      (computedTiming.mTimeFraction - segment->mFromKey) /
      (segment->mToKey - segment->mFromKey);
    double valuePosition =
      segment->mTimingFunction.GetValue(positionInSegment);

    StyleAnimationValue *val = aStyleRule->AddEmptyValue(prop.mProperty);

#ifdef DEBUG
    bool result =
#endif
      StyleAnimationValue::Interpolate(prop.mProperty,
                                       segment->mFromValue,
                                       segment->mToValue,
                                       valuePosition, *val);
    MOZ_ASSERT(result, "interpolate must succeed now");
  }
}
Пример #3
0
void
ElementAnimations::EnsureStyleRuleFor(TimeStamp aRefreshTime,
                                      EventArray& aEventsToDispatch)
{
  if (!mNeedsRefreshes) {
    // All of our animations are paused or completed.
    mStyleRuleRefreshTime = aRefreshTime;
    return;
  }

  // mStyleRule may be null and valid, if we have no style to apply.
  if (mStyleRuleRefreshTime.IsNull() ||
      mStyleRuleRefreshTime != aRefreshTime) {
    mStyleRuleRefreshTime = aRefreshTime;
    mStyleRule = nsnull;
    // We'll set mNeedsRefreshes to true below in all cases where we need them.
    mNeedsRefreshes = false;

    // FIXME(spec): assume that properties in higher animations override
    // those in lower ones.
    // Therefore, we iterate from last animation to first.
    nsCSSPropertySet properties;

    for (PRUint32 animIdx = mAnimations.Length(); animIdx-- != 0; ) {
      ElementAnimation &anim = mAnimations[animIdx];

      if (anim.mProperties.Length() == 0 ||
          anim.mIterationDuration.ToMilliseconds() <= 0.0) {
        // No animation data.
        continue;
      }

      TimeDuration currentTimeDuration;
      if (anim.IsPaused()) {
        // FIXME: avoid recalculating every time
        currentTimeDuration = anim.mPauseStart - anim.mStartTime;
      } else {
        currentTimeDuration = aRefreshTime - anim.mStartTime;
      }

      // Set |currentIterationCount| to the (fractional) number of
      // iterations we've completed up to the current position.
      double currentIterationCount =
        currentTimeDuration / anim.mIterationDuration;
      bool dispatchStartOrIteration = false;
      if (currentIterationCount >= double(anim.mIterationCount)) {
        // Dispatch 'animationend' when needed.
        if (IsForElement() && 
            anim.mLastNotification !=
              ElementAnimation::LAST_NOTIFICATION_END) {
          anim.mLastNotification = ElementAnimation::LAST_NOTIFICATION_END;
          AnimationEventInfo ei(mElement, anim.mName, NS_ANIMATION_END,
                                currentTimeDuration);
          aEventsToDispatch.AppendElement(ei);
        }

        if (!anim.FillsForwards()) {
          // No animation data.
          continue;
        }
        currentIterationCount = double(anim.mIterationCount);
      } else {
        if (!anim.IsPaused()) {
          mNeedsRefreshes = true;
        }
        if (currentIterationCount < 0.0) {
          if (!anim.FillsBackwards()) {
            // No animation data.
            continue;
          }
          currentIterationCount = 0.0;
        } else {
          dispatchStartOrIteration = !anim.IsPaused();
        }
      }

      // Set |positionInIteration| to the position from 0% to 100% along
      // the keyframes.
      NS_ABORT_IF_FALSE(currentIterationCount >= 0.0, "must be positive");
      PRUint32 whichIteration = int(currentIterationCount);
      if (whichIteration == anim.mIterationCount && whichIteration != 0) {
        // When the animation's iteration count is an integer (as it
        // normally is), we need to end at 100% of its last iteration
        // rather than 0% of the next one (unless it's zero).
        --whichIteration;
      }
      double positionInIteration =
        currentIterationCount - double(whichIteration);
      bool thisIterationReverse = false;
      switch (anim.mDirection) {
        case NS_STYLE_ANIMATION_DIRECTION_NORMAL:
          thisIterationReverse = false;
          break;
        case NS_STYLE_ANIMATION_DIRECTION_REVERSE:
          thisIterationReverse = true;
          break;
        case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE:
          thisIterationReverse = (whichIteration & 1) == 1;
          break;
        case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE_REVERSE:
          thisIterationReverse = (whichIteration & 1) == 0;
          break;
      }
      if (thisIterationReverse) {
        positionInIteration = 1.0 - positionInIteration;
      }

      // Dispatch 'animationstart' or 'animationiteration' when needed.
      if (IsForElement() && dispatchStartOrIteration &&
          whichIteration != anim.mLastNotification) {
        // Notify 'animationstart' even if a negative delay puts us
        // past the first iteration.
        // Note that when somebody changes the animation-duration
        // dynamically, this will fire an extra iteration event
        // immediately in many cases.  It's not clear to me if that's the
        // right thing to do.
        PRUint32 message =
          anim.mLastNotification == ElementAnimation::LAST_NOTIFICATION_NONE
            ? NS_ANIMATION_START : NS_ANIMATION_ITERATION;
        anim.mLastNotification = whichIteration;
        AnimationEventInfo ei(mElement, anim.mName, message,
                              currentTimeDuration);
        aEventsToDispatch.AppendElement(ei);
      }

      NS_ABORT_IF_FALSE(0.0 <= positionInIteration &&
                          positionInIteration <= 1.0,
                        "position should be in [0-1]");

      for (PRUint32 propIdx = 0, propEnd = anim.mProperties.Length();
           propIdx != propEnd; ++propIdx)
      {
        const AnimationProperty &prop = anim.mProperties[propIdx];

        NS_ABORT_IF_FALSE(prop.mSegments[0].mFromKey == 0.0,
                          "incorrect first from key");
        NS_ABORT_IF_FALSE(prop.mSegments[prop.mSegments.Length() - 1].mToKey
                            == 1.0,
                          "incorrect last to key");

        if (properties.HasProperty(prop.mProperty)) {
          // A later animation already set this property.
          continue;
        }
        properties.AddProperty(prop.mProperty);

        NS_ABORT_IF_FALSE(prop.mSegments.Length() > 0,
                          "property should not be in animations if it "
                          "has no segments");

        // FIXME: Maybe cache the current segment?
        const AnimationPropertySegment *segment = prop.mSegments.Elements();
        while (segment->mToKey < positionInIteration) {
          NS_ABORT_IF_FALSE(segment->mFromKey < segment->mToKey,
                            "incorrect keys");
          ++segment;
          NS_ABORT_IF_FALSE(segment->mFromKey == (segment-1)->mToKey,
                            "incorrect keys");
        }
        NS_ABORT_IF_FALSE(segment->mFromKey < segment->mToKey,
                          "incorrect keys");
        NS_ABORT_IF_FALSE(segment - prop.mSegments.Elements() <
                            prop.mSegments.Length(),
                          "ran off end");

        if (!mStyleRule) {
          // Allocate the style rule now that we know we have animation data.
          mStyleRule = new css::AnimValuesStyleRule();
        }

        double positionInSegment = (positionInIteration - segment->mFromKey) /
                                   (segment->mToKey - segment->mFromKey);
        double valuePosition =
          segment->mTimingFunction.GetValue(positionInSegment);

        nsStyleAnimation::Value *val =
          mStyleRule->AddEmptyValue(prop.mProperty);

#ifdef DEBUG
        bool result =
#endif
          nsStyleAnimation::Interpolate(prop.mProperty,
                                        segment->mFromValue, segment->mToValue,
                                        valuePosition, *val);
        NS_ABORT_IF_FALSE(result, "interpolate must succeed now");
      }
    }
  }
}