195 lines
6.1 KiB
C++
195 lines
6.1 KiB
C++
#include <reanimated/CSS/progress/AnimationProgressProvider.h>
|
|
|
|
#include <utility>
|
|
|
|
namespace reanimated::css {
|
|
|
|
AnimationProgressProvider::AnimationProgressProvider(
|
|
const double timestamp,
|
|
const double duration,
|
|
const double delay,
|
|
const double iterationCount,
|
|
const AnimationDirection direction,
|
|
EasingFunction easingFunction,
|
|
const std::shared_ptr<KeyframeEasingFunctions> &keyframeEasingFunctions)
|
|
: RawProgressProvider(timestamp, duration, delay),
|
|
iterationCount_(iterationCount),
|
|
direction_(direction),
|
|
easingFunction_(std::move(easingFunction)),
|
|
keyframeEasingFunctions_(keyframeEasingFunctions) {}
|
|
|
|
void AnimationProgressProvider::setIterationCount(double iterationCount) {
|
|
iterationCount_ = iterationCount;
|
|
}
|
|
|
|
void AnimationProgressProvider::setDirection(AnimationDirection direction) {
|
|
direction_ = direction;
|
|
}
|
|
|
|
void AnimationProgressProvider::setEasingFunction(
|
|
const EasingFunction &easingFunction) {
|
|
easingFunction_ = easingFunction;
|
|
}
|
|
|
|
AnimationDirection AnimationProgressProvider::getDirection() const {
|
|
return direction_;
|
|
}
|
|
|
|
double AnimationProgressProvider::getGlobalProgress() const {
|
|
return applyAnimationDirection(rawProgress_.value_or(0));
|
|
}
|
|
|
|
AnimationProgressState AnimationProgressProvider::getState(
|
|
const double timestamp) const {
|
|
if (shouldFinish(timestamp)) {
|
|
return AnimationProgressState::Finished;
|
|
}
|
|
if (pauseTimestamp_ > 0) {
|
|
return AnimationProgressState::Paused;
|
|
}
|
|
if (timestamp < getStartTimestamp(timestamp) || !rawProgress_.has_value()) {
|
|
return AnimationProgressState::Pending;
|
|
}
|
|
const auto rawProgress = rawProgress_.value();
|
|
if (rawProgress >= 1) {
|
|
return AnimationProgressState::Finished;
|
|
}
|
|
return AnimationProgressState::Running;
|
|
}
|
|
|
|
double AnimationProgressProvider::getPauseTimestamp() const {
|
|
return pauseTimestamp_;
|
|
}
|
|
|
|
double AnimationProgressProvider::getTotalPausedTime(
|
|
const double timestamp) const {
|
|
return pauseTimestamp_ > 0
|
|
? (totalPausedTime_ + (timestamp - pauseTimestamp_))
|
|
: totalPausedTime_;
|
|
}
|
|
|
|
double AnimationProgressProvider::getStartTimestamp(
|
|
const double timestamp) const {
|
|
// Start timestamp is the timestamp when the first animation keyframe
|
|
// should be applied (it depends on the animation delay and the total
|
|
// time when the animation was paused)
|
|
return creationTimestamp_ + delay_ + getTotalPausedTime(timestamp);
|
|
}
|
|
|
|
double AnimationProgressProvider::getKeyframeProgress(
|
|
const double fromOffset,
|
|
const double toOffset) const {
|
|
if (fromOffset == toOffset) {
|
|
return 1;
|
|
}
|
|
|
|
const auto keyframeProgress =
|
|
(getGlobalProgress() - fromOffset) / (toOffset - fromOffset);
|
|
|
|
// Use the overridden easing function if it was overridden for the
|
|
// current keyframe
|
|
const auto easingFunctionIt = keyframeEasingFunctions_->find(fromOffset);
|
|
if (easingFunctionIt != keyframeEasingFunctions_->end()) {
|
|
return easingFunctionIt->second(keyframeProgress);
|
|
}
|
|
|
|
return easingFunction_(keyframeProgress);
|
|
}
|
|
|
|
void AnimationProgressProvider::pause(const double timestamp) {
|
|
pauseTimestamp_ = timestamp;
|
|
}
|
|
|
|
void AnimationProgressProvider::play(const double timestamp) {
|
|
if (pauseTimestamp_ > 0) {
|
|
totalPausedTime_ += timestamp - pauseTimestamp_;
|
|
}
|
|
pauseTimestamp_ = 0;
|
|
}
|
|
|
|
void AnimationProgressProvider::resetProgress() {
|
|
RawProgressProvider::resetProgress();
|
|
currentIteration_ = 1;
|
|
previousIterationsDuration_ = 0;
|
|
}
|
|
|
|
bool AnimationProgressProvider::shouldFinish(const double timestamp) const {
|
|
if (iterationCount_ == 0) {
|
|
return true;
|
|
}
|
|
if (iterationCount_ == -1) {
|
|
return false;
|
|
}
|
|
const auto elapsedDuration = timestamp - getStartTimestamp(timestamp);
|
|
return elapsedDuration >= duration_ * iterationCount_;
|
|
}
|
|
|
|
std::optional<double> AnimationProgressProvider::calculateRawProgress(
|
|
const double timestamp) {
|
|
const double currentIterationElapsedTime = timestamp -
|
|
(creationTimestamp_ + delay_ + previousIterationsDuration_ +
|
|
getTotalPausedTime(timestamp));
|
|
|
|
if (currentIterationElapsedTime < 0) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
const double iterationProgress =
|
|
updateIterationProgress(currentIterationElapsedTime);
|
|
|
|
if (shouldFinish(timestamp)) {
|
|
// Override current progress for the last update in the last iteration to
|
|
// ensure that animation finishes exactly at the specified iteration
|
|
const double intPart = std::floor(iterationCount_);
|
|
return intPart == iterationCount_ ? 1 : iterationCount_ - intPart;
|
|
}
|
|
|
|
return iterationProgress;
|
|
}
|
|
|
|
double AnimationProgressProvider::updateIterationProgress(
|
|
const double currentIterationElapsedTime) {
|
|
if (duration_ == 0) {
|
|
return 1;
|
|
}
|
|
// We can increase curentIteration by more than just one iteration if the
|
|
// animation delay is negative, thus we are using this division to get the
|
|
// number of iterations that have passed since the previous animation update
|
|
// (deltaIterations can be greater than for the first update of the
|
|
// animation with the negative delay)
|
|
const double progress = currentIterationElapsedTime / duration_;
|
|
const auto deltaIterations = static_cast<unsigned>(progress);
|
|
|
|
if (deltaIterations > 0) {
|
|
// Return 1 if the current iteration is the last one
|
|
if (iterationCount_ != -1 &&
|
|
currentIteration_ + deltaIterations > iterationCount_) {
|
|
currentIteration_ = iterationCount_;
|
|
return 1;
|
|
}
|
|
|
|
currentIteration_ += deltaIterations;
|
|
previousIterationsDuration_ = (currentIteration_ - 1) * duration_;
|
|
}
|
|
|
|
// If the current iteration changes, the progress must be updated
|
|
// respectively not to contain the progress of the previous iteration
|
|
return progress - deltaIterations;
|
|
}
|
|
|
|
double AnimationProgressProvider::applyAnimationDirection(
|
|
const double progress) const {
|
|
switch (direction_) {
|
|
case AnimationDirection::Normal:
|
|
return progress;
|
|
case AnimationDirection::Reverse:
|
|
return 1.0 - progress;
|
|
case AnimationDirection::Alternate:
|
|
return currentIteration_ % 2 == 0 ? 1.0 - progress : progress;
|
|
case AnimationDirection::AlternateReverse:
|
|
return currentIteration_ % 2 == 0 ? progress : 1.0 - progress;
|
|
}
|
|
}
|
|
|
|
} // namespace reanimated::css
|