| /* |
| * Copyright (C) 2017-2018 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "AnimationEffectReadOnly.h" |
| |
| #include "AnimationEffectTimingReadOnly.h" |
| #include "FillMode.h" |
| #include "JSComputedTimingProperties.h" |
| #include "WebAnimationUtilities.h" |
| |
| namespace WebCore { |
| |
| AnimationEffectReadOnly::AnimationEffectReadOnly(ClassType classType, Ref<AnimationEffectTimingReadOnly>&& timing) |
| : m_classType(classType) |
| , m_timing(WTFMove(timing)) |
| { |
| m_timing->setEffect(this); |
| } |
| |
| AnimationEffectReadOnly::~AnimationEffectReadOnly() |
| { |
| m_timing->setEffect(nullptr); |
| } |
| |
| void AnimationEffectReadOnly::timingDidChange() |
| { |
| if (m_animation) |
| m_animation->timingModelDidChange(); |
| } |
| |
| std::optional<Seconds> AnimationEffectReadOnly::localTime() const |
| { |
| if (m_animation) |
| return m_animation->currentTime(); |
| return std::nullopt; |
| } |
| |
| auto AnimationEffectReadOnly::phase() const -> Phase |
| { |
| // 3.5.5. Animation effect phases and states |
| // https://drafts.csswg.org/web-animations-1/#animation-effect-phases-and-states |
| |
| bool animationIsBackwards = m_animation && m_animation->playbackRate() < 0; |
| auto beforeActiveBoundaryTime = std::max(std::min(m_timing->delay(), m_timing->endTime()), 0_s); |
| auto activeAfterBoundaryTime = std::max(std::min(m_timing->delay() + m_timing->activeDuration(), m_timing->endTime()), 0_s); |
| |
| // (This should be the last statement, but it's more efficient to cache the local time and return right away if it's not resolved.) |
| // Furthermore, it is often convenient to refer to the case when an animation effect is in none of the above phases |
| // as being in the idle phase. |
| auto effectLocalTime = localTime(); |
| if (!effectLocalTime) |
| return Phase::Idle; |
| |
| auto localTimeValue = effectLocalTime.value(); |
| |
| // An animation effect is in the before phase if the animation effect’s local time is not unresolved and |
| // either of the following conditions are met: |
| // 1. the local time is less than the before-active boundary time, or |
| // 2. the animation direction is ‘backwards’ and the local time is equal to the before-active boundary time. |
| if ((localTimeValue + timeEpsilon) < beforeActiveBoundaryTime || (animationIsBackwards && std::abs(localTimeValue.microseconds() - beforeActiveBoundaryTime.microseconds()) < timeEpsilon.microseconds())) |
| return Phase::Before; |
| |
| // An animation effect is in the after phase if the animation effect’s local time is not unresolved and |
| // either of the following conditions are met: |
| // 1. the local time is greater than the active-after boundary time, or |
| // 2. the animation direction is ‘forwards’ and the local time is equal to the active-after boundary time. |
| if ((localTimeValue - timeEpsilon) > activeAfterBoundaryTime || (!animationIsBackwards && std::abs(localTimeValue.microseconds() - activeAfterBoundaryTime.microseconds()) < timeEpsilon.microseconds())) |
| return Phase::After; |
| |
| // An animation effect is in the active phase if the animation effect’s local time is not unresolved and it is not |
| // in either the before phase nor the after phase. |
| // (No need to check, we've already established that local time was resolved). |
| return Phase::Active; |
| } |
| |
| std::optional<Seconds> AnimationEffectReadOnly::activeTime() const |
| { |
| // 3.8.3.1. Calculating the active time |
| // https://drafts.csswg.org/web-animations-1/#calculating-the-active-time |
| |
| // The active time is based on the local time and start delay. However, it is only defined |
| // when the animation effect should produce an output and hence depends on its fill mode |
| // and phase as follows, |
| |
| auto effectPhase = phase(); |
| |
| // If the animation effect is in the before phase, the result depends on the first matching |
| // condition from the following, |
| if (effectPhase == Phase::Before) { |
| // If the fill mode is backwards or both, return the result of evaluating |
| // max(local time - start delay, 0). |
| if (m_timing->fill() == FillMode::Backwards || m_timing->fill() == FillMode::Both) |
| return std::max(localTime().value() - m_timing->delay(), 0_s); |
| // Otherwise, return an unresolved time value. |
| return std::nullopt; |
| } |
| |
| // If the animation effect is in the active phase, return the result of evaluating local time - start delay. |
| if (effectPhase == Phase::Active) |
| return localTime().value() - m_timing->delay(); |
| |
| // If the animation effect is in the after phase, the result depends on the first matching |
| // condition from the following, |
| if (effectPhase == Phase::After) { |
| // If the fill mode is forwards or both, return the result of evaluating |
| // max(min(local time - start delay, active duration), 0). |
| if (m_timing->fill() == FillMode::Forwards || m_timing->fill() == FillMode::Both) |
| return std::max(std::min(localTime().value() - m_timing->delay(), m_timing->activeDuration()), 0_s); |
| // Otherwise, return an unresolved time value. |
| return std::nullopt; |
| } |
| |
| // Otherwise (the local time is unresolved), return an unresolved time value. |
| return std::nullopt; |
| } |
| |
| std::optional<double> AnimationEffectReadOnly::overallProgress() const |
| { |
| // 3.8.3.2. Calculating the overall progress |
| // https://drafts.csswg.org/web-animations-1/#calculating-the-overall-progress |
| |
| // The overall progress describes the number of iterations that have completed (including partial iterations) and is defined as follows: |
| |
| // 1. If the active time is unresolved, return unresolved. |
| auto effectActiveTime = activeTime(); |
| if (!effectActiveTime) |
| return std::nullopt; |
| |
| // 2. Calculate an initial value for overall progress based on the first matching condition from below, |
| double overallProgress; |
| |
| if (!m_timing->iterationDuration()) { |
| // If the iteration duration is zero, if the animation effect is in the before phase, let overall progress be zero, |
| // otherwise, let it be equal to the iteration count. |
| overallProgress = phase() == Phase::Before ? 0 : m_timing->iterations(); |
| } else { |
| // Otherwise, let overall progress be the result of calculating active time / iteration duration. |
| overallProgress = secondsToWebAnimationsAPITime(effectActiveTime.value()) / secondsToWebAnimationsAPITime(m_timing->iterationDuration()); |
| } |
| |
| // 3. Return the result of calculating overall progress + iteration start. |
| overallProgress += m_timing->iterationStart(); |
| return std::abs(overallProgress); |
| } |
| |
| std::optional<double> AnimationEffectReadOnly::simpleIterationProgress() const |
| { |
| // 3.8.3.3. Calculating the simple iteration progress |
| // https://drafts.csswg.org/web-animations-1/#calculating-the-simple-iteration-progress |
| |
| // The simple iteration progress is a fraction of the progress through the current iteration that |
| // ignores transformations to the time introduced by the playback direction or timing functions |
| // applied to the effect, and is calculated as follows: |
| |
| // 1. If the overall progress is unresolved, return unresolved. |
| auto effectOverallProgress = overallProgress(); |
| if (!effectOverallProgress) |
| return std::nullopt; |
| |
| // 2. If overall progress is infinity, let the simple iteration progress be iteration start % 1.0, |
| // otherwise, let the simple iteration progress be overall progress % 1.0. |
| double overallProgressValue = effectOverallProgress.value(); |
| double simpleIterationProgress = std::isinf(overallProgressValue) ? fmod(m_timing->iterationStart(), 1) : fmod(overallProgressValue, 1); |
| |
| // 3. If all of the following conditions are true, |
| // |
| // the simple iteration progress calculated above is zero, and |
| // the animation effect is in the active phase or the after phase, and |
| // the active time is equal to the active duration, and |
| // the iteration count is not equal to zero. |
| // let the simple iteration progress be 1.0. |
| auto effectPhase = phase(); |
| if (!simpleIterationProgress && (effectPhase == Phase::Active || effectPhase == Phase::After) && std::abs(activeTime().value().microseconds() - m_timing->activeDuration().microseconds()) < timeEpsilon.microseconds() && m_timing->iterations()) |
| return 1; |
| |
| return simpleIterationProgress; |
| } |
| |
| std::optional<double> AnimationEffectReadOnly::currentIteration() const |
| { |
| // 3.8.4. Calculating the current iteration |
| // https://drafts.csswg.org/web-animations-1/#calculating-the-current-iteration |
| |
| // The current iteration can be calculated using the following steps: |
| |
| // 1. If the active time is unresolved, return unresolved. |
| if (!activeTime()) |
| return std::nullopt; |
| |
| // 2. If the animation effect is in the after phase and the iteration count is infinity, return infinity. |
| if (phase() == Phase::After && std::isinf(m_timing->iterations())) |
| return std::numeric_limits<double>::infinity(); |
| |
| // 3. If the simple iteration progress is 1.0, return floor(overall progress) - 1. |
| if (simpleIterationProgress().value() == 1) |
| return floor(overallProgress().value()) - 1; |
| |
| // 4. Otherwise, return floor(overall progress). |
| return floor(overallProgress().value()); |
| } |
| |
| AnimationEffectReadOnly::ComputedDirection AnimationEffectReadOnly::currentDirection() const |
| { |
| // 3.9.1. Calculating the directed progress |
| // https://drafts.csswg.org/web-animations-1/#calculating-the-directed-progress |
| |
| // If playback direction is normal, let the current direction be forwards. |
| if (m_timing->direction() == PlaybackDirection::Normal) |
| return AnimationEffectReadOnly::ComputedDirection::Forwards; |
| |
| // If playback direction is reverse, let the current direction be reverse. |
| if (m_timing->direction() == PlaybackDirection::Reverse) |
| return AnimationEffectReadOnly::ComputedDirection::Reverse; |
| |
| // Otherwise, let d be the current iteration. |
| auto d = currentIteration().value(); |
| // If playback direction is alternate-reverse increment d by 1. |
| if (m_timing->direction() == PlaybackDirection::AlternateReverse) |
| d++; |
| // If d % 2 == 0, let the current direction be forwards, otherwise let the current direction be reverse. |
| // If d is infinity, let the current direction be forwards. |
| if (std::isinf(d) || !fmod(d, 2)) |
| return AnimationEffectReadOnly::ComputedDirection::Forwards; |
| return AnimationEffectReadOnly::ComputedDirection::Reverse; |
| } |
| |
| std::optional<double> AnimationEffectReadOnly::directedProgress() const |
| { |
| // 3.9.1. Calculating the directed progress |
| // https://drafts.csswg.org/web-animations-1/#calculating-the-directed-progress |
| |
| // The directed progress is calculated from the simple iteration progress using the following steps: |
| |
| // 1. If the simple iteration progress is unresolved, return unresolved. |
| auto effectSimpleIterationProgress = simpleIterationProgress(); |
| if (!effectSimpleIterationProgress) |
| return std::nullopt; |
| |
| // 2. Calculate the current direction (we implement this as a separate method). |
| |
| // 3. If the current direction is forwards then return the simple iteration progress. |
| if (currentDirection() == AnimationEffectReadOnly::ComputedDirection::Forwards) |
| return effectSimpleIterationProgress.value(); |
| |
| // Otherwise, return 1.0 - simple iteration progress. |
| return 1 - effectSimpleIterationProgress.value(); |
| } |
| |
| std::optional<double> AnimationEffectReadOnly::transformedProgress() const |
| { |
| // 3.10.1. Calculating the transformed progress |
| // https://drafts.csswg.org/web-animations-1/#calculating-the-transformed-progress |
| |
| // The transformed progress is calculated from the directed progress using the following steps: |
| // |
| // 1. If the directed progress is unresolved, return unresolved. |
| auto effectDirectedProgress = directedProgress(); |
| if (!effectDirectedProgress) |
| return std::nullopt; |
| |
| auto effectDirectedProgressValue = effectDirectedProgress.value(); |
| |
| if (auto iterationDuration = m_timing->iterationDuration().seconds()) { |
| bool before = false; |
| auto* timingFunction = m_timing->timingFunction(); |
| // 2. Calculate the value of the before flag as follows: |
| if (is<StepsTimingFunction>(timingFunction)) { |
| // 1. Determine the current direction using the procedure defined in §3.9.1 Calculating the directed progress. |
| // 2. If the current direction is forwards, let going forwards be true, otherwise it is false. |
| bool goingForwards = currentDirection() == AnimationEffectReadOnly::ComputedDirection::Forwards; |
| // 3. The before flag is set if the animation effect is in the before phase and going forwards is true; |
| // or if the animation effect is in the after phase and going forwards is false. |
| auto effectPhase = phase(); |
| before = (effectPhase == Phase::Before && goingForwards) || (effectPhase == Phase::After && !goingForwards); |
| } |
| |
| // 3. Return the result of evaluating the animation effect’s timing function passing directed progress as the |
| // input progress value and before flag as the before flag. |
| return timingFunction->transformTime(effectDirectedProgressValue, iterationDuration, before); |
| } |
| |
| return effectDirectedProgressValue; |
| } |
| |
| std::optional<double> AnimationEffectReadOnly::iterationProgress() const |
| { |
| return transformedProgress(); |
| } |
| |
| ComputedTimingProperties AnimationEffectReadOnly::getComputedTiming() |
| { |
| ComputedTimingProperties computedTiming; |
| computedTiming.delay = m_timing->bindingsDelay(); |
| computedTiming.endDelay = m_timing->bindingsEndDelay(); |
| auto fillMode = m_timing->fill(); |
| computedTiming.fill = fillMode == FillMode::Auto ? FillMode::None : fillMode; |
| computedTiming.iterationStart = m_timing->iterationStart(); |
| computedTiming.iterations = m_timing->iterations(); |
| computedTiming.duration = secondsToWebAnimationsAPITime(m_timing->iterationDuration()); |
| computedTiming.direction = m_timing->direction(); |
| computedTiming.easing = m_timing->easing(); |
| computedTiming.endTime = secondsToWebAnimationsAPITime(m_timing->endTime()); |
| computedTiming.activeDuration = secondsToWebAnimationsAPITime(m_timing->activeDuration()); |
| if (auto effectLocalTime = localTime()) |
| computedTiming.localTime = secondsToWebAnimationsAPITime(effectLocalTime.value()); |
| computedTiming.progress = iterationProgress(); |
| computedTiming.currentIteration = currentIteration(); |
| return computedTiming; |
| } |
| |
| } // namespace WebCore |