blob: 682a34a5b8c2ee63d460fa05c3802b4c8261495e [file] [log] [blame]
/*
* 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