first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Adapted from react-native-windows under the MIT license.
*/
#include "AnimationDriver.h"
#include <glog/logging.h>
#include <react/renderer/animated/drivers/AnimationDriverUtils.h>
#include <react/renderer/animated/nodes/ValueAnimatedNode.h>
#include <utility>
namespace facebook::react {
std::optional<AnimationDriverType> AnimationDriver::getDriverTypeByName(
const std::string& driverTypeName) {
if (driverTypeName == "frames") {
return AnimationDriverType::Frames;
} else if (driverTypeName == "spring") {
return AnimationDriverType::Spring;
} else if (driverTypeName == "decay") {
return AnimationDriverType::Decay;
} else {
return std::nullopt;
}
}
AnimationDriver::AnimationDriver(
int id,
Tag animatedValueTag,
std::optional<AnimationEndCallback> endCallback,
folly::dynamic config,
NativeAnimatedNodesManager* manager)
: endCallback_(std::move(endCallback)),
id_(id),
animatedValueTag_(animatedValueTag),
manager_(manager),
config_(std::move(config)) {
onConfigChanged();
}
void AnimationDriver::startAnimation() {
startFrameTimeMs_ = -1;
isStarted_ = true;
}
void AnimationDriver::stopAnimation(bool /*ignoreCompletedHandlers*/) {
if (auto node =
manager_->getAnimatedNode<ValueAnimatedNode>(animatedValueTag_);
node != nullptr && endCallback_) {
endCallback_.value().call(
{.finished = true,
.value = node->getRawValue(),
.offset = node->getOffset()});
}
}
void AnimationDriver::runAnimationStep(double renderingTime) {
if (!isStarted_ || isComplete_) {
return;
}
const auto frameTimeMs = renderingTime;
auto restarting = false;
if (startFrameTimeMs_ < 0) {
startFrameTimeMs_ = frameTimeMs;
restarting = true;
}
const auto timeDeltaMs = frameTimeMs - startFrameTimeMs_;
const auto isComplete = update(timeDeltaMs, restarting);
if (isComplete) {
if (iterations_ == -1 || ++currentIteration_ < iterations_) {
startFrameTimeMs_ = -1;
} else {
isComplete_ = true;
}
}
}
void AnimationDriver::updateConfig(folly::dynamic config) {
config_ = std::move(config);
onConfigChanged();
}
void AnimationDriver::onConfigChanged() {
iterations_ = (config_.count("iterations") != 0u)
? static_cast<int>(config_["iterations"].asDouble())
: 1;
isComplete_ = iterations_ == 0;
currentIteration_ = 1;
startFrameTimeMs_ = -1;
}
} // namespace facebook::react

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Adapted from react-native-windows under the MIT license.
*/
#pragma once
#include <react/debug/flags.h>
#include <react/renderer/animated/NativeAnimatedNodesManager.h>
namespace facebook::react {
enum class AnimationDriverType {
Frames,
Spring,
Decay,
};
class AnimationDriver {
public:
AnimationDriver(
int id,
Tag animatedValueTag,
std::optional<AnimationEndCallback> endCallback,
folly::dynamic config,
NativeAnimatedNodesManager *manager);
virtual ~AnimationDriver() = default;
void startAnimation();
void stopAnimation(bool ignoreCompletedHandlers = false);
inline int getId() const noexcept
{
return id_;
}
inline Tag getAnimatedValueTag() const noexcept
{
return animatedValueTag_;
}
bool getIsComplete() const noexcept
{
return isComplete_;
}
void runAnimationStep(double renderingTime);
virtual void updateConfig(folly::dynamic config);
#ifdef REACT_NATIVE_DEBUG
std::string debugID() const
{
return (config_.count("debugID") != 0u) ? config_["debugID"].asString() : "";
}
#endif
static std::optional<AnimationDriverType> getDriverTypeByName(const std::string &driverTypeName);
protected:
virtual bool update(double /*timeDeltaMs*/, bool /*restarting*/)
{
return true;
}
void markNodeUpdated(Tag tag)
{
manager_->updatedNodeTags_.insert(tag);
}
std::optional<AnimationEndCallback> endCallback_;
int id_{0};
Tag animatedValueTag_{}; // Tag of a ValueAnimatedNode
int iterations_{0};
NativeAnimatedNodesManager *manager_;
bool isComplete_{false};
int currentIteration_{0};
double startFrameTimeMs_{-1};
bool isStarted_{false};
bool ignoreCompletedHandlers_{false};
folly::dynamic config_{};
private:
void onConfigChanged();
};
} // namespace facebook::react

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Adapted from react-native-windows under the MIT license.
*/
#pragma once
#include <string_view>
namespace facebook::react {
static constexpr std::string_view ExtrapolateTypeIdentity = "identity";
static constexpr std::string_view ExtrapolateTypeClamp = "clamp";
static constexpr std::string_view ExtrapolateTypeExtend = "extend";
static constexpr double SingleFrameIntervalMs = 1000.0 / 60.0;
static constexpr double TicksPerMs = 10000.0; // ticks are 100 nanoseconds
inline double interpolate(
double inputValue,
double inputMin,
double inputMax,
double outputMin,
double outputMax,
std::string_view extrapolateLeft,
std::string_view extrapolateRight)
{
auto result = inputValue;
// Extrapolate
if (result < inputMin) {
if (extrapolateLeft == ExtrapolateTypeIdentity) {
return result;
} else if (extrapolateLeft == ExtrapolateTypeClamp) {
result = inputMin;
}
}
if (result > inputMax) {
if (extrapolateRight == ExtrapolateTypeIdentity) {
return result;
} else if (extrapolateRight == ExtrapolateTypeClamp) {
result = inputMax;
}
}
if (inputMin == inputMax) {
if (inputValue <= inputMin) {
return outputMin;
}
return outputMax;
}
return outputMin + (outputMax - outputMin) * (result - inputMin) / (inputMax - inputMin);
}
} // namespace facebook::react

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Adapted from react-native-windows under the MIT license.
*/
#include "DecayAnimationDriver.h"
#include <react/debug/react_native_assert.h>
#include <react/renderer/animated/nodes/ValueAnimatedNode.h>
namespace facebook::react {
DecayAnimationDriver::DecayAnimationDriver(
int id,
Tag animatedValueTag,
std::optional<AnimationEndCallback> endCallback,
folly::dynamic config,
NativeAnimatedNodesManager* manager)
: AnimationDriver(
id,
animatedValueTag,
std::move(endCallback),
std::move(config),
manager),
velocity_(config_["velocity"].asDouble()),
deceleration_(config_["deceleration"].asDouble()) {
react_native_assert(deceleration_ > 0);
}
std::tuple<float, double> DecayAnimationDriver::getValueAndVelocityForTime(
double time) const {
const auto value = fromValue_.value() +
velocity_ / (1 - deceleration_) *
(1 - std::exp(-(1 - deceleration_) * (1000 * time)));
return std::make_tuple(
static_cast<float>(value),
42.0f); // we don't need the velocity, so set it to a dummy value
}
bool DecayAnimationDriver::update(double timeDeltaMs, bool restarting) {
if (const auto node =
manager_->getAnimatedNode<ValueAnimatedNode>(animatedValueTag_)) {
if (restarting) {
const auto value = node->getRawValue();
if (!fromValue_.has_value()) {
// First iteration, assign fromValue based on AnimatedValue
fromValue_ = value;
} else {
// Not the first iteration, reset AnimatedValue based on
// originalValue
if (node->setRawValue(fromValue_.value())) {
markNodeUpdated(node->tag());
}
}
lastValue_ = value;
}
const auto [value, velocity] =
getValueAndVelocityForTime(timeDeltaMs / 1000.0);
auto isComplete =
lastValue_.has_value() && std::abs(value - lastValue_.value()) < 0.1;
if (!restarting && isComplete) {
return true;
} else {
lastValue_ = value;
if (node->setRawValue(value)) {
markNodeUpdated(node->tag());
}
return false;
}
}
return true;
}
} // namespace facebook::react

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Adapted from react-native-windows under the MIT license.
*/
#pragma once
#include "AnimationDriver.h"
namespace facebook::react {
class DecayAnimationDriver : public AnimationDriver {
public:
DecayAnimationDriver(
int id,
Tag animatedValueTag,
std::optional<AnimationEndCallback> endCallback,
folly::dynamic config,
NativeAnimatedNodesManager *manager);
protected:
bool update(double timeDeltaMs, bool restarting) override;
private:
std::tuple<float, double> getValueAndVelocityForTime(double time) const;
private:
double velocity_{0};
double deceleration_{0};
std::optional<double> fromValue_{std::nullopt};
std::optional<double> lastValue_{0};
};
} // namespace facebook::react

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Adapted from react-native-windows under the MIT license.
*/
#include "FrameAnimationDriver.h"
#include <react/debug/react_native_assert.h>
#include <react/renderer/animated/drivers/AnimationDriver.h>
#include <react/renderer/animated/drivers/AnimationDriverUtils.h>
#include <react/renderer/animated/nodes/ValueAnimatedNode.h>
#include <cmath>
#include <utility>
namespace facebook::react {
FrameAnimationDriver::FrameAnimationDriver(
int id,
Tag animatedValueTag,
std::optional<AnimationEndCallback> endCallback,
folly::dynamic config,
NativeAnimatedNodesManager* manager)
: AnimationDriver(
id,
animatedValueTag,
std::move(endCallback),
std::move(config),
manager) {
onConfigChanged();
}
void FrameAnimationDriver::updateConfig(folly::dynamic config) {
AnimationDriver::updateConfig(config);
onConfigChanged();
}
void FrameAnimationDriver::onConfigChanged() {
auto frames = config_["frames"];
react_native_assert(frames.type() == folly::dynamic::ARRAY);
for (const auto& frame : frames) {
auto frameValue = frame.asDouble();
frames_.push_back(frameValue);
}
toValue_ = config_["toValue"].asDouble();
}
bool FrameAnimationDriver::update(double timeDeltaMs, bool /*restarting*/) {
if (auto node =
manager_->getAnimatedNode<ValueAnimatedNode>(animatedValueTag_)) {
if (!startValue_) {
startValue_ = node->getRawValue();
}
const auto startIndex =
static_cast<size_t>(std::round(timeDeltaMs / SingleFrameIntervalMs));
assert(startIndex >= 0);
const auto nextIndex = startIndex + 1;
double nextValue = NAN;
auto isComplete = false;
if (nextIndex >= frames_.size()) {
if (iterations_ == -1 || currentIteration_ < iterations_) {
// Use last frame value, just in case it's different from toValue_
nextValue = startValue_.value() +
frames_[frames_.size() - 1] * (toValue_ - startValue_.value());
} else {
nextValue = toValue_;
}
isComplete = true;
} else {
const auto fromInterval = startIndex * SingleFrameIntervalMs;
const auto toInterval = nextIndex * SingleFrameIntervalMs;
const auto fromValue = frames_[startIndex];
const auto toValue = frames_[nextIndex];
// Map timestamp to frame value (frames_ elements are in [0,1])
const auto frameOutput = interpolate(
timeDeltaMs,
fromInterval,
toInterval,
fromValue,
toValue,
ExtrapolateTypeExtend,
ExtrapolateTypeExtend);
// Map frame to output value
nextValue = interpolate(
frameOutput,
0,
1,
startValue_.value(),
toValue_,
ExtrapolateTypeExtend,
ExtrapolateTypeExtend);
}
if (node->setRawValue(nextValue)) {
markNodeUpdated(node->tag());
}
return isComplete;
}
return true;
}
} // namespace facebook::react

