double AnimationBase::getElapsedTime() const { #if ENABLE(CSS_ANIMATIONS_LEVEL_2) if (m_animation->trigger() && m_animation->trigger()->isScrollAnimationTrigger()) { ScrollAnimationTrigger& scrollTrigger = downcast<ScrollAnimationTrigger>(*m_animation->trigger().get()); if (scrollTrigger.hasEndValue() && m_object) { float offset = m_compositeAnimation->animationController().scrollPosition(); float startValue = scrollTrigger.startValue().value(); if (offset < startValue) return 0; float endValue = scrollTrigger.endValue().value(); if (offset > endValue) return m_animation->duration(); return m_animation->duration() * (offset - startValue) / (endValue - startValue); } } #endif if (paused()) return m_pauseTime - m_startTime; if (m_startTime <= 0) return 0; if (postActive() || fillingForwards()) return m_totalDuration; return beginAnimationUpdateTime() - m_startTime; }
void ImplicitAnimation::animate(CompositeAnimation*, RenderObject*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) { // If we get this far and the animation is done, it means we are cleaning up a just finished animation. // So just return. Everything is already all cleaned up. if (postActive()) return; // Reset to start the transition if we are new if (isNew()) reset(targetStyle); // Run a cycle of animation. // We know we will need a new render style, so make one if needed if (!animatedStyle) animatedStyle = RenderStyle::clone(targetStyle); bool needsAnim = CSSPropertyAnimation::blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0)); // FIXME: we also need to detect cases where we have to software animate for other reasons, // such as a child using inheriting the transform. https://bugs.webkit.org/show_bug.cgi?id=23902 if (needsAnim) setAnimating(); else { #if USE(ACCELERATED_COMPOSITING) // If we are running an accelerated animation, set a flag in the style which causes the style // to compare as different to any other style. This ensures that changes to the property // that is animating are correctly detected during the animation (e.g. when a transition // gets interrupted). animatedStyle->setIsRunningAcceleratedAnimation(); #endif } // Fire the start timeout if needed fireAnimationEventsIfNeeded(); }
void KeyframeAnimation::animate(CompositeAnimation*, RenderObject*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) { // Fire the start timeout if needed fireAnimationEventsIfNeeded(); // If we have not yet started, we will not have a valid start time, so just start the animation if needed. if (isNew() && m_animation->playState() == AnimPlayStatePlaying) updateStateMachine(AnimationStateInputStartAnimation, -1); // If we get this far and the animation is done, it means we are cleaning up a just finished animation. // If so, we need to send back the targetStyle. if (postActive()) { if (!animatedStyle) animatedStyle = const_cast<RenderStyle*>(targetStyle); return; } // If we are waiting for the start timer, we don't want to change the style yet. // Special case 1 - if the delay time is 0, then we do want to set the first frame of the // animation right away. This avoids a flash when the animation starts. // Special case 2 - if there is a backwards fill mode, then we want to continue // through to the style blend so that we get the fromStyle. if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards()) return; // FIXME: we need to be more efficient about determining which keyframes we are animating between. // We should cache the last pair or something. // Get the from/to styles and progress between const RenderStyle* fromStyle = 0; const RenderStyle* toStyle = 0; double progress; getKeyframeAnimationInterval(fromStyle, toStyle, progress); // If either style is 0 we have an invalid case, just stop the animation. if (!fromStyle || !toStyle) { updateStateMachine(AnimationStateInputEndAnimation, -1); return; } // Run a cycle of animation. // We know we will need a new render style, so make one if needed. if (!animatedStyle) animatedStyle = RenderStyle::clone(targetStyle); HashSet<int>::const_iterator endProperties = m_keyframes.endProperties(); for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { bool needsAnim = blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, progress); if (needsAnim) setAnimating(); else { #if USE(ACCELERATED_COMPOSITING) // If we are running an accelerated animation, set a flag in the style // to indicate it. This can be used to make sure we get an updated // style for hit testing, etc. animatedStyle->setIsRunningAcceleratedAnimation(); #endif } } }
void AnimationBase::getTimeToNextEvent(double& time, bool& isLooping) const { if (postActive()) { time = -1; isLooping = false; return; } // Decide when the end or loop event needs to fire const double elapsedDuration = getElapsedTime(); double durationLeft = 0; double nextIterationTime = m_totalDuration; if (m_totalDuration < 0 || elapsedDuration < m_totalDuration) { durationLeft = m_animation->duration() > 0 ? (m_animation->duration() - fmod(elapsedDuration, m_animation->duration())) : 0; nextIterationTime = elapsedDuration + durationLeft; } if (m_totalDuration < 0 || nextIterationTime < m_totalDuration) { // We are not at the end yet ASSERT(nextIterationTime > 0); isLooping = true; } else { // We are at the end isLooping = false; } time = durationLeft; }
double AnimationBase::progress(double scale, double offset, const TimingFunction* timingFunction) const { if (preActive()) return 0; double dur = m_animation->duration(); if (m_animation->iterationCount() > 0) dur *= m_animation->iterationCount(); if (postActive() || !m_animation->duration()) return 1.0; double elapsedTime = getElapsedTime(); if (m_animation->iterationCount() > 0 && elapsedTime >= dur) { const int integralIterationCount = static_cast<int>(m_animation->iterationCount()); const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount; return (integralIterationCount % 2 || iterationCountHasFractional) ? 1.0 : 0.0; } const double fractionalTime = this->fractionalTime(scale, elapsedTime, offset); if (!timingFunction) timingFunction = m_animation->timingFunction(); return timingFunction->evaluate(fractionalTime, accuracyForDuration(m_animation->duration())); }
bool KeyframeAnimation::animate(CompositeAnimation* compositeAnimation, RenderElement*, const RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) { // Fire the start timeout if needed fireAnimationEventsIfNeeded(); // If we have not yet started, we will not have a valid start time, so just start the animation if needed. if (isNew() && m_animation->playState() == AnimPlayStatePlaying && !compositeAnimation->isSuspended()) updateStateMachine(AnimationStateInput::StartAnimation, -1); // If we get this far and the animation is done, it means we are cleaning up a just finished animation. // If so, we need to send back the targetStyle. if (postActive()) { if (!animatedStyle) animatedStyle = const_cast<RenderStyle*>(targetStyle); return false; } // If we are waiting for the start timer, we don't want to change the style yet. // Special case 1 - if the delay time is 0, then we do want to set the first frame of the // animation right away. This avoids a flash when the animation starts. // Special case 2 - if there is a backwards fill mode, then we want to continue // through to the style blend so that we get the fromStyle. if (waitingToStart() && m_animation->delay() > 0 && !m_animation->fillsBackwards()) return false; // If we have no keyframes, don't animate. if (!m_keyframes.size()) { updateStateMachine(AnimationStateInput::EndAnimation, -1); return false; } AnimationState oldState = state(); // Run a cycle of animation. // We know we will need a new render style, so make one if needed. if (!animatedStyle) animatedStyle = RenderStyle::clone(targetStyle); // FIXME: we need to be more efficient about determining which keyframes we are animating between. // We should cache the last pair or something. for (auto propertyID : m_keyframes.properties()) { // Get the from/to styles and progress between const RenderStyle* fromStyle = nullptr; const RenderStyle* toStyle = nullptr; double progress = 0; fetchIntervalEndpointsForProperty(propertyID, fromStyle, toStyle, progress); bool needsAnim = CSSPropertyAnimation::blendProperties(this, propertyID, animatedStyle.get(), fromStyle, toStyle, progress); if (!needsAnim) // If we are running an accelerated animation, set a flag in the style // to indicate it. This can be used to make sure we get an updated // style for hit testing, etc. // FIXME: still need this? animatedStyle->setIsRunningAcceleratedAnimation(); } return state() != oldState; }
double AnimationBase::getElapsedTime() const { if (paused()) return m_pauseTime - m_startTime; if (m_startTime <= 0) return 0; if (postActive()) return 1; return beginAnimationUpdateTime() - m_startTime; }
double AnimationBase::getElapsedTime() const { ASSERT(!postActive()); if (paused()) return m_pauseTime - m_startTime; if (m_startTime <= 0) return 0; double elapsedTime = beginAnimationUpdateTime() - m_startTime; // It's possible for the start time to be ahead of the last update time // if the compositor has just sent notification for the start of an // accelerated animation. return max(elapsedTime, 0.0); }
double AnimationBase::progress(double scale, double offset, const TimingFunction* timingFunction) const { if (preActive()) return 0; if (postActive()) return 1; double elapsedTime = getElapsedTime(); double duration = m_animation->duration(); if (m_animation->iterationCount() > 0) duration *= m_animation->iterationCount(); if (fillingForwards()) elapsedTime = duration; double fractionalTime = this->fractionalTime(scale, elapsedTime, offset); if (m_animation->iterationCount() > 0 && elapsedTime >= duration) { if (WTF::isIntegral(fractionalTime)) return fractionalTime; } if (!timingFunction) timingFunction = m_animation->timingFunction().get(); switch (timingFunction->type()) { case TimingFunction::CubicBezierFunction: { const CubicBezierTimingFunction* function = static_cast<const CubicBezierTimingFunction*>(timingFunction); return solveCubicBezierFunction(function->x1(), function->y1(), function->x2(), function->y2(), fractionalTime, m_animation->duration()); } case TimingFunction::StepsFunction: { const StepsTimingFunction* stepsTimingFunction = static_cast<const StepsTimingFunction*>(timingFunction); return solveStepsFunction(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtStart(), fractionalTime); } case TimingFunction::LinearFunction: return fractionalTime; } ASSERT_NOT_REACHED(); return 0; }
double AnimationBase::progress(double scale, double offset, const TimingFunction* tf) const { if (preActive()) return 0; double elapsedTime = getElapsedTime(); double dur = m_animation->duration(); if (m_animation->iterationCount() > 0) dur *= m_animation->iterationCount(); if (postActive() || !m_animation->duration()) return 1.0; if (m_animation->iterationCount() > 0 && elapsedTime >= dur) { const int integralIterationCount = static_cast<int>(m_animation->iterationCount()); const bool iterationCountHasFractional = m_animation->iterationCount() - integralIterationCount; return (integralIterationCount % 2 || iterationCountHasFractional) ? 1.0 : 0.0; } const double fractionalTime = this->fractionalTime(scale, elapsedTime, offset); if (!tf) tf = m_animation->timingFunction().get(); switch (tf->type()) { case TimingFunction::CubicBezierFunction: { const CubicBezierTimingFunction* function = static_cast<const CubicBezierTimingFunction*>(tf); return solveCubicBezierFunction(function->x1(), function->y1(), function->x2(), function->y2(), fractionalTime, m_animation->duration()); } case TimingFunction::StepsFunction: { const StepsTimingFunction* stepsTimingFunction = static_cast<const StepsTimingFunction*>(tf); return solveStepsFunction(stepsTimingFunction->numberOfSteps(), stepsTimingFunction->stepAtStart(), fractionalTime); } case TimingFunction::LinearFunction: return fractionalTime; } ASSERT_NOT_REACHED(); return 0; }
void ImplicitAnimation::animate(CompositeAnimation*, RenderObject*, RenderStyle*, RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) { // If we get this far and the animation is done, it means we are cleaning up a just finished animation. // So just return. Everything is already all cleaned up. if (postActive()) return; // Reset to start the transition if we are new if (isNew()) reset(targetStyle); // Run a cycle of animation. // We know we will need a new render style, so make one if needed if (!animatedStyle) animatedStyle = RenderStyle::clone(targetStyle); if (blendProperties(this, m_animatingProperty, animatedStyle.get(), m_fromStyle.get(), m_toStyle.get(), progress(1, 0, 0))) setAnimating(); // Fire the start timeout if needed fireAnimationEventsIfNeeded(); }
KeyframeAnimation::~KeyframeAnimation() { // Make sure to tell the renderer that we are ending. This will make sure any accelerated animations are removed. if (!postActive()) endAnimation(true); }
ImplicitAnimation::~ImplicitAnimation() { // Do the cleanup here instead of in the base class so the specialized methods get called if (!postActive()) updateStateMachine(AnimationStateInputEndAnimation, -1); }
void KeyframeAnimation::animate(CompositeAnimation* animation, RenderObject* renderer, const RenderStyle* currentStyle, const RenderStyle* targetStyle, RefPtr<RenderStyle>& animatedStyle) { // If we have not yet started, we will not have a valid start time, so just start the animation if needed. if (isNew() && m_animation->playState() == AnimPlayStatePlaying) updateStateMachine(AnimationStateInputStartAnimation, -1); // If we get this far and the animation is done, it means we are cleaning up a just finished animation. // If so, we need to send back the targetStyle. if (postActive()) { if (!animatedStyle) animatedStyle = const_cast<RenderStyle*>(targetStyle); return; } // If we are waiting for the start timer, we don't want to change the style yet. // Special case - if the delay time is 0, then we do want to set the first frame of the // animation right away. This avoids a flash when the animation starts. if (waitingToStart() && m_animation->delay() > 0) return; // FIXME: we need to be more efficient about determining which keyframes we are animating between. // We should cache the last pair or something. // Find the first key double elapsedTime = (m_startTime > 0) ? ((!paused() ? currentTime() : m_pauseTime) - m_startTime) : 0; if (elapsedTime < 0) elapsedTime = 0; double t = m_animation->duration() ? (elapsedTime / m_animation->duration()) : 1; int i = static_cast<int>(t); t -= i; if (m_animation->direction() && (i & 1)) t = 1 - t; const RenderStyle* fromStyle = 0; const RenderStyle* toStyle = 0; double scale = 1; double offset = 0; Vector<KeyframeValue>::const_iterator endKeyframes = m_keyframes.endKeyframes(); for (Vector<KeyframeValue>::const_iterator it = m_keyframes.beginKeyframes(); it != endKeyframes; ++it) { if (t < it->key()) { // The first key should always be 0, so we should never succeed on the first key if (!fromStyle) break; scale = 1.0 / (it->key() - offset); toStyle = it->style(); break; } offset = it->key(); fromStyle = it->style(); } // If either style is 0 we have an invalid case, just stop the animation. if (!fromStyle || !toStyle) { updateStateMachine(AnimationStateInputEndAnimation, -1); return; } // Run a cycle of animation. // We know we will need a new render style, so make one if needed. if (!animatedStyle) animatedStyle = RenderStyle::clone(targetStyle); const TimingFunction* timingFunction = 0; if (fromStyle->animations() && fromStyle->animations()->size() > 0) timingFunction = &(fromStyle->animations()->animation(0)->timingFunction()); double prog = progress(scale, offset, timingFunction); HashSet<int>::const_iterator endProperties = m_keyframes.endProperties(); for (HashSet<int>::const_iterator it = m_keyframes.beginProperties(); it != endProperties; ++it) { if (blendProperties(this, *it, animatedStyle.get(), fromStyle, toStyle, prog)) setAnimating(); } }