View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Adapted from react-native-windows under the MIT license.
*/
#pragma once
#include "AnimationDriver.h"
namespace facebook::react {
class FrameAnimationDriver : public AnimationDriver {
public:
FrameAnimationDriver(
int id,
Tag animatedValueTag,
std::optional<AnimationEndCallback> endCallback,
folly::dynamic config,
NativeAnimatedNodesManager *manager);
protected:
bool update(double timeDeltaMs, bool restarting) override;
void updateConfig(folly::dynamic config) override;
private:
void onConfigChanged();
std::vector<double> frames_{};
double toValue_{0};
std::optional<double> startValue_{};
};
} // namespace facebook::react

View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Adapted from react-native-windows under the MIT license.
*/
#include "SpringAnimationDriver.h"
#include <react/renderer/animated/drivers/AnimationDriverUtils.h>
#include <react/renderer/animated/nodes/ValueAnimatedNode.h>
namespace facebook::react {
static constexpr auto MaxDeltaTimeMs = 4.0 * 1000.0 / 60.0;
SpringAnimationDriver::SpringAnimationDriver(
int id,
Tag animatedValueTag,
std::optional<AnimationEndCallback> endCallback,
folly::dynamic config,
NativeAnimatedNodesManager* manager)
: AnimationDriver(
id,
animatedValueTag,
std::move(endCallback),
std::move(config),
manager),
springStiffness_(config_["stiffness"].asDouble()),
springDamping_(config_["damping"].asDouble()),
springMass_(config_["mass"].asDouble()),
initialVelocity_(config_["initialVelocity"].asDouble()),
endValue_(config_["toValue"].asDouble()),
restSpeedThreshold_(config_["restSpeedThreshold"].asDouble()),
displacementFromRestThreshold_(
config_["restDisplacementThreshold"].asDouble()),
overshootClampingEnabled_(config_["overshootClamping"].asBool()) {}
std::tuple<float, double> SpringAnimationDriver::getValueAndVelocityForTime(
double time) const {
const auto startValue = fromValue_.value();
const auto toValue = endValue_;
const auto c = springDamping_;
const auto m = springMass_;
const auto k = springStiffness_;
const auto v0 = -initialVelocity_;
const auto zeta = c / (2 * std::sqrt(k * m));
const auto omega0 = std::sqrt(k / m);
const auto omega1 = omega0 * std::sqrt(1.0 - (zeta * zeta));
const auto x0 = toValue - startValue;
if (zeta < 1) {
const auto envelope = std::exp(-zeta * omega0 * time);
const auto value = static_cast<float>(
toValue -
envelope *
((v0 + zeta * omega0 * x0) / omega1 * std::sin(omega1 * time) +
x0 * std::cos(omega1 * time)));
const auto velocity = zeta * omega0 * envelope *
(std::sin(omega1 * time) * (v0 + zeta * omega0 * x0) / omega1 +
x0 * std::cos(omega1 * time)) -
envelope *
(std::cos(omega1 * time) * (v0 + zeta * omega0 * x0) -
omega1 * x0 * std::sin(omega1 * time));
return std::make_tuple(value, velocity);
} else {
const auto envelope = std::exp(-omega0 * time);
const auto value = static_cast<float>(
endValue_ - envelope * (x0 + (v0 + omega0 * x0) * time));
const auto velocity =
envelope * (v0 * (time * omega0 - 1) + time * x0 * (omega0 * omega0));
return std::make_tuple(value, velocity);
}
}
bool SpringAnimationDriver::update(double timeDeltaMs, bool restarting) {
if (const auto node =
manager_->getAnimatedNode<ValueAnimatedNode>(animatedValueTag_)) {
if (restarting) {
if (!fromValue_.has_value()) {
fromValue_ = node->getRawValue();
} else {
if (node->setRawValue(fromValue_.value())) {
markNodeUpdated(node->tag());
}
}
// Spring animations run a frame behind JS driven animations if we do
// not start the first frame at 16ms.
lastTime_ = timeDeltaMs - SingleFrameIntervalMs;
timeAccumulator_ = 0.0;
}
// clamp the amount of timeDeltaMs to avoid stuttering in the UI.
// We should be able to catch up in a subsequent advance if necessary.
auto adjustedDeltaTime = timeDeltaMs - lastTime_;
if (adjustedDeltaTime > MaxDeltaTimeMs) {
adjustedDeltaTime = MaxDeltaTimeMs;
}
timeAccumulator_ += adjustedDeltaTime;
lastTime_ = timeDeltaMs;
auto [value, velocity] =
getValueAndVelocityForTime(timeAccumulator_ / 1000.0);
auto isComplete = false;
if (isAtRest(velocity, value, endValue_) ||
(overshootClampingEnabled_ && isOvershooting(value))) {
if (springStiffness_ > 0) {
value = static_cast<float>(endValue_);
} else {
endValue_ = value;
}
isComplete = true;
}
if (node->setRawValue(value)) {
markNodeUpdated(node->tag());
}
return isComplete;
}
return true;
}
bool SpringAnimationDriver::isAtRest(
double currentVelocity,
double currentValue,
double endValue) const {
return std::abs(currentVelocity) <= restSpeedThreshold_ &&
(std::abs(currentValue - endValue) <= displacementFromRestThreshold_ ||
springStiffness_ == 0);
}
bool SpringAnimationDriver::isOvershooting(double currentValue) const {
const auto startValue = fromValue_.value();
return springStiffness_ > 0 &&
((startValue < endValue_ && currentValue > endValue_) ||
(startValue > endValue_ && currentValue < endValue_));
}
} // namespace facebook::react

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/*
* Adapted from react-native-windows under the MIT license.
*/
#pragma once
#include "AnimationDriver.h"
namespace facebook::react {
class SpringAnimationDriver : public AnimationDriver {
public:
SpringAnimationDriver(
int id,
Tag animatedValueTag,
std::optional<AnimationEndCallback> endCallback,
folly::dynamic config,
NativeAnimatedNodesManager *manager);
protected:
bool update(double timeDeltaMs, bool restarting) override;
private:
std::tuple<float, double> getValueAndVelocityForTime(double time) const;
bool isAtRest(double currentVelocity, double currentValue, double endValue) const;
bool isOvershooting(double currentValue) const;
double springStiffness_{0};
double springDamping_{0};
double springMass_{0};
double initialVelocity_{0};
std::optional<double> fromValue_{std::nullopt};
double endValue_{0};
double restSpeedThreshold_{0};
double displacementFromRestThreshold_{0};
bool overshootClampingEnabled_{false};
double lastTime_{0};
double timeAccumulator_{0};
};
} // namespace facebook::react