chore: update map

This commit is contained in:
2026-02-19 08:55:45 +01:00
parent 3308adf1ea
commit 900c54c20b
1845 changed files with 3677 additions and 84269 deletions
+62 -18
View File
@@ -441,6 +441,41 @@
"@babel/core": "^7.0.0-0" "@babel/core": "^7.0.0-0"
} }
}, },
"node_modules/@babel/plugin-proposal-nullish-coalescing-operator": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz",
"integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==",
"deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.18.6",
"@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-proposal-optional-chaining": {
"version": "7.21.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz",
"integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==",
"deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.",
"peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.20.2",
"@babel/helper-skip-transparent-expression-wrappers": "^7.20.0",
"@babel/plugin-syntax-optional-chaining": "^7.8.3"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-syntax-async-generators": { "node_modules/@babel/plugin-syntax-async-generators": {
"version": "7.8.4", "version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
@@ -984,6 +1019,20 @@
"@babel/core": "^7.0.0-0" "@babel/core": "^7.0.0-0"
} }
}, },
"node_modules/@babel/plugin-transform-object-assign": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.27.1.tgz",
"integrity": "sha512-LP6tsnirA6iy13uBKiYgjJsfQrodmlSrpZModtlo1Vk8sOO68gfo7dfA9TGJyEgxTiO7czK4EGZm8FJEZtk4kQ==",
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-transform-object-rest-spread": { "node_modules/@babel/plugin-transform-object-rest-spread": {
"version": "7.28.6", "version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz",
@@ -6601,29 +6650,24 @@
} }
}, },
"node_modules/react-native-reanimated": { "node_modules/react-native-reanimated": {
"version": "4.1.6", "version": "3.6.3",
"resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.6.tgz", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.6.3.tgz",
"integrity": "sha512-F+ZJBYiok/6Jzp1re75F/9aLzkgoQCOh4yxrnwATa8392RvM3kx+fiXXFvwcgE59v48lMwd9q0nzF1oJLXpfxQ==", "integrity": "sha512-2KkkPozoIvDbJcHuf8qeyoLROXQxizSi+2CTCkuNVkVZOxxY4B0Omvgq61aOQhSZUh/649x1YHoAaTyGMGDJUw==",
"dependencies": { "dependencies": {
"react-native-is-edge-to-edge": "^1.2.1", "@babel/plugin-transform-object-assign": "^7.16.7",
"semver": "7.7.2" "@babel/preset-typescript": "^7.16.7",
"convert-source-map": "^2.0.0",
"invariant": "^2.2.4"
}, },
"peerDependencies": { "peerDependencies": {
"@babel/core": "^7.0.0-0", "@babel/core": "^7.0.0-0",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0-0",
"@babel/plugin-proposal-optional-chaining": "^7.0.0-0",
"@babel/plugin-transform-arrow-functions": "^7.0.0-0",
"@babel/plugin-transform-shorthand-properties": "^7.0.0-0",
"@babel/plugin-transform-template-literals": "^7.0.0-0",
"react": "*", "react": "*",
"react-native": "*", "react-native": "*"
"react-native-worklets": ">=0.5.0"
}
},
"node_modules/react-native-reanimated/node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
} }
}, },
"node_modules/react-native-safe-area-context": { "node_modules/react-native-safe-area-context": {
@@ -1,89 +0,0 @@
#include <react/debug/react_native_assert.h>
#include <reanimated/AnimatedSensor/AnimatedSensorModule.h>
#include <utility>
namespace reanimated {
AnimatedSensorModule::AnimatedSensorModule(
const PlatformDepMethodsHolder &platformDepMethodsHolder)
: platformRegisterSensorFunction_(platformDepMethodsHolder.registerSensor),
platformUnregisterSensorFunction_(
platformDepMethodsHolder.unregisterSensor) {}
AnimatedSensorModule::~AnimatedSensorModule() {
react_native_assert(
sensorsIds_.empty() &&
"Tried to deallocate AnimatedSensorModule with registered sensors");
}
jsi::Value AnimatedSensorModule::registerSensor(
jsi::Runtime &rt,
const std::shared_ptr<WorkletRuntime> &uiWorkletRuntime,
const jsi::Value &sensorTypeValue,
const jsi::Value &interval,
const jsi::Value &iosReferenceFrame,
const jsi::Value &sensorDataHandler) {
SensorType sensorType = static_cast<SensorType>(sensorTypeValue.asNumber());
auto serializableHandler = extractSerializableOrThrow<SerializableWorklet>(
rt,
sensorDataHandler,
"[Reanimated] Sensor event handler must be a worklet.");
int sensorId = platformRegisterSensorFunction_(
sensorType,
interval.asNumber(),
iosReferenceFrame.asNumber(),
[sensorType,
serializableHandler,
weakUiWorkletRuntime = std::weak_ptr<WorkletRuntime>(uiWorkletRuntime)](
double newValues[], int orientationDegrees) {
auto uiWorkletRuntime = weakUiWorkletRuntime.lock();
if (uiWorkletRuntime == nullptr) {
return;
}
jsi::Runtime &uiRuntime = uiWorkletRuntime->getJSIRuntime();
jsi::Object value(uiRuntime);
if (sensorType == SensorType::ROTATION_VECTOR) {
// TODO: timestamp should be provided by the platform implementation
// such that the native side has a chance of providing a true event
// timestamp
value.setProperty(uiRuntime, "qx", newValues[0]);
value.setProperty(uiRuntime, "qy", newValues[1]);
value.setProperty(uiRuntime, "qz", newValues[2]);
value.setProperty(uiRuntime, "qw", newValues[3]);
value.setProperty(uiRuntime, "yaw", newValues[4]);
value.setProperty(uiRuntime, "pitch", newValues[5]);
value.setProperty(uiRuntime, "roll", newValues[6]);
} else {
value.setProperty(uiRuntime, "x", newValues[0]);
value.setProperty(uiRuntime, "y", newValues[1]);
value.setProperty(uiRuntime, "z", newValues[2]);
}
value.setProperty(
uiRuntime, "interfaceOrientation", orientationDegrees);
uiWorkletRuntime->runGuarded(serializableHandler, value);
});
if (sensorId != -1) {
sensorsIds_.insert(sensorId);
}
return jsi::Value(sensorId);
}
void AnimatedSensorModule::unregisterSensor(const jsi::Value &sensorId) {
// It is called during sensor hook unmounting
sensorsIds_.erase(sensorId.getNumber());
platformUnregisterSensorFunction_(sensorId.asNumber());
}
void AnimatedSensorModule::unregisterAllSensors() {
for (auto sensorId : sensorsIds_) {
platformUnregisterSensorFunction_(sensorId);
}
sensorsIds_.clear();
}
} // namespace reanimated
@@ -1,47 +0,0 @@
#pragma once
#include <reanimated/Tools/PlatformDepMethodsHolder.h>
#include <worklets/SharedItems/Serializable.h>
#include <worklets/WorkletRuntime/WorkletRuntime.h>
#include <jsi/jsi.h>
#include <memory>
#include <unordered_set>
namespace reanimated {
using namespace facebook;
using namespace worklets;
enum SensorType {
ACCELEROMETER = 1,
GYROSCOPE = 2,
GRAVITY = 3,
MAGNETIC_FIELD = 4,
ROTATION_VECTOR = 5,
};
class AnimatedSensorModule {
std::unordered_set<int> sensorsIds_;
RegisterSensorFunction platformRegisterSensorFunction_;
UnregisterSensorFunction platformUnregisterSensorFunction_;
public:
AnimatedSensorModule(
const PlatformDepMethodsHolder &platformDepMethodsHolder);
~AnimatedSensorModule();
jsi::Value registerSensor(
jsi::Runtime &rt,
const std::shared_ptr<WorkletRuntime> &uiWorkletRuntime,
const jsi::Value &sensorType,
const jsi::Value &interval,
const jsi::Value &iosReferenceFrame,
const jsi::Value &sensorDataContainer);
void unregisterSensor(const jsi::Value &sensorId);
void unregisterAllSensors();
};
} // namespace reanimated
@@ -1,427 +0,0 @@
#include <reanimated/CSS/InterpolatorRegistry.h>
#include <reanimated/Tools/FeatureFlags.h>
#include <reanimated/CSS/common/values/CSSAngle.h>
#include <reanimated/CSS/common/values/CSSBoolean.h>
#include <reanimated/CSS/common/values/CSSColor.h>
#include <reanimated/CSS/common/values/CSSDiscreteArray.h>
#include <reanimated/CSS/common/values/CSSKeyword.h>
#include <reanimated/CSS/common/values/CSSLength.h>
#include <reanimated/CSS/common/values/CSSNumber.h>
#include <reanimated/CSS/common/values/CSSValue.h>
#include <reanimated/CSS/common/transforms/TransformMatrix3D.h>
#include <reanimated/CSS/svg/values/SVGLength.h>
#include <reanimated/CSS/svg/values/SVGStrokeDashArray.h>
#include <reanimated/CSS/interpolation/InterpolatorFactory.h>
#include <reanimated/CSS/interpolation/transforms/operations/matrix.h>
#include <reanimated/CSS/interpolation/transforms/operations/perspective.h>
#include <reanimated/CSS/interpolation/transforms/operations/rotate.h>
#include <reanimated/CSS/interpolation/transforms/operations/scale.h>
#include <reanimated/CSS/interpolation/transforms/operations/skew.h>
#include <reanimated/CSS/interpolation/transforms/operations/translate.h>
#include <vector>
namespace reanimated::css {
namespace {
// Private implementation details
const auto BLACK = CSSColor(0, 0, 0, 255);
const auto TRANSPARENT = CSSColor::Transparent;
InterpolatorFactoriesRecord mergeInterpolators(
const std::vector<InterpolatorFactoriesRecord> &maps) {
InterpolatorFactoriesRecord result;
for (const auto &map : maps) {
result.insert(map.begin(), map.end());
}
return result;
}
// ==========================
// React Native Interpolators
// ==========================
const InterpolatorFactoriesRecord FLEX_INTERPOLATORS = {
{"alignContent", value<CSSKeyword>("flex-start")},
{"alignItems", value<CSSKeyword>("stretch")},
{"alignSelf", value<CSSKeyword>("auto")},
{"aspectRatio", value<CSSDouble, CSSKeyword>("auto")},
{"borderBottomWidth", value<CSSDouble>(0)},
{"borderEndWidth", value<CSSDouble>(0)},
{"borderLeftWidth", value<CSSDouble>(0)},
{"borderRightWidth", value<CSSDouble>(0)},
{"borderStartWidth", value<CSSDouble>(0)},
{"borderTopWidth", value<CSSDouble>(0)},
{"borderWidth", value<CSSDouble>(0)},
{"bottom",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "height", "auto")},
{"boxSizing", value<CSSKeyword>("border-box")},
{"display", value<CSSDisplay>("flex")},
{"end", value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", "auto")},
{"flex", value<CSSDouble>(0)},
{"flexBasis",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", "auto")},
{"flexDirection", value<CSSKeyword>("column")},
{"rowGap", value<CSSLength>(RelativeTo::Self, "height", 0)},
{"columnGap", value<CSSLength>(RelativeTo::Self, "width", 0)},
{"flexGrow", value<CSSDouble>(0)},
{"flexShrink", value<CSSDouble>(0)},
{"flexWrap", value<CSSKeyword>("no-wrap")},
{"height",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "height", "auto")},
{"justifyContent", value<CSSKeyword>("flex-start")},
{"left", value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", "auto")},
{"margin", value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"marginBottom",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"marginEnd", value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"marginHorizontal",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"marginLeft",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"marginRight",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"marginStart",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"marginTop", value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"marginVertical",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"maxHeight",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "height", "auto")},
{"maxWidth",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", "auto")},
{"minHeight",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "height", "auto")},
{"minWidth",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", "auto")},
{"overflow", value<CSSKeyword>("visible")},
{"padding", value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"paddingBottom",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"paddingEnd",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"paddingHorizontal",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"paddingLeft",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"paddingRight",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"paddingStart",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"paddingTop",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"paddingVertical",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", 0)},
{"position", value<CSSKeyword>("relative")},
{"right",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", "auto")},
{"start",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", "auto")},
{"top", value<CSSLength, CSSKeyword>(RelativeTo::Parent, "height", "auto")},
{"width",
value<CSSLength, CSSKeyword>(RelativeTo::Parent, "width", "auto")},
{"zIndex", value<CSSInteger>(0)},
{"direction", value<CSSKeyword>("inherit")}};
const InterpolatorFactoriesRecord SHADOW_INTERPOLATORS_IOS = {
{"shadowColor", value<CSSColor>(BLACK)},
{"shadowOffset",
record({{"width", value<CSSDouble>(0)}, {"height", value<CSSDouble>(0)}})},
{"shadowRadius", value<CSSDouble>(0)},
{"shadowOpacity", value<CSSDouble>(1)}};
const InterpolatorFactoriesRecord TRANSFORMS_INTERPOLATORS = {
{"transformOrigin",
array(
{value<CSSLength>(RelativeTo::Self, "width", "50%"),
value<CSSLength>(RelativeTo::Self, "height", "50%"),
value<CSSDouble>(0)})},
{"transform",
transforms(
{{"perspective",
transformOp<PerspectiveOperation>(0)}, // 0 - no perspective
{"rotate", transformOp<RotateOperation>("0deg")},
{"rotateX", transformOp<RotateXOperation>("0deg")},
{"rotateY", transformOp<RotateYOperation>("0deg")},
{"rotateZ", transformOp<RotateZOperation>("0deg")},
{"scale", transformOp<ScaleOperation>(1)},
{"scaleX", transformOp<ScaleXOperation>(1)},
{"scaleY", transformOp<ScaleYOperation>(1)},
{"translateX",
transformOp<TranslateXOperation>(RelativeTo::Self, "width", 0)},
{"translateY",
transformOp<TranslateYOperation>(RelativeTo::Self, "height", 0)},
{"skewX", transformOp<SkewXOperation>("0deg")},
{"skewY", transformOp<SkewYOperation>("0deg")},
{"matrix",
transformOp<MatrixOperation>(TransformMatrix3D::Identity())}})},
};
const InterpolatorFactoriesRecord VIEW_INTERPOLATORS = mergeInterpolators(
{FLEX_INTERPOLATORS,
SHADOW_INTERPOLATORS_IOS,
TRANSFORMS_INTERPOLATORS,
InterpolatorFactoriesRecord{
{"backfaceVisibility", value<CSSKeyword>("visible")},
{"backgroundColor", value<CSSColor>(TRANSPARENT)},
{"borderBlockColor", value<CSSColor>(BLACK)},
{"borderBlockEndColor", value<CSSColor>(BLACK)},
{"borderBlockStartColor", value<CSSColor>(BLACK)},
{"borderBottomColor", value<CSSColor>(BLACK)},
{"borderBottomEndRadius",
value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderBottomLeftRadius",
value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderBottomRightRadius",
value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderBottomStartRadius",
value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderColor", value<CSSColor>(BLACK)},
{"borderCurve", value<CSSKeyword>("circular")},
{"borderEndColor", value<CSSColor>(BLACK)},
{"borderEndEndRadius", value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderEndStartRadius",
value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderLeftColor", value<CSSColor>(BLACK)},
{"borderRadius", value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderRightColor", value<CSSColor>(BLACK)},
{"borderStartColor", value<CSSColor>(BLACK)},
{"borderStartEndRadius",
value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderStartStartRadius",
value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderStyle", value<CSSKeyword>("solid")},
{"borderTopColor", value<CSSColor>(BLACK)},
{"borderTopEndRadius", value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderTopLeftRadius",
value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderTopRightRadius",
value<CSSLength>(RelativeTo::Self, "width", 0)},
{"borderTopStartRadius",
value<CSSLength>(RelativeTo::Self, "width", 0)},
{"outlineColor", value<CSSColor>(BLACK)},
{"outlineOffset", value<CSSDouble>(0)},
{"outlineStyle", value<CSSKeyword>("solid")},
{"outlineWidth", value<CSSDouble>(0)},
{"opacity", value<CSSDouble>(1)},
{"elevation", value<CSSDouble>(0)},
{"pointerEvents", value<CSSKeyword>("auto")},
{"isolation", value<CSSKeyword>("auto")},
{"cursor", value<CSSKeyword>("auto")},
{"boxShadow",
array({record({
{"offsetX", value<CSSDouble>(0)},
{"offsetY", value<CSSDouble>(0)},
#ifdef ANDROID
// For some reason Android crashes when blurRadius is smaller
// than 1, so we use a custom value type that will never be
// smaller than 1
{"blurRadius", value<CSSShadowRadiusAndroid>(1)},
#else
{"blurRadius", value<CSSDouble>(0)},
#endif
{"spreadDistance", value<CSSDouble>(0)},
{"color", value<CSSColor>(TRANSPARENT)},
{"inset", value<CSSBoolean>(false)},
})})},
{"mixBlendMode", value<CSSKeyword>("normal")}}});
const InterpolatorFactoriesRecord TEXT_INTERPOLATORS_IOS = {
{"fontVariant",
value<CSSDiscreteArray<CSSKeyword>>(std::vector<CSSKeyword>{})},
{"textDecorationColor", value<CSSColor>(BLACK)},
{"textDecorationStyle", value<CSSKeyword>("solid")},
{"writingDirection", value<CSSKeyword>("auto")},
};
const InterpolatorFactoriesRecord TEXT_INTERPOLATORS_ANDROID = {
{"textAlignVertical", value<CSSKeyword>("auto")},
{"verticalAlign", value<CSSKeyword>("auto")},
{"includeFontPadding", value<CSSBoolean>(false)},
};
const InterpolatorFactoriesRecord TEXT_INTERPOLATORS = mergeInterpolators(
{VIEW_INTERPOLATORS,
TEXT_INTERPOLATORS_IOS,
TEXT_INTERPOLATORS_ANDROID,
InterpolatorFactoriesRecord{
{"color", value<CSSColor>(BLACK)},
{"fontFamily", value<CSSKeyword>("inherit")},
{"fontSize", value<CSSDouble>(14)},
{"fontStyle", value<CSSKeyword>("normal")},
{"fontWeight", value<CSSKeyword>("normal")},
{"letterSpacing", value<CSSDouble>(0)},
{"lineHeight",
value<CSSDouble>(14)}, // TODO - should inherit from fontSize
{"textAlign", value<CSSKeyword>("auto")},
{"textDecorationLine", value<CSSKeyword>("none")},
{"textShadowColor", value<CSSColor>(BLACK)},
{"textShadowOffset",
record(
{{"width", value<CSSDouble>(0)},
{"height", value<CSSDouble>(0)}})},
{"textShadowRadius", value<CSSDouble>(0)},
{"textTransform", value<CSSKeyword>("none")},
{"userSelect", value<CSSKeyword>("auto")},
}});
const InterpolatorFactoriesRecord IMAGE_INTERPOLATORS = mergeInterpolators(
{VIEW_INTERPOLATORS,
InterpolatorFactoriesRecord{
{"resizeMode", value<CSSKeyword>("cover")},
{"overlayColor", value<CSSColor>(BLACK)},
{"tintColor", value<CSSColor>(BLACK)},
}});
// =================
// SVG INTERPOLATORS
// =================
const InterpolatorFactoriesRecord SVG_COLOR_INTERPOLATORS = {
{"color", value<CSSColor>(BLACK)},
};
const InterpolatorFactoriesRecord SVG_FILL_INTERPOLATORS = {
{"fill", value<CSSColor>(BLACK)},
{"fillOpacity", value<CSSDouble>(1)},
{"fillRule", value<CSSInteger>(0)},
};
const InterpolatorFactoriesRecord SVG_STROKE_INTERPOLATORS = {
{"stroke", value<CSSColor>(BLACK)},
{"strokeWidth", value<SVGLength>(1)},
{"strokeOpacity", value<CSSDouble>(1)},
{"strokeDasharray",
value<SVGStrokeDashArray, CSSKeyword>(SVGStrokeDashArray())},
{"strokeDashoffset", value<SVGLength>(0)},
{"strokeLinecap", value<CSSInteger>(0)},
{"strokeLinejoin", value<CSSInteger>(0)},
{"strokeMiterlimit", value<CSSDouble>(4)},
{"vectorEffect", value<CSSInteger>(0)},
};
const InterpolatorFactoriesRecord SVG_CLIP_INTERPOLATORS = {
{"clipRule", value<CSSKeyword>("nonzero")},
{"clipPath", value<CSSKeyword>("none")},
};
const InterpolatorFactoriesRecord SVG_TRANSFORM_INTERPOLATORS = {
{"translateX", value<SVGLength>(0)},
{"translateY", value<SVGLength>(0)},
{"originX", value<SVGLength>(0)},
{"originY", value<SVGLength>(0)},
{"scaleX", value<CSSDouble>(1)},
{"scaleY", value<CSSDouble>(1)},
{"skewX", value<CSSAngle>(0)},
{"skewY", value<CSSAngle>(0)},
{"rotation", value<CSSAngle>(0)},
};
const InterpolatorFactoriesRecord SVG_COMMON_INTERPOLATORS =
mergeInterpolators({
SVG_COLOR_INTERPOLATORS,
SVG_FILL_INTERPOLATORS,
SVG_STROKE_INTERPOLATORS,
});
const InterpolatorFactoriesRecord SVG_CIRCLE_INTERPOLATORS = mergeInterpolators(
{SVG_COMMON_INTERPOLATORS,
InterpolatorFactoriesRecord{
{"cx", value<SVGLength, CSSKeyword>(0)},
{"cy", value<SVGLength, CSSKeyword>(0)},
{"r", value<SVGLength, CSSKeyword>(0)},
{"opacity", value<CSSDouble>(1)},
}});
const InterpolatorFactoriesRecord SVG_ELLIPSE_INTERPOLATORS =
mergeInterpolators(
{SVG_COMMON_INTERPOLATORS,
InterpolatorFactoriesRecord{
{"cx", value<SVGLength, CSSKeyword>(0)},
{"cy", value<SVGLength, CSSKeyword>(0)},
{"rx", value<SVGLength, CSSKeyword>(0)},
{"ry", value<SVGLength, CSSKeyword>(0)},
{"opacity", value<CSSDouble>(1)},
}});
const InterpolatorFactoriesRecord SVG_LINE_INTERPOLATORS = mergeInterpolators(
{SVG_COMMON_INTERPOLATORS,
InterpolatorFactoriesRecord{
{"x1", value<SVGLength, CSSKeyword>(0)},
{"y1", value<SVGLength, CSSKeyword>(0)},
{"x2", value<SVGLength, CSSKeyword>(0)},
{"y2", value<SVGLength, CSSKeyword>(0)},
{"opacity", value<CSSDouble>(1)},
}});
const InterpolatorFactoriesRecord SVG_RECT_INTERPOLATORS = mergeInterpolators(
{SVG_COMMON_INTERPOLATORS,
InterpolatorFactoriesRecord{
{"x", value<SVGLength, CSSKeyword>(0)},
{"y", value<SVGLength, CSSKeyword>(0)},
{"width", value<SVGLength, CSSKeyword>(0)},
{"height", value<SVGLength, CSSKeyword>(0)},
{"rx", value<SVGLength, CSSKeyword>(0)},
{"ry", value<SVGLength, CSSKeyword>(0)},
{"opacity", value<CSSDouble>(1)},
}});
const InterpolatorFactoriesRecord SVG_PATH_INTERPOLATORS = mergeInterpolators(
{SVG_COMMON_INTERPOLATORS,
InterpolatorFactoriesRecord{
// TODO - add more properties
}});
// ==================
// COMPONENT REGISTRY
// ==================
ComponentInterpolatorsMap initializeRegistry() {
ComponentInterpolatorsMap registry = {
// React Native Components
{"View", VIEW_INTERPOLATORS},
{"Paragraph", TEXT_INTERPOLATORS},
{"Image", IMAGE_INTERPOLATORS},
};
if (StaticFeatureFlags::getFlag(
"EXPERIMENTAL_CSS_ANIMATIONS_FOR_SVG_COMPONENTS")) {
// SVG Components
registry["RNSVGCircle"] = SVG_CIRCLE_INTERPOLATORS;
registry["RNSVGEllipse"] = SVG_ELLIPSE_INTERPOLATORS;
registry["RNSVGLine"] = SVG_LINE_INTERPOLATORS;
registry["RNSVGPath"] = SVG_PATH_INTERPOLATORS;
registry["RNSVGRect"] = SVG_RECT_INTERPOLATORS;
}
return registry;
}
ComponentInterpolatorsMap registry = initializeRegistry();
} // namespace
const InterpolatorFactoriesRecord &getComponentInterpolators(
const std::string &componentName) {
if (auto it = registry.find(componentName); it != registry.end()) {
return it->second;
}
// Use View interpolators as a fallback for unknown components
// (e.g. we get the ScrollView component name for the ScrollView component
// but it should be styled in the same way as a View)
return VIEW_INTERPOLATORS;
}
void registerComponentInterpolators(
const std::string &componentName,
const InterpolatorFactoriesRecord &interpolators) {
registry[componentName] = interpolators;
}
} // namespace reanimated::css
@@ -1,20 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/PropertyInterpolator.h>
#include <string>
#include <unordered_map>
namespace reanimated::css {
using ComponentInterpolatorsMap =
std::unordered_map<std::string, InterpolatorFactoriesRecord>;
const InterpolatorFactoriesRecord &getComponentInterpolators(
const std::string &componentName);
void registerComponentInterpolators(
const std::string &componentName,
const InterpolatorFactoriesRecord &interpolators);
} // namespace reanimated::css
@@ -1,23 +0,0 @@
#pragma once
#include <jsi/jsi.h>
#include <string>
#include <vector>
namespace reanimated::css {
using namespace facebook;
using PropertyNames = std::vector<std::string>;
using PropertyPath = std::vector<std::string>;
/**
* If nullopt - all style properties can trigger transition
* If empty vector - no style property can trigger transition
* Otherwise - only specified style properties can trigger transition
*/
using TransitionProperties = std::optional<PropertyNames>;
using EasingFunction = std::function<double(double)>;
using ColorChannels = std::array<uint8_t, 4>;
} // namespace reanimated::css
@@ -1,57 +0,0 @@
#include <reanimated/CSS/common/transforms/Quaternion.h>
#include <cmath>
namespace reanimated::css {
bool Quaternion::operator==(const Quaternion &other) const {
return x == other.x && y == other.y && z == other.z && w == other.w;
}
#ifndef NDEBUG
std::ostream &operator<<(std::ostream &os, const Quaternion &quaternion) {
os << "Quaternion(" << quaternion.x << ", " << quaternion.y << ", "
<< quaternion.z << ", " << quaternion.w << ")";
return os;
}
#endif // NDEBUG
Quaternion Quaternion::interpolate(const double t, const Quaternion &other)
const {
const double kEpsilon = 1e-5;
Quaternion copy = *this;
double cosHalfAngle =
copy.x * other.x + copy.y * other.y + copy.z * other.z + copy.w * other.w;
if (cosHalfAngle < 0.0) {
copy.x = -copy.x;
copy.y = -copy.y;
copy.z = -copy.z;
copy.w = -copy.w;
cosHalfAngle = -cosHalfAngle;
}
if (cosHalfAngle > 1)
cosHalfAngle = 1;
double sinHalfAngle = std::sqrt(1.0 - cosHalfAngle * cosHalfAngle);
if (sinHalfAngle < kEpsilon) {
// Quaternions share common axis and angle.
return *this;
}
double halfAngle = std::acos(cosHalfAngle);
double scale = std::sin((1 - t) * halfAngle) / sinHalfAngle;
double invscale = std::sin(t * halfAngle) / sinHalfAngle;
return {
copy.x * scale + other.x * invscale,
copy.y * scale + other.y * invscale,
copy.z * scale + other.z * invscale,
copy.w * scale + other.w * invscale};
}
} // namespace reanimated::css
@@ -1,23 +0,0 @@
#pragma once
#ifndef NDEBUG
#include <iostream>
#endif // NDEBUG
namespace reanimated::css {
struct Quaternion {
double x, y, z, w;
bool operator==(const Quaternion &other) const;
#ifndef NDEBUG
friend std::ostream &operator<<(
std::ostream &os,
const Quaternion &quaternion);
#endif // NDEBUG
Quaternion interpolate(double progress, const Quaternion &other) const;
};
} // namespace reanimated::css
@@ -1,147 +0,0 @@
#pragma once
#include <reanimated/CSS/common/definitions.h>
#include <folly/dynamic.h>
#include <string>
#include <utility>
namespace reanimated::css {
class TransformMatrix {
public:
virtual ~TransformMatrix() = default;
virtual double determinant() const = 0;
virtual double &operator[](size_t index) = 0;
virtual const double &operator[](size_t index) const = 0;
virtual size_t getDimension() const = 0;
virtual std::string toString() const = 0;
virtual folly::dynamic toDynamic() const = 0;
};
template <typename TDerived, size_t TDimension>
class TransformMatrixBase : public TransformMatrix {
public:
static constexpr size_t SIZE = TDimension * TDimension;
using MatrixArray = std::array<double, SIZE>;
explicit TransformMatrixBase(MatrixArray matrix)
: matrix_(std::move(matrix)) {}
explicit TransformMatrixBase(jsi::Runtime &rt, const jsi::Value &value) {
const auto array = value.asObject(rt).asArray(rt);
if (array.size(rt) != SIZE) {
throw std::invalid_argument(
"[Reanimated] Matrix array should have " + std::to_string(SIZE) +
" elements");
}
for (size_t i = 0; i < SIZE; ++i) {
matrix_[i] = array.getValueAtIndex(rt, i).asNumber();
}
}
explicit TransformMatrixBase(const folly::dynamic &array) {
if (!array.isArray() || array.size() != SIZE) {
throw std::invalid_argument(
"[Reanimated] Matrix array should have " + std::to_string(SIZE) +
" elements");
}
for (size_t i = 0; i < SIZE; ++i) {
matrix_[i] = array[i].asDouble();
}
}
virtual bool operator==(const TDerived &other) const = 0;
double &operator[](size_t index) override {
return matrix_[index];
}
const double &operator[](size_t index) const override {
return matrix_[index];
}
TDerived operator*(const TDerived &rhs) const {
return TDerived(multiply(rhs));
}
TDerived &operator*=(const TDerived &rhs) {
matrix_ = multiply(rhs);
return static_cast<TDerived &>(*this);
}
std::string toString() const override {
std::string result = "[";
for (size_t i = 0; i < SIZE; ++i) {
result += std::to_string(matrix_[i]);
if (i < SIZE - 1) {
result += ", ";
}
}
result += "]";
return result;
}
folly::dynamic toDynamic() const override {
folly::dynamic result = folly::dynamic::array;
for (size_t i = 0; i < SIZE; ++i) {
result.push_back(matrix_[i]);
}
return result;
}
size_t getDimension() const override {
return TDimension;
}
bool isSingular() const {
return determinant() == 0;
}
bool normalize() {
const auto last = matrix_[SIZE - 1];
if (last == 0) {
return false;
}
if (last == 1) {
return true;
}
for (size_t i = 0; i < SIZE; ++i) {
matrix_[i] /= last;
}
return true;
}
void transpose() {
for (size_t i = 0; i < TDimension; ++i) {
for (size_t j = i + 1; j < TDimension; ++j) {
std::swap(matrix_[i * TDimension + j], matrix_[j * TDimension + i]);
}
}
}
protected:
std::array<double, SIZE> matrix_;
MatrixArray multiply(const TDerived &rhs) const {
std::array<double, SIZE> result{};
for (size_t i = 0; i < TDimension; ++i) {
for (size_t j = 0; j < TDimension; ++j) {
for (size_t k = 0; k < TDimension; ++k) {
result[i * TDimension + j] +=
matrix_[i * TDimension + k] * rhs[k * TDimension + j];
}
}
}
return result;
}
};
} // namespace reanimated::css
@@ -1,259 +0,0 @@
#include <reanimated/CSS/common/transforms/TransformMatrix2D.h>
namespace reanimated::css {
TransformMatrix2D::Decomposed TransformMatrix2D::Decomposed::interpolate(
const double progress,
const TransformMatrix2D::Decomposed &other) const {
return {
.scale = scale.interpolate(progress, other.scale),
.skew = skew + (other.skew - skew) * progress,
.rotation = rotation + (other.rotation - rotation) * progress,
.translation = translation.interpolate(progress, other.translation)};
}
#ifndef NDEBUG
std::ostream &operator<<(
std::ostream &os,
const TransformMatrix2D::Decomposed &decomposed) {
os << "TransformMatrix2D::Decomposed(scale=" << decomposed.scale
<< ", skew=" << decomposed.skew << ", rotation=" << decomposed.rotation
<< ", translation=" << decomposed.translation << ")";
return os;
}
#endif // NDEBUG
TransformMatrix2D TransformMatrix2D::Identity() {
// clang-format off
return TransformMatrix2D({
1, 0, 0,
0, 1, 0,
0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix2D TransformMatrix2D::create<TransformOp::Rotate>(double v) {
const auto cosVal = std::cos(v);
const auto sinVal = std::sin(v);
// clang-format off
return TransformMatrix2D({
cosVal, -sinVal, 0,
sinVal, cosVal, 0,
0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix2D TransformMatrix2D::create<TransformOp::Scale>(double v) {
// clang-format off
return TransformMatrix2D({
v, 0, 0,
0, v, 0,
0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix2D TransformMatrix2D::create<TransformOp::ScaleX>(double v) {
// clang-format off
return TransformMatrix2D({
v, 0, 0,
0, 1, 0,
0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix2D TransformMatrix2D::create<TransformOp::ScaleY>(double v) {
// clang-format off
return TransformMatrix2D({
1, 0, 0,
0, v, 0,
0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix2D TransformMatrix2D::create<TransformOp::TranslateX>(double v) {
// clang-format off
return TransformMatrix2D({
1, 0, 0,
0, 1, 0,
v, 0, 1
});
// clang-format on
}
template <>
TransformMatrix2D TransformMatrix2D::create<TransformOp::TranslateY>(double v) {
// clang-format off
return TransformMatrix2D({
1, 0, 0,
0, 1, 0,
0, v, 1
});
// clang-format on
}
template <>
TransformMatrix2D TransformMatrix2D::create<TransformOp::SkewX>(double v) {
const auto tanVal = std::tan(v);
// clang-format off
return TransformMatrix2D({
1, 0, 0,
tanVal, 1, 0,
0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix2D TransformMatrix2D::create<TransformOp::SkewY>(double v) {
const auto tanVal = std::tan(v);
// clang-format off
return TransformMatrix2D({
1, tanVal, 0,
0, 1, 0,
0, 0, 1
});
// clang-format on
}
template <TransformOp TOperation>
TransformMatrix2D TransformMatrix2D::create(double value) {
throw std::invalid_argument(
"[Reanimated] Cannot create TransformMatrix2D from: " +
getOperationNameFromType(TOperation));
}
bool TransformMatrix2D::operator==(const TransformMatrix2D &other) const {
return matrix_ == other.matrix_;
}
double TransformMatrix2D::determinant() const {
return (matrix_[0] * matrix_[4] * matrix_[8]) +
(matrix_[1] * matrix_[5] * matrix_[6]) +
(matrix_[2] * matrix_[3] * matrix_[7]) -
(matrix_[2] * matrix_[4] * matrix_[6]) -
(matrix_[1] * matrix_[3] * matrix_[8]) -
(matrix_[0] * matrix_[5] * matrix_[7]);
}
void TransformMatrix2D::translate2d(const Vector2D &translation) {
for (size_t i = 0; i < 3; ++i) {
matrix_[6 + i] +=
translation[0] * matrix_[i] + translation[1] * matrix_[3 + i];
}
}
void TransformMatrix2D::scale2d(const Vector2D &scale) {
for (size_t i = 0; i < 3; ++i) {
matrix_[i] *= scale[0];
matrix_[3 + i] *= scale[1];
}
}
std::optional<TransformMatrix2D::Decomposed> TransformMatrix2D::decompose()
const {
auto matrixCp = *this;
if (!matrixCp.normalize()) {
return std::nullopt;
}
const auto translation = matrixCp.getTranslation();
// Take the 2×2 linear part into two column vectors (col-major)
std::array<Vector2D, 2> rows;
for (size_t i = 0; i < 2; ++i) {
rows[i] = Vector2D(matrixCp[i * 3], matrixCp[i * 3 + 1]);
}
auto [scale, skew] = computeScaleAndSkew(rows);
// At this point, the matrix (in rows) is orthonormal.
// Check for a coordinate system flip. If the determinant
// is negative, then negate the matrix and the scaling factors.
if (rows[0].cross(rows[1]) < 0) {
scale *= -1;
for (auto &row : rows) {
row *= -1;
}
}
const auto rotation = computeRotation(rows);
return TransformMatrix2D::Decomposed{
.scale = scale,
.skew = skew,
.rotation = rotation,
.translation = translation};
}
TransformMatrix2D TransformMatrix2D::recompose(
const TransformMatrix2D::Decomposed &decomposed) {
auto result = TransformMatrix2D::Identity();
// Apply Translation
result.translate2d(decomposed.translation);
// Apply Rotation
const auto rotationMatrix =
TransformMatrix2D::create<TransformOp::Rotate>(decomposed.rotation);
result = rotationMatrix * result;
// Apply XY shear
if (decomposed.skew != 0) {
auto tmp = TransformMatrix2D::Identity();
tmp[3] = decomposed.skew;
result = tmp * result;
}
// Apply Scale
result.scale2d(decomposed.scale);
return result;
}
Vector2D TransformMatrix2D::getTranslation() const {
return Vector2D(matrix_[6], matrix_[7]);
}
std::pair<Vector2D, double> TransformMatrix2D::computeScaleAndSkew(
std::array<Vector2D, 2> &rows) {
Vector2D scale;
// Compute X scale and normalize first row
scale[0] = rows[0].length();
rows[0].normalize();
// Compute XY shear and orthogonalize second row against first
double skew = rows[0].dot(rows[1]);
rows[1] = rows[1].addScaled(rows[0], -skew);
// Now, compute Y scale and normalize second row
scale[1] = rows[1].length();
rows[1].normalize();
// Next, Normalize shear by Y scale
skew /= scale[1];
return {scale, skew};
}
double TransformMatrix2D::computeRotation(std::array<Vector2D, 2> &rows) {
// For 2D, we can compute rotation directly from the orthonormal matrix
// The rotation angle is atan2(m10, m00) where m10 is rows[1][0] and m00 is
// rows[0][0]
return std::atan2(rows[1][0], rows[0][0]);
}
} // namespace reanimated::css
@@ -1,58 +0,0 @@
#pragma once
#include <reanimated/CSS/common/transforms/TransformMatrix.h>
#include <reanimated/CSS/common/transforms/TransformOp.h>
#include <reanimated/CSS/common/transforms/vectors.h>
#include <folly/dynamic.h>
#include <utility>
namespace reanimated::css {
namespace {
static constexpr size_t MATRIX_2D_DIMENSION = 3; // 3x3 matrix
}
class TransformMatrix2D
: public TransformMatrixBase<TransformMatrix2D, MATRIX_2D_DIMENSION> {
public:
struct Decomposed {
Vector2D scale;
double skew;
double rotation;
Vector2D translation;
#ifndef NDEBUG
friend std::ostream &operator<<(
std::ostream &os,
const Decomposed &decomposed);
#endif // NDEBUG
Decomposed interpolate(double progress, const Decomposed &other) const;
};
using TransformMatrixBase<TransformMatrix2D, MATRIX_2D_DIMENSION>::
TransformMatrixBase;
static TransformMatrix2D Identity();
template <TransformOp TOperation>
static TransformMatrix2D create(double value);
bool operator==(const TransformMatrix2D &other) const override;
double determinant() const override;
void translate2d(const Vector2D &translation);
void scale2d(const Vector2D &scale);
std::optional<Decomposed> decompose() const;
static TransformMatrix2D recompose(const Decomposed &decomposed);
private:
Vector2D getTranslation() const;
static std::pair<Vector2D, double> computeScaleAndSkew(
std::array<Vector2D, 2> &rows);
static double computeRotation(std::array<Vector2D, 2> &rows);
};
} // namespace reanimated::css
@@ -1,583 +0,0 @@
#include <reanimated/CSS/common/transforms/TransformMatrix3D.h>
#include <reanimated/CSS/interpolation/transforms/TransformOperation.h>
#include <string>
namespace reanimated::css {
TransformMatrix3D::Decomposed TransformMatrix3D::Decomposed::interpolate(
const double progress,
const TransformMatrix3D::Decomposed &other) const {
return {
.scale = scale.interpolate(progress, other.scale),
.skew = skew.interpolate(progress, other.skew),
.quaternion = quaternion.interpolate(progress, other.quaternion),
.translation = translation.interpolate(progress, other.translation),
.perspective = perspective.interpolate(progress, other.perspective)};
}
#ifndef NDEBUG
std::ostream &operator<<(
std::ostream &os,
const TransformMatrix3D::Decomposed &decomposed) {
os << "TransformMatrix3D::Decomposed(scale=" << decomposed.scale
<< ", skew=" << decomposed.skew << ", quaternion=" << decomposed.quaternion
<< ", translation=" << decomposed.translation
<< ", perspective=" << decomposed.perspective << ")";
return os;
}
#endif // NDEBUG
TransformMatrix3D TransformMatrix3D::Identity() {
// clang-format off
return TransformMatrix3D({
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
});
// clang-format on
}
// Template specializations for TransformMatrix3D::create
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::Perspective>(
double v) {
if (v == 0) {
// Ignore perspective if it is invalid
return TransformMatrix3D::Identity();
}
// clang-format off
return TransformMatrix3D({
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, -1.0 / v,
0, 0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::RotateX>(double v) {
const auto cosVal = std::cos(v);
const auto sinVal = std::sin(v);
// clang-format off
return TransformMatrix3D({
1, 0, 0, 0,
0, cosVal, sinVal, 0,
0, -sinVal, cosVal, 0,
0, 0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::RotateY>(double v) {
const auto cosVal = std::cos(v);
const auto sinVal = std::sin(v);
// clang-format off
return TransformMatrix3D({
cosVal, 0, -sinVal, 0,
0, 1, 0, 0,
sinVal, 0, cosVal, 0,
0, 0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::RotateZ>(double v) {
const auto cosVal = std::cos(v);
const auto sinVal = std::sin(v);
// clang-format off
return TransformMatrix3D({
cosVal, sinVal, 0, 0,
-sinVal, cosVal, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::Rotate>(double v) {
return TransformMatrix3D::create<TransformOp::RotateZ>(v);
}
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::Scale>(double v) {
// clang-format off
return TransformMatrix3D({
v, 0, 0, 0,
0, v, 0, 0,
0, 0, v, 0,
0, 0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::ScaleX>(double v) {
// clang-format off
return TransformMatrix3D({
v, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::ScaleY>(double v) {
// clang-format off
return TransformMatrix3D({
1, 0, 0, 0,
0, v, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::TranslateX>(double v) {
// clang-format off
return TransformMatrix3D({
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
v, 0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::TranslateY>(double v) {
// clang-format off
return TransformMatrix3D({
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, v, 0, 1
});
// clang-format on
}
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::SkewX>(double v) {
const auto tanVal = std::tan(v);
// clang-format off
return TransformMatrix3D({
1, 0, 0, 0,
tanVal, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
});
// clang-format on
}
template <>
TransformMatrix3D TransformMatrix3D::create<TransformOp::SkewY>(double v) {
const auto tanVal = std::tan(v);
// clang-format off
return TransformMatrix3D({
1, tanVal, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
});
// clang-format on
}
template <TransformOp TOperation>
TransformMatrix3D TransformMatrix3D::create(double value) {
throw std::invalid_argument(
"[Reanimated] Cannot create TransformMatrix3D from: " +
getOperationNameFromType(TOperation));
}
bool TransformMatrix3D::operator==(const TransformMatrix3D &other) const {
return matrix_ == other.matrix_;
}
Vector4D operator*(const Vector4D &v, const TransformMatrix3D &m) {
Vector4D result;
for (size_t i = 0; i < 4; ++i) {
for (size_t j = 0; j < 4; ++j) {
result[i] += v[j] * m[j * 4 + i];
}
}
return result;
}
#ifndef NDEBUG
std::ostream &operator<<(std::ostream &os, const TransformMatrix3D &matrix) {
std::string result = "TransformMatrix3D{";
for (size_t i = 0; i < 16; ++i) {
result += std::to_string(matrix[i]);
if (i < 15) {
result += ", ";
}
}
result += "}";
return os << result;
}
#endif // NDEBUG
/**
* Calculates the determinant of the 4x4 matrix using the minor (Laplace
* expansion) method.
*
* | a1 a2 a3 a4 |
* | b1 b2 b3 b4 |
* | c1 c2 c3 c4 |
* | d1 d2 d3 d4 |
*/
double TransformMatrix3D::determinant() const {
const double a1 = matrix_[0];
const double b1 = matrix_[1];
const double c1 = matrix_[2];
const double d1 = matrix_[3];
const double a2 = matrix_[4];
const double b2 = matrix_[5];
const double c2 = matrix_[6];
const double d2 = matrix_[7];
const double a3 = matrix_[8];
const double b3 = matrix_[9];
const double c3 = matrix_[10];
const double d3 = matrix_[11];
const double a4 = matrix_[12];
const double b4 = matrix_[13];
const double c4 = matrix_[14];
const double d4 = matrix_[15];
return a1 * determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4) -
b1 * determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4) +
c1 * determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4) -
d1 * determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
}
void TransformMatrix3D::adjugate() {
const double a1 = matrix_[0];
const double b1 = matrix_[1];
const double c1 = matrix_[2];
const double d1 = matrix_[3];
const double a2 = matrix_[4];
const double b2 = matrix_[5];
const double c2 = matrix_[6];
const double d2 = matrix_[7];
const double a3 = matrix_[8];
const double b3 = matrix_[9];
const double c3 = matrix_[10];
const double d3 = matrix_[11];
const double a4 = matrix_[12];
const double b4 = matrix_[13];
const double c4 = matrix_[14];
const double d4 = matrix_[15];
matrix_[0] = determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4);
matrix_[4] = -determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4);
matrix_[8] = determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4);
matrix_[12] = -determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4);
matrix_[1] = -determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4);
matrix_[5] = determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4);
matrix_[9] = -determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4);
matrix_[13] = determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4);
matrix_[2] = determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4);
matrix_[6] = -determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4);
matrix_[10] = determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4);
matrix_[14] = -determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4);
matrix_[3] = -determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3);
matrix_[7] = determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3);
matrix_[11] = -determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3);
matrix_[15] = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3);
}
bool TransformMatrix3D::invert() {
const auto det = determinant();
// If the determinant is invalid (zero, very small number, etc.), then the
// matrix is not invertible
if (!std::isnormal(det)) {
return false;
}
adjugate();
for (size_t i = 0; i < 16; ++i) {
matrix_[i] /= det;
}
return true;
}
void TransformMatrix3D::translate3d(const Vector3D &translation) {
for (size_t i = 0; i < 4; ++i) {
matrix_[12 + i] += translation[0] * matrix_[i] +
translation[1] * matrix_[4 + i] + translation[2] * matrix_[8 + i];
}
}
void TransformMatrix3D::scale3d(const Vector3D &scale) {
for (size_t i = 0; i < 4; ++i) {
matrix_[i] *= scale[0];
matrix_[4 + i] *= scale[1];
matrix_[8 + i] *= scale[2];
}
}
std::optional<TransformMatrix3D::Decomposed> TransformMatrix3D::decompose()
const {
auto matrixCp = *this;
if (!matrixCp.normalize()) {
return std::nullopt;
}
const auto perspective = matrixCp.computePerspective();
if (!perspective) {
return std::nullopt;
}
const auto translation = matrixCp.getTranslation();
// Move the remaining matrix to 3 separate column vectors for easier
// processing
std::array<Vector3D, 3> rows;
for (size_t i = 0; i < 3; ++i) {
rows[i] =
Vector3D(matrixCp[i * 4], matrixCp[i * 4 + 1], matrixCp[i * 4 + 2]);
}
auto [scale, skew] = computeScaleAndSkew(rows);
// At this point, the matrix (in rows) is orthonormal.
// Check for a coordinate system flip. If the determinant
// is negative, then negate the matrix and the scaling factors.
if (rows[0].dot(rows[1].cross(rows[2])) < 0) {
scale *= -1;
for (auto &row : rows) {
row *= -1;
}
}
const auto rotation = computeQuaternion(rows);
return TransformMatrix3D::Decomposed{
.scale = scale,
.skew = skew,
.quaternion = rotation,
.translation = translation,
.perspective = perspective.value()};
}
TransformMatrix3D TransformMatrix3D::recompose(
const TransformMatrix3D::Decomposed &decomposed) {
auto result = TransformMatrix3D::Identity();
// Start from applying perspective
for (size_t i = 0; i < 4; ++i) {
result[3 + i * 4] = decomposed.perspective[i];
}
// Apply translation
result.translate3d(decomposed.translation);
// Apply rotation
result = fromQuaternion(decomposed.quaternion) * result;
// Apply skew
auto hasSkewYZ = decomposed.skew[2] != 0;
auto hasSkewXZ = decomposed.skew[1] != 0;
auto hasSkewXY = decomposed.skew[0] != 0;
if (hasSkewYZ || hasSkewXZ || hasSkewXY) {
auto tmp = TransformMatrix3D::Identity();
if (hasSkewYZ) { // YZ
tmp[9] = decomposed.skew[2];
result = tmp * result;
}
if (hasSkewXZ) { // XZ
tmp[8] = decomposed.skew[1];
result = tmp * result;
}
if (hasSkewXY) { // XY
tmp[4] = decomposed.skew[0];
result = tmp * result;
}
}
// Apply scale
result.scale3d(decomposed.scale);
return result;
}
TransformMatrix3D TransformMatrix3D::fromQuaternion(const Quaternion &q) {
const double xx = q.x * q.x;
const double yy = q.y * q.y;
const double zz = q.z * q.z;
const double xz = q.x * q.z;
const double xy = q.x * q.y;
const double yz = q.y * q.z;
const double xw = q.w * q.x;
const double yw = q.w * q.y;
const double zw = q.w * q.z;
// clang-format off
return TransformMatrix3D({
1 - 2 * (yy + zz), 2 * (xy - zw), 2 * (xz + yw), 0,
2 * (xy + zw), 1 - 2 * (xx + zz), 2 * (yz - xw), 0,
2 * (xz - yw), 2 * (yz + xw), 1 - 2 * (xx + yy), 0,
0, 0, 0, 1
});
// clang-format on
}
std::optional<Vector4D> TransformMatrix3D::computePerspective() const {
auto perspectiveMatrix = *this;
for (size_t i = 0; i < 3; ++i) {
perspectiveMatrix[3 + i * 4] = 0;
}
perspectiveMatrix[15] = 1;
if (perspectiveMatrix.isSingular()) {
return std::nullopt;
}
if (matrix_[3] == 0 && matrix_[7] == 0 && matrix_[11] == 0) {
// No perspective
return Vector4D{0, 0, 0, 1};
}
// Invert and transpose the perspective matrix (we will solve the equation
// by multiplying the rhs vector by the inverse of the perspective matrix)
if (!perspectiveMatrix.invert()) {
return std::nullopt;
}
perspectiveMatrix.transpose();
// rhs is the right hand side of the equation we are trying to solve
const Vector4D rhs(matrix_[3], matrix_[7], matrix_[11], matrix_[15]);
// Solve the equation
return rhs * perspectiveMatrix;
}
Vector3D TransformMatrix3D::getTranslation() const {
return Vector3D(matrix_[12], matrix_[13], matrix_[14]);
}
std::pair<Vector3D, Vector3D> TransformMatrix3D::computeScaleAndSkew(
std::array<Vector3D, 3> &rows) {
Vector3D scale, skew;
// Compute X scale factor and normalize first row
scale[0] = rows[0].length();
rows[0].normalize();
// Compute XY shear factor and make 2nd row orthogonal to 1st.
skew[0] = rows[0].dot(rows[1]);
rows[1] = rows[1].addScaled(rows[0], -skew[0]);
// Now, compute Y scale and normalize 2nd row.
scale[1] = rows[1].length();
rows[1].normalize();
skew[0] /= scale[1]; // normalize XY shear
// Compute XZ and YZ shears, orthogonalize 3rd row
skew[1] = rows[0].dot(rows[2]);
rows[2] = rows[2].addScaled(rows[0], -skew[1]);
skew[2] = rows[1].dot(rows[2]);
rows[2] = rows[2].addScaled(rows[1], -skew[2]);
// Next, get Z scale and normalize 3rd row
scale[2] = rows[2].length();
rows[2].normalize();
skew[1] /= scale[2]; // normalize XZ shear
skew[2] /= scale[2]; // normalize YZ shear
return {scale, skew};
}
Quaternion TransformMatrix3D::computeQuaternion(std::array<Vector3D, 3> &rows) {
double m00 = rows[0][0];
double m01 = rows[0][1];
double m02 = rows[0][2];
double m10 = rows[1][0];
double m11 = rows[1][1];
double m12 = rows[1][2];
double m20 = rows[2][0];
double m21 = rows[2][1];
double m22 = rows[2][2];
Quaternion q;
double trace = m00 + m11 + m22; // Trace of the matrix
if (trace > 0.0) {
double s = 0.5 / sqrt(trace + 1.0);
q.w = 0.25 / s;
q.x = (m21 - m12) * s;
q.y = (m02 - m20) * s;
q.z = (m10 - m01) * s;
} else {
if (m00 > m11 && m00 > m22) {
double s = 2.0 * sqrt(1.0 + m00 - m11 - m22);
q.w = (m21 - m12) / s;
q.x = 0.25 * s;
q.y = (m01 + m10) / s;
q.z = (m02 + m20) / s;
} else if (m11 > m22) {
double s = 2.0 * sqrt(1.0 + m11 - m00 - m22);
q.w = (m02 - m20) / s;
q.x = (m01 + m10) / s;
q.y = 0.25 * s;
q.z = (m12 + m21) / s;
} else {
double s = 2.0 * sqrt(1.0 + m22 - m00 - m11);
q.w = (m10 - m01) / s;
q.x = (m02 + m20) / s;
q.y = (m12 + m21) / s;
q.z = 0.25 * s;
}
}
return q;
}
/**
* Calculate the determinant of a 3x3 matrix
*
* | a b c |
* | d e f |
* | g h i |
*/
double TransformMatrix3D::determinant3x3(
const double a,
const double b,
const double c,
const double d,
const double e,
const double f,
const double g,
const double h,
const double i) {
return (a * e * i) + (b * f * g) + (c * d * h) - (c * e * g) - (b * d * i) -
(a * f * h);
}
} // namespace reanimated::css
@@ -1,84 +0,0 @@
#pragma once
#include <reanimated/CSS/common/transforms/Quaternion.h>
#include <reanimated/CSS/common/transforms/TransformMatrix.h>
#include <reanimated/CSS/common/transforms/TransformOp.h>
#include <reanimated/CSS/common/transforms/vectors.h>
#include <folly/dynamic.h>
#include <utility>
namespace reanimated::css {
namespace {
static constexpr size_t MATRIX_3D_DIMENSION = 4; // 4x4 matrix
}
class TransformMatrix3D
: public TransformMatrixBase<TransformMatrix3D, MATRIX_3D_DIMENSION> {
public:
struct Decomposed {
Vector3D scale;
Vector3D skew;
Quaternion quaternion;
Vector3D translation;
Vector4D perspective;
#ifndef NDEBUG
friend std::ostream &operator<<(
std::ostream &os,
const Decomposed &decomposed);
#endif // NDEBUG
Decomposed interpolate(double progress, const Decomposed &other) const;
};
using TransformMatrixBase<TransformMatrix3D, MATRIX_3D_DIMENSION>::
TransformMatrixBase;
static TransformMatrix3D Identity();
template <TransformOp TOperation>
static TransformMatrix3D create(double value);
bool operator==(const TransformMatrix3D &other) const override;
#ifndef NDEBUG
friend std::ostream &operator<<(
std::ostream &os,
const TransformMatrix3D &matrix);
#endif // NDEBUG
double determinant() const override;
void adjugate();
bool invert();
void translate3d(const Vector3D &translation);
void scale3d(const Vector3D &scale);
std::optional<Decomposed> decompose() const;
static TransformMatrix3D recompose(const Decomposed &decomposed);
static TransformMatrix3D fromQuaternion(const Quaternion &q);
private:
std::optional<Vector4D> computePerspective() const;
Vector3D getTranslation() const;
static std::pair<Vector3D, Vector3D> computeScaleAndSkew(
std::array<Vector3D, 3> &rows);
static Quaternion computeQuaternion(std::array<Vector3D, 3> &columns);
inline static double determinant3x3(
double a,
double b,
double c,
double d,
double e,
double f,
double g,
double h,
double i);
};
Vector4D operator*(const Vector4D &v, const TransformMatrix3D &m);
} // namespace reanimated::css
@@ -1,52 +0,0 @@
#include <reanimated/CSS/common/transforms/TransformOp.h>
#include <array>
#include <unordered_map>
namespace reanimated::css {
constexpr std::array<const char *, 13> transformOperationStrings = {
"perspective",
"rotate",
"rotateX",
"rotateY",
"rotateZ",
"scale",
"scaleX",
"scaleY",
"translateX",
"translateY",
"skewX",
"skewY",
"matrix"};
TransformOp getTransformOperationType(const std::string &property) {
static const std::unordered_map<std::string, TransformOp> stringToEnumMap = {
{"perspective", TransformOp::Perspective},
{"rotate", TransformOp::Rotate},
{"rotateX", TransformOp::RotateX},
{"rotateY", TransformOp::RotateY},
{"rotateZ", TransformOp::RotateZ},
{"scale", TransformOp::Scale},
{"scaleX", TransformOp::ScaleX},
{"scaleY", TransformOp::ScaleY},
{"translateX", TransformOp::TranslateX},
{"translateY", TransformOp::TranslateY},
{"skewX", TransformOp::SkewX},
{"skewY", TransformOp::SkewY},
{"matrix", TransformOp::Matrix}};
auto it = stringToEnumMap.find(property);
if (it != stringToEnumMap.end()) {
return it->second;
} else {
throw std::invalid_argument(
"[Reanimated] Unknown transform operation: " + property);
}
}
std::string getOperationNameFromType(const TransformOp type) {
return transformOperationStrings[static_cast<size_t>(type)];
}
} // namespace reanimated::css
@@ -1,27 +0,0 @@
#pragma once
#include <string>
namespace reanimated::css {
enum class TransformOp {
Perspective,
Rotate,
RotateX,
RotateY,
RotateZ,
Scale,
ScaleX,
ScaleY,
TranslateX,
TranslateY,
SkewX,
SkewY,
Matrix,
};
TransformOp getTransformOperationType(const std::string &property);
std::string getOperationNameFromType(const TransformOp type);
} // namespace reanimated::css
@@ -1,166 +0,0 @@
#include <reanimated/CSS/common/transforms/vectors.h>
namespace reanimated::css {
// Vector2D
double &Vector2D::operator[](const size_t idx) {
return vec[idx];
}
const double &Vector2D::operator[](const size_t idx) const {
return vec[idx];
}
Vector2D &Vector2D::operator*=(const double scalar) {
vec[0] *= scalar;
vec[1] *= scalar;
return *this;
}
#ifndef NDEBUG
std::ostream &operator<<(std::ostream &os, const Vector2D &vector) {
os << "Vector2D(" << vector.vec[0] << ", " << vector.vec[1] << ")";
return os;
}
#endif // NDEBUG
double Vector2D::length() const {
return std::hypot(vec[0], vec[1]);
}
void Vector2D::scaleToLength(double targetLength) {
double currentLength = length();
if (currentLength != 0) {
double factor = targetLength / currentLength;
vec[0] *= factor;
vec[1] *= factor;
}
}
void Vector2D::normalize() {
scaleToLength(1);
}
double Vector2D::dot(const Vector2D &other) const {
return vec[0] * other.vec[0] + vec[1] * other.vec[1];
}
double Vector2D::cross(const Vector2D &other) const {
return vec[0] * other.vec[1] - vec[1] * other.vec[0];
}
Vector2D Vector2D::addScaled(const Vector2D &other, const double scale) const {
return Vector2D{vec[0] + scale * other.vec[0], vec[1] + scale * other.vec[1]};
}
Vector2D Vector2D::interpolate(const double progress, const Vector2D &other)
const {
return Vector2D{
vec[0] + progress * (other.vec[0] - vec[0]),
vec[1] + progress * (other.vec[1] - vec[1])};
}
// Vector3D
double &Vector3D::operator[](const size_t idx) {
return vec[idx];
}
const double &Vector3D::operator[](const size_t idx) const {
return vec[idx];
}
Vector3D &Vector3D::operator*=(const double scalar) {
vec[0] *= scalar;
vec[1] *= scalar;
vec[2] *= scalar;
return *this;
}
#ifndef NDEBUG
std::ostream &operator<<(std::ostream &os, const Vector3D &vector) {
os << "Vector3D(" << vector.vec[0] << ", " << vector.vec[1] << ", "
<< vector.vec[2] << ")";
return os;
}
#endif // NDEBUG
double Vector3D::length() const {
return std::hypot(vec[0], vec[1], vec[2]);
}
void Vector3D::scaleToLength(double targetLength) {
double currentLength = length();
if (currentLength != 0) {
double factor = targetLength / currentLength;
vec[0] *= factor;
vec[1] *= factor;
vec[2] *= factor;
}
}
void Vector3D::normalize() {
scaleToLength(1);
}
double Vector3D::dot(const Vector3D &other) const {
return vec[0] * other.vec[0] + vec[1] * other.vec[1] + vec[2] * other.vec[2];
}
Vector3D Vector3D::cross(const Vector3D &other) const {
return Vector3D{
vec[1] * other.vec[2] - vec[2] * other.vec[1],
vec[2] * other.vec[0] - vec[0] * other.vec[2],
vec[0] * other.vec[1] - vec[1] * other.vec[0]};
}
Vector3D Vector3D::addScaled(const Vector3D &other, const double scale) const {
return Vector3D{
vec[0] + scale * other.vec[0],
vec[1] + scale * other.vec[1],
vec[2] + scale * other.vec[2]};
}
Vector3D Vector3D::interpolate(const double progress, const Vector3D &other)
const {
return Vector3D{
vec[0] + progress * (other.vec[0] - vec[0]),
vec[1] + progress * (other.vec[1] - vec[1]),
vec[2] + progress * (other.vec[2] - vec[2])};
}
// Vector4D
double &Vector4D::operator[](const size_t idx) {
return vec[idx];
}
const double &Vector4D::operator[](const size_t idx) const {
return vec[idx];
}
Vector4D Vector4D::interpolate(const double progress, const Vector4D &other)
const {
return Vector4D{
vec[0] + progress * (other.vec[0] - vec[0]),
vec[1] + progress * (other.vec[1] - vec[1]),
vec[2] + progress * (other.vec[2] - vec[2]),
vec[3] + progress * (other.vec[3] - vec[3])};
}
#ifndef NDEBUG
std::ostream &operator<<(std::ostream &os, const Vector4D &vector) {
os << "Vector4D(" << vector.vec[0] << ", " << vector.vec[1] << ", "
<< vector.vec[2] << ", " << vector.vec[3] << ")";
return os;
}
#endif // NDEBUG
} // namespace reanimated::css
@@ -1,77 +0,0 @@
#pragma once
#include <array>
#ifndef NDEBUG
#include <iostream>
#endif // NDEBUG
namespace reanimated::css {
struct Vector2D {
std::array<double, 2> vec;
Vector2D() : vec({0, 0}) {}
explicit Vector2D(double x, double y) : vec({x, y}) {}
explicit Vector2D(std::array<double, 2> vec) : vec(vec) {}
double &operator[](size_t idx);
const double &operator[](size_t idx) const;
Vector2D &operator*=(double scalar);
#ifndef NDEBUG
friend std::ostream &operator<<(std::ostream &os, const Vector2D &vector);
#endif // NDEBUG
double length() const;
void scaleToLength(double targetLength);
void normalize();
double dot(const Vector2D &other) const;
double cross(const Vector2D &other) const;
Vector2D addScaled(const Vector2D &other, double scale) const;
Vector2D interpolate(double progress, const Vector2D &other) const;
};
struct Vector3D {
std::array<double, 3> vec;
Vector3D() : vec({0, 0, 0}) {}
explicit Vector3D(double x, double y, double z) : vec({x, y, z}) {}
explicit Vector3D(std::array<double, 3> vec) : vec(vec) {}
double &operator[](size_t idx);
const double &operator[](size_t idx) const;
Vector3D &operator*=(double scalar);
#ifndef NDEBUG
friend std::ostream &operator<<(std::ostream &os, const Vector3D &vector);
#endif // NDEBUG
double length() const;
void scaleToLength(double targetLength);
void normalize();
double dot(const Vector3D &other) const;
Vector3D cross(const Vector3D &other) const;
Vector3D addScaled(const Vector3D &other, double scale) const;
Vector3D interpolate(double progress, const Vector3D &other) const;
};
struct Vector4D {
std::array<double, 4> vec;
Vector4D() : vec({0, 0, 0, 0}) {}
explicit Vector4D(double x, double y, double z, double w)
: vec({x, y, z, w}) {}
explicit Vector4D(std::array<double, 4> vec) : vec(vec) {}
double &operator[](size_t idx);
const double &operator[](size_t idx) const;
#ifndef NDEBUG
friend std::ostream &operator<<(std::ostream &os, const Vector4D &vector);
#endif // NDEBUG
Vector4D interpolate(double progress, const Vector4D &other) const;
};
} // namespace reanimated::css
@@ -1,107 +0,0 @@
#include <reanimated/CSS/common/values/CSSAngle.h>
#include <iomanip>
#include <regex>
#include <unordered_map>
namespace reanimated::css {
CSSAngle::CSSAngle() : value(0) {}
CSSAngle::CSSAngle(const double value) : value(value) {}
CSSAngle::CSSAngle(const std::string &rotationString) {
static const std::regex validNumberRegex(R"(^[-+]?\d*\.?\d+$)");
static const std::unordered_map<std::string, double> unitFactors = {
{"rad", 1},
{"deg", M_PI / 180},
};
// Find position of the first non-numeric character (first character
// of the unit, e.g. "deg" or "rad")
size_t pos = rotationString.find_first_not_of("0123456789.-+");
if (pos == std::string::npos) {
throw std::invalid_argument(
"[Reanimated] CSSAngle: Invalid angle value: " + rotationString);
}
std::string numericPart = rotationString.substr(0, pos);
std::string unitPart = rotationString.substr(pos);
if (!std::regex_match(numericPart, validNumberRegex)) {
throw std::invalid_argument(
"[Reanimated] CSSAngle: Invalid angle value: " + rotationString);
}
// Lookup the unit and convert to radians
auto it = unitFactors.find(unitPart);
if (it == unitFactors.cend()) {
throw std::invalid_argument(
"[Reanimated] CSSAngle: Invalid angle unit: " + unitPart);
}
double numericValue = std::stod(numericPart);
this->value = numericValue * it->second;
}
CSSAngle::CSSAngle(const char *cstr) : CSSAngle(std::string_view{cstr}) {}
CSSAngle::CSSAngle(jsi::Runtime &rt, const jsi::Value &jsiValue) {
if (!jsiValue.isString()) {
throw std::invalid_argument(
"[Reanimated] CSSAngle: Invalid value type: " +
stringifyJSIValue(rt, jsiValue));
}
std::string strValue = jsiValue.asString(rt).utf8(rt);
*this = CSSAngle(strValue);
}
CSSAngle::CSSAngle(const folly::dynamic &value) {
if (!value.isString()) {
throw std::invalid_argument(
"[Reanimated] CSSAngle: Invalid value type: " + folly::toJson(value));
}
*this = CSSAngle(value.asString());
}
bool CSSAngle::canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue) {
// TODO - improve canConstruct check and add check for string correctness
return jsiValue.isString() || jsiValue.isNumber();
}
bool CSSAngle::canConstruct(const folly::dynamic &value) {
return value.isString() || value.isNumber();
}
folly::dynamic CSSAngle::toDynamic() const {
return folly::dynamic(toString());
}
std::string CSSAngle::toString() const {
std::ostringstream stream;
stream << std::fixed << std::setprecision(4) << value;
return stream.str() + "rad";
}
CSSAngle CSSAngle::interpolate(double progress, const CSSAngle &to) const {
return CSSAngle(value + (to.value - value) * progress);
}
bool CSSAngle::operator==(const CSSAngle &other) const {
return value == other.value;
}
#ifndef NDEBUG
std::ostream &operator<<(std::ostream &os, const CSSAngle &angleValue) {
os << "CSSAngle(" << angleValue.value << ")";
return os;
}
#endif // NDEBUG
} // namespace reanimated::css
@@ -1,38 +0,0 @@
#pragma once
#include <reanimated/CSS/common/values/CSSValue.h>
#include <worklets/Tools/JSISerializer.h>
#include <folly/json.h>
#include <string>
namespace reanimated::css {
using namespace worklets;
struct CSSAngle : public CSSSimpleValue<CSSAngle> {
double value;
CSSAngle();
explicit CSSAngle(double value);
explicit CSSAngle(const std::string &rotationString);
explicit CSSAngle(const char *cstr);
explicit CSSAngle(jsi::Runtime &rt, const jsi::Value &jsiValue);
explicit CSSAngle(const folly::dynamic &value);
static bool canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue);
static bool canConstruct(const folly::dynamic &value);
folly::dynamic toDynamic() const override;
std::string toString() const override;
CSSAngle interpolate(double progress, const CSSAngle &to) const override;
bool operator==(const CSSAngle &other) const;
#ifndef NDEBUG
friend std::ostream &operator<<(std::ostream &os, const CSSAngle &angleValue);
#endif // NDEBUG
};
} // namespace reanimated::css
@@ -1,48 +0,0 @@
#include <reanimated/CSS/common/values/CSSBoolean.h>
namespace reanimated::css {
CSSBoolean::CSSBoolean() : value(false) {}
CSSBoolean::CSSBoolean(bool value) : value(value) {}
CSSBoolean::CSSBoolean(jsi::Runtime &rt, const jsi::Value &jsiValue)
: value(jsiValue.asBool()) {}
CSSBoolean::CSSBoolean(const folly::dynamic &value) : value(value.asBool()) {}
bool CSSBoolean::canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue) {
return jsiValue.isBool();
}
bool CSSBoolean::canConstruct(const folly::dynamic &value) {
return value.isBool();
}
folly::dynamic CSSBoolean::toDynamic() const {
return {value};
}
std::string CSSBoolean::toString() const {
return value ? "true" : "false";
}
CSSBoolean CSSBoolean::interpolate(double progress, const CSSBoolean &to)
const {
return CSSBoolean(progress < 0.5 ? value : to.value);
}
bool CSSBoolean::operator==(const CSSBoolean &other) const {
return value == other.value;
}
#ifndef NDEBUG
std::ostream &operator<<(std::ostream &os, const CSSBoolean &boolValue) {
os << boolValue.toString();
return os;
}
#endif // NDEBUG
} // namespace reanimated::css
@@ -1,33 +0,0 @@
#pragma once
#include <reanimated/CSS/common/values/CSSValue.h>
#include <string>
namespace reanimated::css {
struct CSSBoolean : public CSSSimpleValue<CSSBoolean> {
bool value;
CSSBoolean();
explicit CSSBoolean(bool value);
explicit CSSBoolean(jsi::Runtime &rt, const jsi::Value &jsiValue);
explicit CSSBoolean(const folly::dynamic &value);
static bool canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue);
static bool canConstruct(const folly::dynamic &value);
folly::dynamic toDynamic() const override;
std::string toString() const override;
CSSBoolean interpolate(double progress, const CSSBoolean &to) const override;
bool operator==(const CSSBoolean &other) const;
#ifndef NDEBUG
friend std::ostream &operator<<(
std::ostream &os,
const CSSBoolean &boolValue);
#endif // NDEBUG
};
} // namespace reanimated::css
@@ -1,173 +0,0 @@
#include <reanimated/CSS/common/values/CSSColor.h>
namespace reanimated::css {
CSSColor::CSSColor()
: channels{0, 0, 0, 0}, colorType(ColorType::Transparent) {}
CSSColor::CSSColor(ColorType colorType)
: channels{0, 0, 0, 0}, colorType(colorType) {}
CSSColor::CSSColor(int64_t numberValue)
: channels{0, 0, 0, 0}, colorType(ColorType::Rgba) {
uint32_t color;
// On Android, colors are represented as signed 32-bit integers. In JS, we use
// a bitwise operation (normalizedColor = normalizedColor | 0x0) to ensure the
// value is treated as a signed int, causing numbers above 2^31 to become
// negative. To correctly interpret these in C++, we cast negative values to
// int32_t to preserve their bit pattern, then assign to uint32_t. This wraps
// the bits (modulo 2^32), effectively reversing the JS-side bit shift.
if (numberValue < 0) {
color = static_cast<int32_t>(numberValue);
} else {
color = static_cast<uint32_t>(numberValue);
}
channels[0] = (color >> 16) & 0xFF; // Red
channels[1] = (color >> 8) & 0xFF; // Green
channels[2] = color & 0xFF; // Blue
channels[3] = (color >> 24) & 0xFF; // Alpha
colorType = ColorType::Rgba;
}
CSSColor::CSSColor(const std::string &colorString)
: channels{0, 0, 0, 0}, colorType(ColorType::Transparent) {
if (colorString == "transparent") {
colorType = ColorType::Transparent;
} else if (colorString == "currentColor") {
colorType = ColorType::CurrentColor;
} else {
throw std::invalid_argument(
"[Reanimated] CSSColor: Invalid string value: " + colorString);
}
}
CSSColor::CSSColor(const uint8_t r, const uint8_t g, const uint8_t b)
: channels{r, g, b, 255}, colorType(ColorType::Rgba) {}
CSSColor::CSSColor(
const uint8_t r,
const uint8_t g,
const uint8_t b,
const uint8_t a)
: channels{r, g, b, a}, colorType(ColorType::Rgba) {}
CSSColor::CSSColor(const ColorChannels &colorChannels)
: channels{colorChannels[0], colorChannels[1], colorChannels[2], colorChannels[3]},
colorType(ColorType::Rgba) {}
CSSColor::CSSColor(jsi::Runtime &rt, const jsi::Value &jsiValue)
: channels{0, 0, 0, 0}, colorType(ColorType::Transparent) {
if (jsiValue.isNumber()) {
*this = CSSColor(jsiValue.getNumber());
} else if (jsiValue.isString()) {
*this = CSSColor(jsiValue.getString(rt).utf8(rt));
} else if (jsiValue.isUndefined()) {
*this = Transparent;
} else {
throw std::invalid_argument(
"[Reanimated] CSSColor: Invalid value: " +
stringifyJSIValue(rt, jsiValue));
}
}
CSSColor::CSSColor(const folly::dynamic &value)
: channels{0, 0, 0, 0}, colorType(ColorType::Transparent) {
if (value.isNumber()) {
*this = CSSColor(value.asInt());
} else if (value.isString()) {
*this = CSSColor(value.getString());
} else if (value.empty()) {
*this = Transparent;
} else {
throw std::invalid_argument(
"[Reanimated] CSSColor: Invalid value: " + folly::toJson(value));
}
}
bool CSSColor::canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue) {
return jsiValue.isNumber() || jsiValue.isUndefined() ||
(jsiValue.isString() &&
isValidColorString(jsiValue.getString(rt).utf8(rt)));
}
bool CSSColor::canConstruct(const folly::dynamic &value) {
return value.isNumber() || value.empty() ||
(value.isString() && isValidColorString(value.getString()));
}
folly::dynamic CSSColor::toDynamic() const {
if (colorType == ColorType::Transparent) {
return 0x00000000;
}
return (channels[3] << 24) | (channels[0] << 16) | (channels[1] << 8) |
channels[2];
}
std::string CSSColor::toString() const {
if (colorType == ColorType::Rgba) {
return "rgba(" + std::to_string(channels[0]) + "," +
std::to_string(channels[1]) + "," + std::to_string(channels[2]) + "," +
std::to_string(channels[3]) + ")";
}
if (colorType == ColorType::CurrentColor) {
return "currentColor";
}
return "transparent";
}
CSSColor CSSColor::interpolate(const double progress, const CSSColor &to)
const {
if ((to.colorType == ColorType::Transparent &&
colorType == ColorType::Transparent) ||
colorType == ColorType::CurrentColor ||
to.colorType == ColorType::CurrentColor) {
return progress < 0.5 ? *this : to;
}
ColorChannels fromChannels = channels;
ColorChannels toChannels = to.channels;
if (colorType == ColorType::Transparent) {
fromChannels = {toChannels[0], toChannels[1], toChannels[2], 0};
} else if (to.colorType == ColorType::Transparent) {
toChannels = {fromChannels[0], fromChannels[1], fromChannels[2], 0};
}
ColorChannels resultChannels;
for (size_t i = 0; i < 4; ++i) {
resultChannels[i] =
interpolateChannel(fromChannels[i], toChannels[i], progress);
}
return CSSColor(resultChannels);
}
uint8_t CSSColor::interpolateChannel(
const uint8_t from,
const uint8_t to,
const double progress) {
// Cast one of operands to double to avoid unsigned int subtraction overflow
// (when from > to)
double interpolated = (static_cast<double>(to) - from) * progress + from;
return static_cast<uint8_t>(std::round(std::clamp(interpolated, 0.0, 255.0)));
}
bool CSSColor::operator==(const CSSColor &other) const {
return colorType == other.colorType && channels[0] == other.channels[0] &&
channels[1] == other.channels[1] && channels[2] == other.channels[2] &&
channels[3] == other.channels[3];
}
#ifndef NDEBUG
std::ostream &operator<<(std::ostream &os, const CSSColor &colorValue) {
os << "CSSColor(" << colorValue.toString() << ")";
return os;
}
#endif // NDEBUG
bool CSSColor::isValidColorString(const std::string &colorString) {
return colorString == "transparent" || colorString == "currentColor";
}
} // namespace reanimated::css
@@ -1,60 +0,0 @@
#pragma once
#include <reanimated/CSS/common/definitions.h>
#include <reanimated/CSS/common/values/CSSValue.h>
#include <worklets/Tools/JSISerializer.h>
#include <folly/json.h>
#include <string>
namespace reanimated::css {
using namespace worklets;
enum class ColorType {
Rgba,
Transparent,
CurrentColor, // for SVG
};
struct CSSColor : public CSSSimpleValue<CSSColor> {
ColorChannels channels;
ColorType colorType;
static const CSSColor Transparent;
CSSColor();
explicit CSSColor(ColorType colorType);
explicit CSSColor(int64_t numberValue);
explicit CSSColor(const std::string &colorString);
explicit CSSColor(uint8_t r, uint8_t g, uint8_t b);
explicit CSSColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
explicit CSSColor(const ColorChannels &colorChannels);
explicit CSSColor(jsi::Runtime &rt, const jsi::Value &jsiValue);
explicit CSSColor(const folly::dynamic &value);
static bool canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue);
static bool canConstruct(const folly::dynamic &value);
folly::dynamic toDynamic() const override;
std::string toString() const override;
CSSColor interpolate(double progress, const CSSColor &to) const override;
static uint8_t interpolateChannel(uint8_t from, uint8_t to, double progress);
bool operator==(const CSSColor &other) const;
#ifndef NDEBUG
friend std::ostream &operator<<(std::ostream &os, const CSSColor &colorValue);
#endif // NDEBUG
private:
static bool isValidColorString(const std::string &colorString);
};
inline const CSSColor CSSColor::Transparent(ColorType::Transparent);
} // namespace reanimated::css
@@ -1,120 +0,0 @@
#include <folly/json.h>
#include <reanimated/CSS/common/values/CSSDiscreteArray.h>
namespace reanimated::css {
template <CSSValueDerived TValue>
CSSDiscreteArray<TValue>::CSSDiscreteArray() : values() {}
template <CSSValueDerived TValue>
CSSDiscreteArray<TValue>::CSSDiscreteArray(const std::vector<TValue> &values)
: values(values) {}
template <CSSValueDerived TValue>
CSSDiscreteArray<TValue>::CSSDiscreteArray(
jsi::Runtime &rt,
const jsi::Value &jsiValue) {
if (!canConstruct(rt, jsiValue)) {
throw std::invalid_argument(
"[Reanimated] CSSDiscreteArray: Invalid value type: " +
stringifyJSIValue(rt, jsiValue));
}
const auto &array = jsiValue.asObject(rt).asArray(rt);
values.reserve(array.size(rt));
for (size_t i = 0; i < array.size(rt); i++) {
values.emplace_back(rt, array.getValueAtIndex(rt, i));
}
}
template <CSSValueDerived TValue>
CSSDiscreteArray<TValue>::CSSDiscreteArray(const folly::dynamic &value) {
if (!canConstruct(value)) {
throw std::invalid_argument(
"[Reanimated] CSSDiscreteArray: Invalid value type: " +
folly::toJson(value));
}
const auto &array = value;
values.reserve(array.size());
for (size_t i = 0; i < array.size(); i++) {
values.emplace_back(array[i]);
}
}
template <CSSValueDerived TValue>
bool CSSDiscreteArray<TValue>::canConstruct(
jsi::Runtime &rt,
const jsi::Value &jsiValue) {
// TODO - maybe add better validation
return jsiValue.isObject() && jsiValue.asObject(rt).isArray(rt);
}
template <CSSValueDerived TValue>
bool CSSDiscreteArray<TValue>::canConstruct(const folly::dynamic &value) {
return value.isArray();
}
template <CSSValueDerived TValue>
folly::dynamic CSSDiscreteArray<TValue>::toDynamic() const {
folly::dynamic array = folly::dynamic::array;
for (const auto &value : values) {
array.push_back(value.toDynamic());
}
return array;
}
template <CSSValueDerived TValue>
std::string CSSDiscreteArray<TValue>::toString() const {
std::stringstream ss;
ss << "{";
for (size_t i = 0; i < values.size(); i++) {
ss << values[i].toString();
if (i < values.size() - 1) {
ss << ", ";
}
}
ss << "}";
return ss.str();
}
template <CSSValueDerived TValue>
CSSDiscreteArray<TValue> CSSDiscreteArray<TValue>::interpolate(
double progress,
const CSSDiscreteArray<TValue> &other) const {
return CSSDiscreteArray<TValue>(progress < 0.5 ? values : other.values);
}
template <CSSValueDerived TValue>
bool CSSDiscreteArray<TValue>::operator==(
const CSSDiscreteArray<TValue> &other) const {
if (values.size() != other.values.size()) {
return false;
}
for (size_t i = 0; i < values.size(); i++) {
if (values[i] != other.values[i]) {
return false;
}
}
return true;
}
#ifndef NDEBUG
template <CSSValueDerived TValue>
std::ostream &operator<<(
std::ostream &os,
const CSSDiscreteArray<TValue> &arrayValue) {
os << "CSSDiscreteArray(" << arrayValue.toString() << ")";
return os;
}
#endif // NDEBUG
template struct CSSDiscreteArray<CSSKeyword>;
} // namespace reanimated::css
@@ -1,49 +0,0 @@
#pragma once
#include <reanimated/CSS/common/values/CSSKeyword.h>
#include <worklets/Tools/JSISerializer.h>
#include <string>
#include <vector>
namespace reanimated::css {
using namespace worklets;
/*
* CSSDiscreteArray is used for array interpolation when arrays need to be
* treated as discrete values. Instead of interpolating between corresponding
* elements of two arrays, this type interpolates between entire arrays
* treated as single discrete values.
*/
template <CSSValueDerived TValue>
struct CSSDiscreteArray : public CSSSimpleValue<CSSDiscreteArray<TValue>> {
static constexpr bool is_discrete_value = true;
std::vector<TValue> values;
CSSDiscreteArray();
explicit CSSDiscreteArray(const std::vector<TValue> &values);
explicit CSSDiscreteArray(jsi::Runtime &rt, const jsi::Value &jsiValue);
explicit CSSDiscreteArray(const folly::dynamic &value);
static bool canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue);
static bool canConstruct(const folly::dynamic &value);
folly::dynamic toDynamic() const override;
std::string toString() const override;
CSSDiscreteArray<TValue> interpolate(
double progress,
const CSSDiscreteArray<TValue> &other) const override;
bool operator==(const CSSDiscreteArray<TValue> &other) const;
#ifndef NDEBUG
friend std::ostream &operator<<(
std::ostream &os,
const CSSDiscreteArray<TValue> &arrayValue);
#endif // NDEBUG
};
} // namespace reanimated::css
@@ -1,101 +0,0 @@
#include <folly/json.h>
#include <reanimated/CSS/common/values/CSSKeyword.h>
#include <utility>
namespace reanimated::css {
template <typename TValue>
CSSKeywordBase<TValue>::CSSKeywordBase(const char *value) : value(value) {}
template <typename TValue>
CSSKeywordBase<TValue>::CSSKeywordBase(
jsi::Runtime &rt,
const jsi::Value &jsiValue) {
if (jsiValue.isString()) {
value = jsiValue.getString(rt).utf8(rt);
} else {
throw std::invalid_argument(
"[Reanimated] CSSKeywordBase: Invalid value type: " +
stringifyJSIValue(rt, jsiValue));
}
}
template <typename TValue>
CSSKeywordBase<TValue>::CSSKeywordBase(const folly::dynamic &value) {
if (value.isString()) {
this->value = value.getString();
} else {
throw std::invalid_argument(
"[Reanimated] CSSKeywordBase: Invalid value type: " +
folly::toJson(value));
}
}
template <typename TValue>
bool CSSKeywordBase<TValue>::canConstruct(
jsi::Runtime &rt,
const jsi::Value &jsiValue) {
return jsiValue.isString();
}
template <typename TValue>
bool CSSKeywordBase<TValue>::canConstruct(const folly::dynamic &value) {
return value.isString();
}
template <typename TValue>
folly::dynamic CSSKeywordBase<TValue>::toDynamic() const {
return value;
}
template <typename TValue>
std::string CSSKeywordBase<TValue>::toString() const {
return value;
}
template <typename TValue>
bool CSSKeywordBase<TValue>::operator==(
const CSSKeywordBase<TValue> &other) const {
return value == other.value;
}
CSSKeyword CSSKeyword::interpolate(double progress, const CSSKeyword &to)
const {
return CSSKeyword(progress < 0.5 ? value : to.value);
}
#ifndef NDEBUG
std::ostream &operator<<(std::ostream &os, const CSSKeyword &keywordValue) {
os << "CSSKeyword(" << keywordValue.toString() << ")";
return os;
}
#endif // NDEBUG
CSSDisplay CSSDisplay::interpolate(double progress, const CSSDisplay &to)
const {
if (value == "none" && progress > 0) {
return CSSDisplay(to.value);
}
if (to.value == "none" && progress < 1) {
return CSSDisplay(value);
}
return CSSDisplay(progress < 0.5 ? value : to.value);
}
#ifndef NDEBUG
std::ostream &operator<<(std::ostream &os, const CSSDisplay &displayValue) {
os << "CSSDisplay(" << displayValue.toString() << ")";
return os;
}
#endif // NDEBUG
// Explicit template instantiations
template class CSSKeywordBase<CSSKeyword>;
template class CSSKeywordBase<CSSDisplay>;
} // namespace reanimated::css
@@ -1,61 +0,0 @@
#pragma once
#include <reanimated/CSS/common/values/CSSValue.h>
#include <worklets/Tools/JSISerializer.h>
#include <string>
namespace reanimated::css {
using namespace worklets;
template <typename TValue>
class CSSKeywordBase : public CSSSimpleValue<TValue> {
public:
static constexpr bool is_discrete_value = true;
CSSKeywordBase() = default;
explicit CSSKeywordBase(const char *value);
explicit CSSKeywordBase(jsi::Runtime &rt, const jsi::Value &jsiValue);
explicit CSSKeywordBase(const folly::dynamic &value);
static bool canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue);
static bool canConstruct(const folly::dynamic &value);
folly::dynamic toDynamic() const override;
std::string toString() const override;
bool operator==(const CSSKeywordBase &other) const;
protected:
std::string value;
};
struct CSSKeyword : public CSSKeywordBase<CSSKeyword> {
using CSSKeywordBase<CSSKeyword>::CSSKeywordBase;
using CSSKeywordBase<CSSKeyword>::canConstruct;
CSSKeyword interpolate(double progress, const CSSKeyword &to) const override;
#ifndef NDEBUG
friend std::ostream &operator<<(
std::ostream &os,
const CSSKeyword &keywordValue);
#endif // NDEBUG
};
struct CSSDisplay : public CSSKeywordBase<CSSDisplay> {
using CSSKeywordBase<CSSDisplay>::CSSKeywordBase;
using CSSKeywordBase<CSSDisplay>::canConstruct;
CSSDisplay interpolate(double progress, const CSSDisplay &to) const override;
#ifndef NDEBUG
friend std::ostream &operator<<(
std::ostream &os,
const CSSDisplay &displayValue);
#endif // NDEBUG
};
} // namespace reanimated::css
@@ -1,140 +0,0 @@
#include <reanimated/CSS/common/values/CSSLength.h>
namespace reanimated::css {
CSSLength::CSSLength() : value(0), isRelative(false) {}
CSSLength::CSSLength(const double value) : value(value), isRelative(false) {}
CSSLength::CSSLength(const double value, const bool isRelative)
: value(value), isRelative(isRelative) {}
CSSLength::CSSLength(const char *value) {
if (!canConstruct(value)) {
throw std::invalid_argument(
"[Reanimated] CSSLength: Invalid value: " + std::string(value));
}
std::string str = value;
str.pop_back();
this->value = std::stod(str) / 100;
this->isRelative = true;
}
CSSLength::CSSLength(jsi::Runtime &rt, const jsi::Value &jsiValue) {
if (jsiValue.isNumber()) {
this->value = jsiValue.asNumber();
this->isRelative = false;
} else if (jsiValue.isString()) {
std::string strValue = jsiValue.asString(rt).utf8(rt);
*this = CSSLength(strValue); // Delegate to the string constructor
} else {
throw std::runtime_error("[Reanimated] CSSLength: Unsupported value type");
}
}
CSSLength::CSSLength(const folly::dynamic &value) {
if (value.isNumber()) {
this->value = value.getDouble();
this->isRelative = false;
} else if (value.isString()) {
std::string strValue = value.getString();
*this = CSSLength(strValue.c_str()); // Delegate to the string constructor
} else {
throw std::runtime_error("[Reanimated] CSSLength: Unsupported value type");
}
}
bool CSSLength::canConstruct(const std::string &value) {
return !value.empty() && value.back() == '%';
}
bool CSSLength::canConstruct(const char *value) {
return canConstruct(std::string(value));
}
bool CSSLength::canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue) {
return jsiValue.isNumber() ||
(jsiValue.isString() && canConstruct(jsiValue.getString(rt).utf8(rt)));
}
bool CSSLength::canConstruct(const folly::dynamic &value) {
return value.isNumber() ||
(value.isString() && canConstruct(value.getString()));
}
folly::dynamic CSSLength::toDynamic() const {
if (isRelative) {
return std::to_string(value * 100) + "%";
}
return value;
}
std::string CSSLength::toString() const {
if (isRelative) {
return std::to_string(value * 100) + "%";
}
return std::to_string(value);
}
CSSLength CSSLength::interpolate(
const double progress,
const CSSLength &to,
const CSSResolvableValueInterpolationContext &context) const {
// If both value types are the same, we can interpolate without reading the
// relative value from the shadow node
// (also, when one of the values is 0, and the other is relative)
if ((isRelative == to.isRelative) || (isRelative && to.value == 0) ||
(to.isRelative && value == 0)) {
return CSSLength(
value + (to.value - value) * progress, isRelative || to.isRelative);
}
// Otherwise, we need to read the relative value from the shadow node and
// interpolate values as numbers
const auto resolvedFrom = resolve(context);
const auto resolvedTo = to.resolve(context);
if (!resolvedFrom.has_value() || !resolvedTo.has_value()) {
return progress < 0.5 ? *this : to;
}
return CSSLength(
resolvedFrom.value() +
(resolvedTo.value() - resolvedFrom.value()) * progress);
}
std::optional<double> CSSLength::resolve(
const CSSResolvableValueInterpolationContext &context) const {
if (!isRelative) {
return value;
}
jsi::Value relativeValue;
if (context.relativeTo == RelativeTo::Self) {
relativeValue = context.viewStylesRepository->getNodeProp(
context.node, context.relativeProperty);
} else {
relativeValue = context.viewStylesRepository->getParentNodeProp(
context.node, context.relativeProperty);
}
if (!relativeValue.isNumber()) {
return std::nullopt;
}
return value * relativeValue.getNumber();
}
bool CSSLength::operator==(const CSSLength &other) const {
return value == other.value && isRelative == other.isRelative;
}
#ifndef NDEBUG
std::ostream &operator<<(std::ostream &os, const CSSLength &value) {
os << "CSSLength(" << value.toString() << ")";
return os;
}
#endif // NDEBUG
} // namespace reanimated::css
@@ -1,41 +0,0 @@
#pragma once
#include <reanimated/CSS/common/values/CSSValue.h>
#include <string>
namespace reanimated::css {
struct CSSLength : public CSSResolvableValue<CSSLength, double> {
double value;
bool isRelative;
CSSLength();
explicit CSSLength(double value);
explicit CSSLength(double value, bool isRelative);
explicit CSSLength(const char *value);
explicit CSSLength(jsi::Runtime &rt, const jsi::Value &jsiValue);
explicit CSSLength(const folly::dynamic &value);
static bool canConstruct(const std::string &value);
static bool canConstruct(const char *value);
static bool canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue);
static bool canConstruct(const folly::dynamic &value);
folly::dynamic toDynamic() const override;
std::string toString() const override;
CSSLength interpolate(
double progress,
const CSSLength &to,
const CSSResolvableValueInterpolationContext &context) const override;
std::optional<double> resolve(
const CSSResolvableValueInterpolationContext &context) const override;
bool operator==(const CSSLength &other) const;
#ifndef NDEBUG
friend std::ostream &operator<<(std::ostream &os, const CSSLength &dimension);
#endif // NDEBUG
};
} // namespace reanimated::css
@@ -1,107 +0,0 @@
#include <folly/json.h>
#include <reanimated/CSS/common/values/CSSNumber.h>
#include <algorithm>
namespace reanimated::css {
template <typename TDerived, typename TValue>
CSSNumberBase<TDerived, TValue>::CSSNumberBase() : value(0) {}
template <typename TDerived, typename TValue>
CSSNumberBase<TDerived, TValue>::CSSNumberBase(TValue value) : value(value) {}
template <typename TDerived, typename TValue>
CSSNumberBase<TDerived, TValue>::CSSNumberBase(
jsi::Runtime &rt,
const jsi::Value &jsiValue) {
if (jsiValue.isNumber()) {
value = static_cast<TValue>(jsiValue.asNumber());
} else {
throw std::invalid_argument(
"[Reanimated] CSSNumberBase: Invalid value type: " +
stringifyJSIValue(rt, jsiValue));
}
}
template <typename TDerived, typename TValue>
CSSNumberBase<TDerived, TValue>::CSSNumberBase(const folly::dynamic &value) {
if (value.isInt() || value.isDouble()) {
this->value = static_cast<TValue>(value.getDouble());
} else {
throw std::invalid_argument(
"[Reanimated] CSSNumberBase: Invalid value type: " +
folly::toJson(value));
}
}
template <typename TDerived, typename TValue>
bool CSSNumberBase<TDerived, TValue>::canConstruct(
jsi::Runtime &rt,
const jsi::Value &jsiValue) {
return jsiValue.isNumber();
}
template <typename TDerived, typename TValue>
bool CSSNumberBase<TDerived, TValue>::canConstruct(
const folly::dynamic &value) {
return value.isInt() || value.isDouble();
}
template <typename TDerived, typename TValue>
folly::dynamic CSSNumberBase<TDerived, TValue>::toDynamic() const {
return value;
}
template <typename TDerived, typename TValue>
std::string CSSNumberBase<TDerived, TValue>::toString() const {
return std::to_string(value);
}
template <typename TDerived, typename TValue>
TDerived CSSNumberBase<TDerived, TValue>::interpolate(
double progress,
const TDerived &other) const {
return TDerived(value + progress * (other.value - value));
}
template <typename TDerived, typename TValue>
bool CSSNumberBase<TDerived, TValue>::operator==(
const CSSNumberBase<TDerived, TValue> &other) const {
return value == other.value;
}
CSSInteger CSSInteger::interpolate(double progress, const CSSInteger &other)
const {
return CSSInteger(
static_cast<int>(std::round(value + progress * (other.value - value))));
}
template struct CSSNumberBase<CSSDouble, double>;
template struct CSSNumberBase<CSSInteger, int>;
#ifdef ANDROID
CSSShadowRadiusAndroid::CSSShadowRadiusAndroid()
: CSSNumberBase<CSSShadowRadiusAndroid, double>(1.0) {}
CSSShadowRadiusAndroid::CSSShadowRadiusAndroid(const double value)
: CSSNumberBase<CSSShadowRadiusAndroid, double>(std::max(1.0, value)) {}
CSSShadowRadiusAndroid::CSSShadowRadiusAndroid(
jsi::Runtime &rt,
const jsi::Value &jsiValue)
: CSSNumberBase<CSSShadowRadiusAndroid, double>(rt, jsiValue) {
value = std::max(1.0, value);
}
CSSShadowRadiusAndroid::CSSShadowRadiusAndroid(const folly::dynamic &value)
: CSSNumberBase<CSSShadowRadiusAndroid, double>(value) {
this->value = std::max(1.0, value.getDouble());
}
template struct CSSNumberBase<CSSShadowRadiusAndroid, double>;
#endif
} // namespace reanimated::css
@@ -1,71 +0,0 @@
#pragma once
#include <reanimated/CSS/common/values/CSSValue.h>
#include <worklets/Tools/JSISerializer.h>
#include <string>
namespace reanimated::css {
using namespace worklets;
template <typename TDerived, typename TValue>
struct CSSNumberBase : public CSSSimpleValue<TDerived> {
TValue value;
CSSNumberBase();
explicit CSSNumberBase(TValue value);
explicit CSSNumberBase(jsi::Runtime &rt, const jsi::Value &jsiValue);
explicit CSSNumberBase(const folly::dynamic &value);
static bool canConstruct(jsi::Runtime &rt, const jsi::Value &jsiValue);
static bool canConstruct(const folly::dynamic &value);
folly::dynamic toDynamic() const override;
std::string toString() const override;
TDerived interpolate(double progress, const TDerived &other) const override;
bool operator==(const CSSNumberBase<TDerived, TValue> &other) const;
};
#ifndef NDEBUG
template <typename TDerived, typename TValue>
std::ostream &operator<<(
std::ostream &os,
const CSSNumberBase<TDerived, TValue> &numberValue) {
os << "CSSNumberBase(" << numberValue.toString() << ")";
return os;
}
#endif // NDEBUG
struct CSSDouble : public CSSNumberBase<CSSDouble, double> {
// Inherit all constructors from the base class
using CSSNumberBase::CSSNumberBase;
};
struct CSSInteger : public CSSNumberBase<CSSInteger, int> {
// Inherit all constructors from the base class
using CSSNumberBase::CSSNumberBase;
CSSInteger interpolate(double progress, const CSSInteger &other)
const override;
};
#ifdef ANDROID
// For some reason Android crashes when blurRadius is smaller than 1 so we use a
// custom value that will never be smaller than 1
struct CSSShadowRadiusAndroid
: public CSSNumberBase<CSSShadowRadiusAndroid, double> {
CSSShadowRadiusAndroid();
explicit CSSShadowRadiusAndroid(double value);
explicit CSSShadowRadiusAndroid(jsi::Runtime &rt, const jsi::Value &jsiValue);
explicit CSSShadowRadiusAndroid(const folly::dynamic &value);
};
#endif
} // namespace reanimated::css
@@ -1,79 +0,0 @@
#pragma once
#include <reanimated/CSS/misc/ViewStylesRepository.h>
#include <memory>
#include <string>
#include <type_traits>
namespace reanimated::css {
using namespace facebook;
struct ValueInterpolatorUpdateContext {
const std::shared_ptr<const ShadowNode> &node;
};
enum class RelativeTo {
Parent,
Self,
};
struct CSSResolvableValueInterpolationContext {
const std::shared_ptr<const ShadowNode> &node;
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository;
const std::string &relativeProperty;
const RelativeTo relativeTo;
};
struct CSSValue {
// This field should be overridden in discrete value types
static constexpr bool is_discrete_value = false;
virtual ~CSSValue() = default;
virtual folly::dynamic toDynamic() const = 0;
virtual std::string toString() const = 0;
};
// Base for leaf values that can be interpolated without resolution
template <typename TDerived>
struct CSSSimpleValue : public CSSValue {
static constexpr bool is_resolvable_value = false;
virtual TDerived interpolate(double progress, const TDerived &to) const = 0;
};
// Base for leaf values that need resolution before interpolation
template <typename TDerived, typename TResolved = TDerived>
struct CSSResolvableValue : public CSSValue {
static constexpr bool is_resolvable_value = true;
virtual TDerived interpolate(
double progress,
const TDerived &to,
const CSSResolvableValueInterpolationContext &context) const = 0;
virtual std::optional<TResolved> resolve(
const CSSResolvableValueInterpolationContext &context) const = 0;
};
// Checks if a type is a resolvable value that needs resolution before
// interpolation
template <typename TCSSValue>
concept Resolvable = requires {
{ TCSSValue::is_resolvable_value } -> std::convertible_to<bool>;
requires TCSSValue::is_resolvable_value == true;
};
// Checks if a type is a discrete value
template <typename TCSSValue>
concept Discrete = requires {
{ TCSSValue::is_discrete_value } -> std::convertible_to<bool>;
requires TCSSValue::is_discrete_value == true;
};
// Check if a type is derived from CSSValue
template <typename TCSSValue>
concept CSSValueDerived = std::is_base_of_v<CSSValue, TCSSValue>;
} // namespace reanimated::css
@@ -1,179 +0,0 @@
#include <reanimated/CSS/common/values/CSSAngle.h>
#include <reanimated/CSS/common/values/CSSBoolean.h>
#include <reanimated/CSS/common/values/CSSColor.h>
#include <reanimated/CSS/common/values/CSSDiscreteArray.h>
#include <reanimated/CSS/common/values/CSSKeyword.h>
#include <reanimated/CSS/common/values/CSSLength.h>
#include <reanimated/CSS/common/values/CSSNumber.h>
#include <reanimated/CSS/common/values/CSSValueVariant.h>
#include <reanimated/CSS/svg/values/SVGLength.h>
#include <reanimated/CSS/svg/values/SVGStrokeDashArray.h>
#include <vector>
namespace reanimated::css {
template <typename... AllowedTypes>
CSSValueVariant<AllowedTypes...>::CSSValueVariant(
std::variant<AllowedTypes...> &&storage)
: storage_(std::move(storage)) {}
template <typename... AllowedTypes>
CSSValueVariant<AllowedTypes...>::CSSValueVariant(
jsi::Runtime &rt,
const jsi::Value &jsiValue) {
auto tryOne = [&]<typename TCSSValue>() -> bool {
// We have to check in a runtime if the type can be constructed from the
// provided jsi::Value. The first match will be used to construct the
// CSS value.
if (!TCSSValue::canConstruct(rt, jsiValue)) {
return false;
}
storage_ = TCSSValue(rt, jsiValue);
return true;
};
// Try constructing with each allowed type until one succeeds
if (!(tryOne.template operator()<AllowedTypes>() || ...)) {
throw std::runtime_error(
"[Reanimated] No compatible type found for construction from: " +
stringifyJSIValue(rt, jsiValue));
}
}
template <typename... AllowedTypes>
CSSValueVariant<AllowedTypes...>::CSSValueVariant(const folly::dynamic &value) {
auto tryOne = [&]<typename TCSSValue>() -> bool {
// We have to check in a runtime if the type can be constructed from the
// provided folly::dynamic. The first match will be used to construct the
// CSS value.
if (!TCSSValue::canConstruct(value)) {
return false;
}
storage_ = TCSSValue(value);
return true;
};
// Try constructing with each allowed type until one succeeds
if (!(tryOne.template operator()<AllowedTypes>() || ...)) {
throw std::runtime_error(
"[Reanimated] No compatible type found for construction from: " +
folly::toJson(value));
}
}
template <typename... AllowedTypes>
bool CSSValueVariant<AllowedTypes...>::operator==(
const CSSValueVariant &other) const {
if (storage_.index() != other.storage_.index()) {
return false;
}
return std::visit(
[](const auto &lhs, const auto &rhs) {
REA_IF_SAME_TYPE(lhs, rhs) {
return lhs == rhs;
}
return false;
},
storage_,
other.storage_);
}
template <typename... AllowedTypes>
bool CSSValueVariant<AllowedTypes...>::operator==(const CSSValue &other) const {
if (auto *o = dynamic_cast<const CSSValueVariant *>(&other)) {
return *this == *o;
}
return false;
}
template <typename... AllowedTypes>
folly::dynamic CSSValueVariant<AllowedTypes...>::toDynamic() const {
return std::visit([](const auto &v) { return v.toDynamic(); }, storage_);
}
template <typename... AllowedTypes>
std::string CSSValueVariant<AllowedTypes...>::toString() const {
return std::visit([](const auto &v) { return v.toString(); }, storage_);
}
template <typename... AllowedTypes>
CSSValueVariant<AllowedTypes...> CSSValueVariant<AllowedTypes...>::interpolate(
const double progress,
const CSSValueVariant &to,
const ValueInterpolatorUpdateContext &context) const {
if (storage_.index() != to.storage_.index()) {
return fallbackInterpolate(progress, to);
}
return std::visit(
[&](const auto &fromValue, const auto &toValue) -> CSSValueVariant {
REA_IF_SAME_TYPE(fromValue, toValue) {
if constexpr (Resolvable<L>) {
throw std::runtime_error(
"[Reanimated] Resolvable value cannot be interpolated as non-resolvable");
} else {
return CSSValueVariant(fromValue.interpolate(progress, toValue));
}
}
return fallbackInterpolate(progress, to);
},
storage_,
to.storage_);
}
template <typename... AllowedTypes>
CSSValueVariant<AllowedTypes...> CSSValueVariant<AllowedTypes...>::interpolate(
const double progress,
const CSSValueVariant &to,
const CSSResolvableValueInterpolationContext &context) const {
if (storage_.index() != to.storage_.index()) {
return fallbackInterpolate(progress, to);
}
return std::visit(
[&](const auto &fromValue, const auto &toValue) -> CSSValueVariant {
REA_IF_SAME_TYPE(fromValue, toValue) {
if constexpr (Resolvable<L>) {
return CSSValueVariant(
fromValue.interpolate(progress, toValue, context));
} else {
throw std::runtime_error(
"[Reanimated] Non-resolvable value cannot be interpolated as resolvable");
}
}
return fallbackInterpolate(progress, to);
},
storage_,
to.storage_);
}
template <typename... AllowedTypes>
CSSValueVariant<AllowedTypes...>
CSSValueVariant<AllowedTypes...>::fallbackInterpolate(
const double progress,
const CSSValueVariant &to) const {
return progress < 0.5 ? *this : to;
}
template class CSSValueVariant<CSSLength>;
template class CSSValueVariant<CSSLength, CSSKeyword>;
template class CSSValueVariant<CSSDouble>;
template class CSSValueVariant<CSSDouble, CSSKeyword>;
template class CSSValueVariant<CSSInteger>;
template class CSSValueVariant<CSSKeyword>;
template class CSSValueVariant<CSSAngle>;
template class CSSValueVariant<CSSBoolean>;
template class CSSValueVariant<CSSColor>;
template class CSSValueVariant<CSSDisplay>;
template class CSSValueVariant<CSSDiscreteArray<CSSKeyword>>;
#ifdef ANDROID
template class CSSValueVariant<CSSShadowRadiusAndroid>;
#endif
template class CSSValueVariant<SVGLength>;
template class CSSValueVariant<SVGLength, CSSKeyword>;
template class CSSValueVariant<SVGStrokeDashArray, CSSKeyword>;
} // namespace reanimated::css
@@ -1,123 +0,0 @@
#pragma once
#include <reanimated/CSS/common/values/CSSValue.h>
#include <worklets/Tools/JSISerializer.h>
#include <folly/json.h>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
namespace reanimated::css {
using namespace worklets;
/**
* Macro to check if two lambda parameters have the same reference-removed type.
*
* Usage:
* REA_IF_SAME_TYPE(lhs, rhs) {
* // same-type block
* } else {
* // mismatch block
* }
*/
#define REA_IF_SAME_TYPE(lhs, rhs) \
using L = std::remove_reference_t<decltype(lhs)>; \
using R = std::remove_reference_t<decltype(rhs)>; \
if constexpr (std::is_same_v<L, R>) // NOLINT(readability/braces)
// Checks whether a type has canConstruct(...) for a generic value
template <typename TCSSValue, typename TValue>
concept ValueConstructibleCSSValue = requires(TValue &&value) {
{
TCSSValue::canConstruct(std::forward<TValue>(value))
} -> std::same_as<bool>;
}; // NOLINT(readability/braces)
// Checks whether a type can be constructed from a jsi::Value
template <typename TCSSValue>
concept JSIConstructibleCSSValue =
requires(jsi::Runtime &rt, const jsi::Value &value) {
{ TCSSValue::canConstruct(rt, value) } -> std::same_as<bool>;
{ TCSSValue(rt, value) } -> std::same_as<TCSSValue>;
}; // NOLINT(readability/braces)
// Checks whether a type can be constructed from a folly::dynamic
template <typename TCSSValue>
concept DynamicConstructibleCSSValue = requires(const folly::dynamic &value) {
{ TCSSValue::canConstruct(value) } -> std::same_as<bool>;
{ TCSSValue(value) } -> std::same_as<TCSSValue>;
}; // NOLINT(readability/braces)
/**
* CSSValueVariant
*
* A std::variant-based container for multiple CSSValue-derived types.
*/
template <typename... AllowedTypes>
class CSSValueVariant final : public CSSValue {
static_assert(
(CSSValueDerived<AllowedTypes> && ...),
"CSSValueVariant accepts only CSSValue-derived types");
static_assert(
(JSIConstructibleCSSValue<AllowedTypes> && ...),
"CSSValueVariant accepts only types that can be constructed from a jsi::Value");
static_assert(
(DynamicConstructibleCSSValue<AllowedTypes> && ...),
"CSSValueVariant accepts only types that can be constructed from a folly::dynamic");
public:
CSSValueVariant() = default;
/**
* Construct from std::variant storage directly
*/
explicit CSSValueVariant(std::variant<AllowedTypes...> &&storage);
/**
* Construct from jsi::Value if it matches any AllowedType's constructor
* (chooses the first one that matches)
*/
explicit CSSValueVariant(jsi::Runtime &rt, const jsi::Value &jsiValue);
/**
* Construct from folly::dynamic if it matches any AllowedType's constructor
* (chooses the first one that matches)
*/
explicit CSSValueVariant(const folly::dynamic &value);
bool operator==(const CSSValueVariant &other) const;
bool operator==(const CSSValue &other) const;
folly::dynamic toDynamic() const override;
std::string toString() const override;
/**
* Interpolate (non-resolvable)
*/
CSSValueVariant interpolate(
const double progress,
const CSSValueVariant &to,
const ValueInterpolatorUpdateContext &context) const;
/**
* Interpolate (resolvable)
*/
CSSValueVariant interpolate(
const double progress,
const CSSValueVariant &to,
const CSSResolvableValueInterpolationContext &context) const;
private:
std::variant<AllowedTypes...> storage_;
CSSValueVariant fallbackInterpolate(
const double progress,
const CSSValueVariant &to) const;
};
} // namespace reanimated::css
@@ -1,196 +0,0 @@
#include <reanimated/CSS/configs/CSSAnimationConfig.h>
#include <utility>
namespace reanimated::css {
double getIterationCount(jsi::Runtime &rt, const jsi::Object &settings) {
return settings.getProperty(rt, "iterationCount").asNumber();
}
AnimationDirection getDirection(jsi::Runtime &rt, const jsi::Object &settings) {
static const std::unordered_map<std::string, AnimationDirection>
strToEnumMap = {
{"normal", AnimationDirection::Normal},
{"reverse", AnimationDirection::Reverse},
{"alternate", AnimationDirection::Alternate},
{"alternate-reverse", AnimationDirection::AlternateReverse}};
const auto str = settings.getProperty(rt, "direction").asString(rt).utf8(rt);
auto it = strToEnumMap.find(str);
if (it == strToEnumMap.cend()) {
throw std::invalid_argument(
"[Reanimated] Invalid animationDirection: " + str);
}
return it->second;
}
AnimationFillMode getFillMode(jsi::Runtime &rt, const jsi::Object &settings) {
static const std::unordered_map<std::string, AnimationFillMode> strToEnumMap =
{{"none", AnimationFillMode::None},
{"forwards", AnimationFillMode::Forwards},
{"backwards", AnimationFillMode::Backwards},
{"both", AnimationFillMode::Both}};
const auto str = settings.getProperty(rt, "fillMode").asString(rt).utf8(rt);
auto it = strToEnumMap.find(str);
if (it == strToEnumMap.cend()) {
throw std::invalid_argument(
"[Reanimated] Invalid animationFillMode: " + str);
}
return it->second;
}
AnimationPlayState getPlayState(jsi::Runtime &rt, const jsi::Object &settings) {
static const std::unordered_map<std::string, AnimationPlayState>
strToEnumMap = {
{"running", AnimationPlayState::Running},
{"paused", AnimationPlayState::Paused}};
const auto str = settings.getProperty(rt, "playState").asString(rt).utf8(rt);
auto it = strToEnumMap.find(str);
if (it == strToEnumMap.cend()) {
throw std::invalid_argument(
"[Reanimated] Invalid animationPlayState: " + str);
}
return it->second;
}
CSSAnimationSettings parseCSSAnimationSettings(
jsi::Runtime &rt,
const jsi::Value &settings) {
const auto &settingsObj = settings.asObject(rt);
return {
getDuration(rt, settingsObj),
getTimingFunction(rt, settingsObj),
getDelay(rt, settingsObj),
getIterationCount(rt, settingsObj),
getDirection(rt, settingsObj),
getFillMode(rt, settingsObj),
getPlayState(rt, settingsObj)};
}
PartialCSSAnimationSettings parsePartialCSSAnimationSettings(
jsi::Runtime &rt,
const jsi::Value &partialSettings) {
const auto &partialObj = partialSettings.asObject(rt);
PartialCSSAnimationSettings result;
if (partialObj.hasProperty(rt, "duration")) {
result.duration = getDuration(rt, partialObj);
}
if (partialObj.hasProperty(rt, "timingFunction")) {
result.easingFunction = getTimingFunction(rt, partialObj);
}
if (partialObj.hasProperty(rt, "delay")) {
result.delay = getDelay(rt, partialObj);
}
if (partialObj.hasProperty(rt, "iterationCount")) {
result.iterationCount = getIterationCount(rt, partialObj);
}
if (partialObj.hasProperty(rt, "direction")) {
result.direction = getDirection(rt, partialObj);
}
if (partialObj.hasProperty(rt, "fillMode")) {
result.fillMode = getFillMode(rt, partialObj);
}
if (partialObj.hasProperty(rt, "playState")) {
result.playState = getPlayState(rt, partialObj);
}
return result;
}
std::vector<std::string> parseAnimationNames(
jsi::Runtime &rt,
const jsi::Value &animationNames) {
std::vector<std::string> result;
const auto &namesArray = animationNames.asObject(rt).asArray(rt);
const auto animationNamesCount = namesArray.size(rt);
result.reserve(animationNamesCount);
for (size_t i = 0; i < animationNamesCount; i++) {
result.emplace_back(
namesArray.getValueAtIndex(rt, i).asString(rt).utf8(rt));
}
return result;
}
template <typename TResult>
std::unordered_map<size_t, TResult> parseHelper(
jsi::Runtime &rt,
const jsi::Object &settingsObj,
std::function<TResult(jsi::Runtime &, const jsi::Value &)> parseFunction) {
std::unordered_map<size_t, TResult> result;
const auto animationIndices = settingsObj.getPropertyNames(rt);
const auto animationIndicesCount = animationIndices.size(rt);
result.reserve(animationIndicesCount);
for (size_t i = 0; i < animationIndicesCount; i++) {
const auto &indexStr = animationIndices.getValueAtIndex(rt, i).toString(rt);
const auto &animationSettings = settingsObj.getProperty(rt, indexStr);
const auto index = std::stoul(indexStr.utf8(rt));
result[index] = parseFunction(rt, animationSettings);
}
return result;
}
CSSAnimationSettingsMap parseNewAnimationSettings(
jsi::Runtime &rt,
const std::vector<std::string> &animationNames,
const jsi::Value &newSettings) {
return parseHelper<CSSAnimationSettings>(
rt,
newSettings.asObject(rt),
[&](jsi::Runtime &rt, const jsi::Value &settings) {
return parseCSSAnimationSettings(rt, settings);
});
}
CSSAnimationSettingsUpdatesMap parseSettingsUpdates(
jsi::Runtime &rt,
const jsi::Value &settingsUpdates) {
return parseHelper<PartialCSSAnimationSettings>(
rt,
settingsUpdates.asObject(rt),
[](jsi::Runtime &rt, const jsi::Value &settings) {
return parsePartialCSSAnimationSettings(rt, settings);
});
}
CSSAnimationUpdates parseCSSAnimationUpdates(
jsi::Runtime &rt,
const jsi::Value &config) {
const auto &configObj = config.asObject(rt);
CSSAnimationUpdates result;
if (configObj.hasProperty(rt, "animationNames")) {
const auto animationNames =
parseAnimationNames(rt, configObj.getProperty(rt, "animationNames"));
result.animationNames = std::move(animationNames);
if (configObj.hasProperty(rt, "newAnimationSettings")) {
result.newAnimationSettings = parseNewAnimationSettings(
rt,
animationNames,
configObj.getProperty(rt, "newAnimationSettings"));
}
}
if (configObj.hasProperty(rt, "settingsUpdates")) {
result.settingsUpdates =
parseSettingsUpdates(rt, configObj.getProperty(rt, "settingsUpdates"));
}
return result;
}
} // namespace reanimated::css
@@ -1,53 +0,0 @@
#pragma once
#include <reanimated/CSS/configs/CSSKeyframesConfig.h>
#include <reanimated/CSS/configs/common.h>
#include <reanimated/CSS/easing/EasingFunctions.h>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
namespace reanimated::css {
enum class AnimationDirection { Normal, Reverse, Alternate, AlternateReverse };
enum class AnimationFillMode { None, Forwards, Backwards, Both };
enum class AnimationPlayState { Running, Paused };
struct CSSAnimationSettings {
double duration;
EasingFunction easingFunction;
double delay;
double iterationCount;
AnimationDirection direction;
AnimationFillMode fillMode;
AnimationPlayState playState;
};
struct PartialCSSAnimationSettings {
std::optional<double> duration;
std::optional<EasingFunction> easingFunction;
std::optional<double> delay;
std::optional<double> iterationCount;
std::optional<AnimationDirection> direction;
std::optional<AnimationFillMode> fillMode;
std::optional<AnimationPlayState> playState;
};
using CSSAnimationSettingsMap =
std::unordered_map<size_t, CSSAnimationSettings>;
using CSSAnimationSettingsUpdatesMap =
std::unordered_map<size_t, PartialCSSAnimationSettings>;
struct CSSAnimationUpdates {
std::optional<std::vector<std::string>> animationNames;
CSSAnimationSettingsMap newAnimationSettings;
CSSAnimationSettingsUpdatesMap settingsUpdates;
};
CSSAnimationUpdates parseCSSAnimationUpdates(
jsi::Runtime &rt,
const jsi::Value &config);
} // namespace reanimated::css
@@ -1,52 +0,0 @@
#include <reanimated/CSS/configs/CSSKeyframesConfig.h>
namespace reanimated::css {
std::shared_ptr<AnimationStyleInterpolator> createStyleInterpolator(
jsi::Runtime &rt,
const jsi::Object &config,
const std::string &componentName,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository) {
return std::make_shared<AnimationStyleInterpolator>(
rt,
config.getProperty(rt, "keyframesStyle"),
componentName,
viewStylesRepository);
}
std::shared_ptr<KeyframeEasingFunctions> getKeyframeTimingFunctions(
jsi::Runtime &rt,
const jsi::Object &config) {
KeyframeEasingFunctions result;
const auto &keyframeTimingFunctions =
config.getProperty(rt, "keyframeTimingFunctions").asObject(rt);
const auto timingFunctionOffsets =
keyframeTimingFunctions.getPropertyNames(rt);
const auto timingFunctionsCount = timingFunctionOffsets.size(rt);
for (size_t i = 0; i < timingFunctionsCount; ++i) {
const auto offset =
timingFunctionOffsets.getValueAtIndex(rt, i).asString(rt).utf8(rt);
const auto easingFunction = createEasingFunction(
rt, keyframeTimingFunctions.getProperty(rt, offset.c_str()));
result[std::stod(offset)] = easingFunction;
}
return std::make_shared<KeyframeEasingFunctions>(result);
}
CSSKeyframesConfig parseCSSAnimationKeyframesConfig(
jsi::Runtime &rt,
const jsi::Value &config,
const std::string &componentName,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository) {
const auto &configObj = config.asObject(rt);
return {
createStyleInterpolator(
rt, configObj, componentName, viewStylesRepository),
getKeyframeTimingFunctions(rt, configObj)};
}
} // namespace reanimated::css
@@ -1,26 +0,0 @@
#pragma once
#include <reanimated/CSS/easing/EasingFunctions.h>
#include <reanimated/CSS/interpolation/styles/AnimationStyleInterpolator.h>
#include <reanimated/CSS/misc/ViewStylesRepository.h>
#include <memory>
#include <string>
#include <unordered_map>
namespace reanimated::css {
using KeyframeEasingFunctions = std::unordered_map<double, EasingFunction>;
struct CSSKeyframesConfig {
std::shared_ptr<AnimationStyleInterpolator> styleInterpolator;
std::shared_ptr<KeyframeEasingFunctions> keyframeEasingFunctions;
};
CSSKeyframesConfig parseCSSAnimationKeyframesConfig(
jsi::Runtime &rt,
const jsi::Value &config,
const std::string &componentName,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository);
} // namespace reanimated::css
@@ -1,102 +0,0 @@
#include <reanimated/CSS/configs/CSSTransitionConfig.h>
namespace reanimated::css {
std::optional<CSSTransitionPropertySettings> getTransitionPropertySettings(
const CSSTransitionPropertiesSettings &propertiesSettings,
const std::string &propName) {
// Try to use property specific settings first
const auto &propIt = propertiesSettings.find(propName);
if (propIt != propertiesSettings.end()) {
return propIt->second;
}
// Fallback to "all" settings if no property specific settings are available
const auto &allIt = propertiesSettings.find("all");
if (allIt != propertiesSettings.end()) {
return allIt->second;
}
// Or return nullopt if no settings are available
return std::nullopt;
}
TransitionProperties getProperties(
jsi::Runtime &rt,
const jsi::Object &config) {
const auto transitionProperty = config.getProperty(rt, "properties");
if (transitionProperty.isObject()) {
PropertyNames properties;
const auto propertiesArray = transitionProperty.asObject(rt).asArray(rt);
const auto propertiesCount = propertiesArray.size(rt);
for (size_t i = 0; i < propertiesCount; ++i) {
properties.emplace_back(
propertiesArray.getValueAtIndex(rt, i).asString(rt).utf8(rt));
}
return properties;
}
return std::nullopt;
}
bool getAllowDiscrete(jsi::Runtime &rt, const jsi::Object &config) {
return config.getProperty(rt, "allowDiscrete").asBool();
}
CSSTransitionPropertiesSettings parseCSSTransitionPropertiesSettings(
jsi::Runtime &rt,
const jsi::Object &settings) {
CSSTransitionPropertiesSettings result;
const auto propertyNames = settings.getPropertyNames(rt);
const auto propertiesCount = propertyNames.size(rt);
for (size_t i = 0; i < propertiesCount; ++i) {
const auto propertyName =
propertyNames.getValueAtIndex(rt, i).asString(rt).utf8(rt);
const auto propertySettings =
settings.getProperty(rt, jsi::PropNameID::forUtf8(rt, propertyName))
.asObject(rt);
result.emplace(
propertyName,
CSSTransitionPropertySettings{
getDuration(rt, propertySettings),
getTimingFunction(rt, propertySettings),
getDelay(rt, propertySettings),
getAllowDiscrete(rt, propertySettings)});
}
return result;
}
CSSTransitionConfig parseCSSTransitionConfig(
jsi::Runtime &rt,
const jsi::Value &config) {
const auto configObj = config.asObject(rt);
return CSSTransitionConfig{
getProperties(rt, configObj),
parseCSSTransitionPropertiesSettings(
rt, configObj.getProperty(rt, "settings").asObject(rt))};
}
PartialCSSTransitionConfig parsePartialCSSTransitionConfig(
jsi::Runtime &rt,
const jsi::Value &partialConfig) {
const auto partialObj = partialConfig.asObject(rt);
PartialCSSTransitionConfig result;
if (partialObj.hasProperty(rt, "properties")) {
result.properties = getProperties(rt, partialObj);
}
if (partialObj.hasProperty(rt, "settings")) {
result.settings = parseCSSTransitionPropertiesSettings(
rt, partialObj.getProperty(rt, "settings").asObject(rt));
}
return result;
}
} // namespace reanimated::css
@@ -1,50 +0,0 @@
#pragma once
#include <reanimated/CSS/common/definitions.h>
#include <reanimated/CSS/configs/common.h>
#include <reanimated/CSS/easing/EasingFunctions.h>
#include <string>
#include <unordered_map>
namespace reanimated::css {
struct CSSTransitionPropertySettings {
double duration;
EasingFunction easingFunction;
double delay;
bool allowDiscrete;
};
using CSSTransitionPropertiesSettings =
std::unordered_map<std::string, CSSTransitionPropertySettings>;
struct CSSTransitionConfig {
TransitionProperties properties;
CSSTransitionPropertiesSettings settings;
};
struct PartialCSSTransitionConfig {
std::optional<TransitionProperties> properties;
std::optional<CSSTransitionPropertiesSettings> settings;
};
std::optional<CSSTransitionPropertySettings> getTransitionPropertySettings(
const CSSTransitionPropertiesSettings &propertiesSettings,
const std::string &propName);
TransitionProperties getProperties(jsi::Runtime &rt, const jsi::Object &config);
CSSTransitionPropertiesSettings parseCSSTransitionPropertiesSettings(
jsi::Runtime &rt,
const jsi::Object &settings);
CSSTransitionConfig parseCSSTransitionConfig(
jsi::Runtime &rt,
const jsi::Value &config);
PartialCSSTransitionConfig parsePartialCSSTransitionConfig(
jsi::Runtime &rt,
const jsi::Value &partialConfig);
} // namespace reanimated::css
@@ -1,17 +0,0 @@
#include <reanimated/CSS/configs/common.h>
namespace reanimated::css {
double getDuration(jsi::Runtime &rt, const jsi::Object &config) {
return config.getProperty(rt, "duration").asNumber();
}
EasingFunction getTimingFunction(jsi::Runtime &rt, const jsi::Object &config) {
return createEasingFunction(rt, config.getProperty(rt, "timingFunction"));
}
double getDelay(jsi::Runtime &rt, const jsi::Object &config) {
return config.getProperty(rt, "delay").asNumber();
}
} // namespace reanimated::css
@@ -1,13 +0,0 @@
#pragma once
#include <reanimated/CSS/easing/EasingFunctions.h>
namespace reanimated::css {
double getDuration(jsi::Runtime &rt, const jsi::Object &config);
EasingFunction getTimingFunction(jsi::Runtime &rt, const jsi::Object &config);
double getDelay(jsi::Runtime &rt, const jsi::Object &config);
} // namespace reanimated::css
@@ -1,135 +0,0 @@
#include <reanimated/CSS/core/CSSAnimation.h>
#include <utility>
namespace reanimated::css {
CSSAnimation::CSSAnimation(
jsi::Runtime &rt,
std::shared_ptr<const ShadowNode> shadowNode,
std::string animationName,
const CSSKeyframesConfig &cssKeyframesConfig,
const CSSAnimationSettings &settings,
const double timestamp)
: name_(std::move(animationName)),
shadowNode_(std::move(shadowNode)),
fillMode_(settings.fillMode),
styleInterpolator_(cssKeyframesConfig.styleInterpolator),
progressProvider_(std::make_shared<AnimationProgressProvider>(
timestamp,
settings.duration,
settings.delay,
settings.iterationCount,
settings.direction,
settings.easingFunction,
cssKeyframesConfig.keyframeEasingFunctions)) {
if (settings.playState == AnimationPlayState::Paused) {
progressProvider_->pause(timestamp);
}
}
const std::string &CSSAnimation::getName() const {
return name_;
}
std::shared_ptr<const ShadowNode> CSSAnimation::getShadowNode() const {
return shadowNode_;
}
double CSSAnimation::getStartTimestamp(const double timestamp) const {
return progressProvider_->getStartTimestamp(timestamp);
}
AnimationProgressState CSSAnimation::getState(double timestamp) const {
return progressProvider_->getState(timestamp);
}
bool CSSAnimation::isReversed() const {
const auto direction = progressProvider_->getDirection();
return direction == AnimationDirection::Reverse ||
direction == AnimationDirection::AlternateReverse;
}
bool CSSAnimation::hasForwardsFillMode() const {
return fillMode_ == AnimationFillMode::Forwards ||
fillMode_ == AnimationFillMode::Both;
}
bool CSSAnimation::hasBackwardsFillMode() const {
return fillMode_ == AnimationFillMode::Backwards ||
fillMode_ == AnimationFillMode::Both;
}
folly::dynamic CSSAnimation::getCurrentInterpolationStyle() const {
return styleInterpolator_->interpolate(shadowNode_, progressProvider_);
}
folly::dynamic CSSAnimation::getBackwardsFillStyle() const {
return isReversed() ? styleInterpolator_->getLastKeyframeValue()
: styleInterpolator_->getFirstKeyframeValue();
}
folly::dynamic CSSAnimation::getResetStyle() const {
return styleInterpolator_->getResetStyle(shadowNode_);
}
void CSSAnimation::run(const double timestamp) {
if (progressProvider_->getState(timestamp) ==
AnimationProgressState::Finished) {
return;
}
progressProvider_->play(timestamp);
}
folly::dynamic CSSAnimation::update(const double timestamp) {
progressProvider_->update(timestamp);
// Check if the animation has not started yet because of the delay
// (In general, it shouldn't be activated until the delay has passed but we
// add this check to make sure that animation doesn't start with the negative
// progress)
if (progressProvider_->getState(timestamp) ==
AnimationProgressState::Pending) {
return hasBackwardsFillMode() ? getBackwardsFillStyle() : folly::dynamic();
}
return styleInterpolator_->interpolate(shadowNode_, progressProvider_);
}
void CSSAnimation::updateSettings(
const PartialCSSAnimationSettings &updatedSettings,
const double timestamp) {
progressProvider_->resetProgress();
if (updatedSettings.duration.has_value()) {
progressProvider_->setDuration(updatedSettings.duration.value());
}
if (updatedSettings.easingFunction.has_value()) {
progressProvider_->setEasingFunction(
updatedSettings.easingFunction.value());
}
if (updatedSettings.delay.has_value()) {
progressProvider_->setDelay(updatedSettings.delay.value());
}
if (updatedSettings.iterationCount.has_value()) {
progressProvider_->setIterationCount(
updatedSettings.iterationCount.value());
}
if (updatedSettings.direction.has_value()) {
progressProvider_->setDirection(updatedSettings.direction.value());
}
if (updatedSettings.fillMode.has_value()) {
fillMode_ = updatedSettings.fillMode.value();
}
if (updatedSettings.playState.has_value()) {
if (updatedSettings.playState.value() == AnimationPlayState::Paused) {
progressProvider_->pause(timestamp);
} else {
progressProvider_->play(timestamp);
}
}
progressProvider_->update(timestamp);
}
} // namespace reanimated::css
@@ -1,51 +0,0 @@
#pragma once
#include <reanimated/CSS/configs/CSSAnimationConfig.h>
#include <reanimated/CSS/configs/CSSKeyframesConfig.h>
#include <reanimated/CSS/progress/AnimationProgressProvider.h>
#include <memory>
#include <string>
namespace reanimated::css {
class CSSAnimation {
public:
CSSAnimation(
jsi::Runtime &rt,
std::shared_ptr<const ShadowNode> shadowNode,
std::string animationName,
const CSSKeyframesConfig &cssKeyframesConfig,
const CSSAnimationSettings &settings,
double timestamp);
const std::string &getName() const;
std::shared_ptr<const ShadowNode> getShadowNode() const;
double getStartTimestamp(double timestamp) const;
AnimationProgressState getState(double timestamp) const;
bool isReversed() const;
bool hasForwardsFillMode() const;
bool hasBackwardsFillMode() const;
folly::dynamic getCurrentInterpolationStyle() const;
folly::dynamic getBackwardsFillStyle() const;
folly::dynamic getResetStyle() const;
void run(double timestamp);
folly::dynamic update(double timestamp);
void updateSettings(
const PartialCSSAnimationSettings &updatedSettings,
double timestamp);
private:
const std::string name_;
const std::shared_ptr<const ShadowNode> shadowNode_;
AnimationFillMode fillMode_;
const std::shared_ptr<AnimationStyleInterpolator> styleInterpolator_;
const std::shared_ptr<AnimationProgressProvider> progressProvider_;
};
} // namespace reanimated::css
@@ -1,145 +0,0 @@
#include <reanimated/CSS/core/CSSTransition.h>
#include <utility>
namespace reanimated::css {
CSSTransition::CSSTransition(
std::shared_ptr<const ShadowNode> shadowNode,
const CSSTransitionConfig &config,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
: shadowNode_(std::move(shadowNode)),
viewStylesRepository_(viewStylesRepository),
properties_(config.properties),
settings_(config.settings),
styleInterpolator_(TransitionStyleInterpolator(
shadowNode_->getComponentName(),
viewStylesRepository)),
progressProvider_(TransitionProgressProvider()) {}
Tag CSSTransition::getViewTag() const {
return shadowNode_->getTag();
}
std::shared_ptr<const ShadowNode> CSSTransition::getShadowNode() const {
return shadowNode_;
}
double CSSTransition::getMinDelay(double timestamp) const {
return progressProvider_.getMinDelay(timestamp);
}
TransitionProgressState CSSTransition::getState() const {
return progressProvider_.getState();
}
folly::dynamic CSSTransition::getCurrentInterpolationStyle() const {
return styleInterpolator_.interpolate(shadowNode_, progressProvider_);
}
TransitionProperties CSSTransition::getProperties() const {
return properties_;
}
PropertyNames CSSTransition::getAllowedProperties(
const folly::dynamic &oldProps,
const folly::dynamic &newProps) {
if (!oldProps.isObject() || !newProps.isObject()) {
return {};
}
// If specific properties are set, process only those
if (properties_.has_value()) {
PropertyNames allowedProps;
const auto &properties = properties_.value();
allowedProps.reserve(properties.size());
for (const auto &prop : properties) {
if (isAllowedProperty(prop)) {
allowedProps.push_back(prop);
}
}
return allowedProps;
}
// Process all properties from both old and new props
std::unordered_set<std::string> allAllowedProps;
for (const auto &props : {oldProps, newProps}) {
for (const auto &propertyName : props.keys()) {
if (isAllowedProperty(propertyName.asString())) {
allAllowedProps.insert(propertyName.asString());
}
}
}
return {allAllowedProps.begin(), allAllowedProps.end()};
}
void CSSTransition::updateSettings(const PartialCSSTransitionConfig &config) {
if (config.properties.has_value()) {
updateTransitionProperties(config.properties.value());
}
if (config.settings.has_value()) {
settings_ = config.settings.value();
}
}
folly::dynamic CSSTransition::run(
const ChangedProps &changedProps,
const folly::dynamic &lastUpdateValue,
const double timestamp) {
progressProvider_.runProgressProviders(
timestamp,
settings_,
changedProps.changedPropertyNames,
styleInterpolator_.getReversedPropertyNames(changedProps.newProps));
styleInterpolator_.updateInterpolatedProperties(
changedProps, lastUpdateValue);
return update(timestamp);
}
folly::dynamic CSSTransition::update(const double timestamp) {
progressProvider_.update(timestamp);
auto result = styleInterpolator_.interpolate(shadowNode_, progressProvider_);
// Remove interpolators for which interpolation has finished
// (we won't need them anymore in the current transition)
styleInterpolator_.discardFinishedInterpolators(progressProvider_);
// And remove finished progress providers after they were used to calculate
// the last frame of the transition
progressProvider_.discardFinishedProgressProviders();
return result;
}
void CSSTransition::updateTransitionProperties(
const TransitionProperties &properties) {
properties_ = properties;
const auto isAllPropertiesTransition = !properties_.has_value();
if (isAllPropertiesTransition) {
return;
}
const std::unordered_set<std::string> transitionPropertyNames(
properties_->begin(), properties_->end());
styleInterpolator_.discardIrrelevantInterpolators(transitionPropertyNames);
progressProvider_.discardIrrelevantProgressProviders(transitionPropertyNames);
}
bool CSSTransition::isAllowedProperty(const std::string &propertyName) const {
if (!isDiscreteProperty(propertyName, shadowNode_->getComponentName())) {
return true;
}
const auto &propertySettings =
getTransitionPropertySettings(settings_, propertyName);
if (!propertySettings.has_value()) {
return false;
}
return propertySettings.value().allowDiscrete;
}
} // namespace reanimated::css
@@ -1,50 +0,0 @@
#pragma once
#include <reanimated/CSS/configs/CSSTransitionConfig.h>
#include <reanimated/CSS/easing/EasingFunctions.h>
#include <reanimated/CSS/interpolation/styles/TransitionStyleInterpolator.h>
#include <reanimated/CSS/progress/TransitionProgressProvider.h>
#include <memory>
#include <string>
#include <unordered_set>
namespace reanimated::css {
class CSSTransition {
public:
CSSTransition(
std::shared_ptr<const ShadowNode> shadowNode,
const CSSTransitionConfig &config,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository);
Tag getViewTag() const;
std::shared_ptr<const ShadowNode> getShadowNode() const;
double getMinDelay(double timestamp) const;
TransitionProgressState getState() const;
folly::dynamic getCurrentInterpolationStyle() const;
TransitionProperties getProperties() const;
PropertyNames getAllowedProperties(
const folly::dynamic &oldProps,
const folly::dynamic &newProps);
void updateSettings(const PartialCSSTransitionConfig &config);
folly::dynamic run(
const ChangedProps &changedProps,
const folly::dynamic &lastUpdateValue,
double timestamp);
folly::dynamic update(double timestamp);
private:
const std::shared_ptr<const ShadowNode> shadowNode_;
const std::shared_ptr<ViewStylesRepository> viewStylesRepository_;
TransitionProperties properties_;
CSSTransitionPropertiesSettings settings_;
TransitionStyleInterpolator styleInterpolator_;
TransitionProgressProvider progressProvider_;
void updateTransitionProperties(const TransitionProperties &properties);
bool isAllowedProperty(const std::string &propertyName) const;
};
} // namespace reanimated::css
@@ -1,76 +0,0 @@
#include <reanimated/CSS/easing/EasingFunctions.h>
#include <vector>
namespace reanimated::css {
inline const std::unordered_map<std::string, EasingFunction>
PREDEFINED_EASING_MAP = {
{"linear", [](double x) { return x; }},
{"ease", cubicBezier(0.25, 0.1, 0.25, 0.1)},
{"ease-in", cubicBezier(0.42, 0.0, 1.0, 1.0)},
{"ease-out", cubicBezier(0.0, 0.0, 0.58, 1.0)},
{"ease-in-out", cubicBezier(0.42, 0.0, 0.58, 1.0)},
{"step-start", steps(std::vector<double>{0}, std::vector<double>{1})},
{"step-end",
steps(std::vector<double>{0, 1}, std::vector<double>{0, 1})}};
EasingFunction createEasingFunction(
jsi::Runtime &rt,
const jsi::Value &easingConfig) {
if (easingConfig.isString()) {
return getPredefinedEasingFunction(easingConfig.asString(rt).utf8(rt));
} else if (easingConfig.isObject()) {
return createParametrizedEasingFunction(rt, easingConfig.asObject(rt));
} else {
throw std::runtime_error(
std::string("[Reanimated] Invalid easing function"));
}
}
EasingFunction getPredefinedEasingFunction(const std::string &name) {
auto it = PREDEFINED_EASING_MAP.find(name);
if (it != PREDEFINED_EASING_MAP.end()) {
return it->second;
} else {
throw std::runtime_error(std::string(
"[Reanimated] Easing function with name '" + name +
"' is not defined."));
}
}
EasingFunction createParametrizedEasingFunction(
jsi::Runtime &rt,
const jsi::Object &easingConfig) {
const auto easingName =
easingConfig.getProperty(rt, "name").asString(rt).utf8(rt);
if (easingName == "cubicBezier") {
return cubicBezier(rt, easingConfig);
}
std::vector<double> pointsX;
std::vector<double> pointsY;
const auto points =
easingConfig.getProperty(rt, "points").asObject(rt).asArray(rt);
const auto pointsCount = points.size(rt);
for (size_t i = 0; i < pointsCount; i++) {
const auto pointObj = points.getValueAtIndex(rt, i).asObject(rt);
pointsX.push_back(pointObj.getProperty(rt, "x").asNumber());
pointsY.push_back(pointObj.getProperty(rt, "y").asNumber());
}
if (easingName == "linear") {
return linear(pointsX, pointsY);
} else if (easingName == "steps") {
return steps(pointsX, pointsY);
} else {
throw std::runtime_error(std::string(
"[Reanimated] Parametrized easing function with name '" + easingName +
"' is not defined."));
}
}
} // namespace reanimated::css
@@ -1,27 +0,0 @@
#pragma once
#include <reanimated/CSS/easing/cubicBezier.h>
#include <reanimated/CSS/easing/linear.h>
#include <reanimated/CSS/easing/steps.h>
#include <jsi/jsi.h>
#include <string>
#include <unordered_map>
namespace reanimated::css {
using namespace facebook;
extern const std::unordered_map<std::string, EasingFunction>
PREDEFINED_EASING_MAP;
EasingFunction getPredefinedEasingFunction(const std::string &name);
EasingFunction createParametrizedEasingFunction(
jsi::Runtime &rt,
const jsi::Object &easingConfig);
EasingFunction createEasingFunction(
jsi::Runtime &rt,
const jsi::Value &easingConfig);
} // namespace reanimated::css
@@ -1,76 +0,0 @@
#include <reanimated/CSS/easing/cubicBezier.h>
namespace reanimated::css {
double sampleCurveX(const double t, const double x1, const double x2) {
return 3 * (1 - t) * (1 - t) * t * x1 + 3 * (1 - t) * t * t * x2 + t * t * t;
}
double sampleCurveY(const double t, const double y1, const double y2) {
return 3 * (1 - t) * (1 - t) * t * y1 + 3 * (1 - t) * t * t * y2 + t * t * t;
}
double
sampleCurveDerivativeX(const double t, const double x1, const double x2) {
return -6 * (1 - t) * t * x1 + 3 * (1 - t) * (1 - t) * x1 +
6 * (1 - t) * t * x2 - 6 * t * t * x2 + 3 * t * t;
}
double solveCurveX(
const double x,
const double x1,
const double x2,
const double epsilon) {
double t0 = 0.0, t1 = 1.0, t2 = x, xValue, dX;
int iterations = 0;
while (iterations < 8) {
xValue = sampleCurveX(t2, x1, x2) - x;
if (std::abs(xValue) < epsilon) {
return t2;
}
dX = sampleCurveDerivativeX(t2, x1, x2);
if (std::abs(dX) < epsilon) {
break;
}
t2 = t2 - xValue / dX;
iterations++;
}
// Fallback: Use binary search
while (t0 < t1) {
t2 = (t0 + t1) / 2.0;
xValue = sampleCurveX(t2, x1, x2);
if (std::abs(xValue - x) < epsilon) {
return t2;
}
if (x > xValue) {
t0 = t2;
} else {
t1 = t2;
}
}
return t2;
}
EasingFunction cubicBezier(
const double x1,
const double y1,
const double x2,
const double y2) {
return [=](double x) {
double t = solveCurveX(x, x1, x2);
return sampleCurveY(t, y1, y2);
};
}
EasingFunction cubicBezier(jsi::Runtime &rt, const jsi::Object &easingConfig) {
const auto x1 = easingConfig.getProperty(rt, "x1").asNumber();
const auto y1 = easingConfig.getProperty(rt, "y1").asNumber();
const auto x2 = easingConfig.getProperty(rt, "x2").asNumber();
const auto y2 = easingConfig.getProperty(rt, "y2").asNumber();
return cubicBezier(x1, y1, x2, y2);
}
} // namespace reanimated::css
@@ -1,15 +0,0 @@
#pragma once
#include <reanimated/CSS/common/definitions.h>
namespace reanimated::css {
double sampleCurveX(double t, double x1, double x2);
double sampleCurveY(double t, double y1, double y2);
double sampleCurveDerivativeX(double t, double x1, double x2);
double solveCurveX(double x, double x1, double x2, double epsilon = 1e-6);
EasingFunction cubicBezier(double x1, double y1, double x2, double y2);
EasingFunction cubicBezier(jsi::Runtime &rt, const jsi::Object &easingConfig);
} // namespace reanimated::css
@@ -1,32 +0,0 @@
#include <reanimated/CSS/easing/linear.h>
namespace reanimated::css {
double interpolateValue(
double x,
size_t leftIdx,
const std::vector<double> &pointsX,
const std::vector<double> &pointsY) {
if (leftIdx == pointsX.size() - 1) {
// We are exactly on the last point of the curve, we just return its y
// coordinate
return pointsY[leftIdx];
}
const auto rightIdx = leftIdx + 1;
// Calculate the line equation for the line between leftIdx and rightIdx
// points
const auto a = (pointsY[rightIdx] - pointsY[leftIdx]) /
(pointsX[rightIdx] - pointsX[leftIdx]);
return pointsY[leftIdx] + a * (x - pointsX[leftIdx]);
}
EasingFunction linear(
const std::vector<double> &pointsX,
const std::vector<double> &pointsY) {
return [=](double x) {
size_t leftIdx = firstSmallerOrEqual(x, pointsX);
return interpolateValue(x, leftIdx, pointsX, pointsY);
};
}
} // namespace reanimated::css
@@ -1,20 +0,0 @@
#pragma once
#include <reanimated/CSS/common/definitions.h>
#include <reanimated/CSS/utils/algorithms.h>
#include <vector>
namespace reanimated::css {
double interpolateValue(
double x,
std::size_t leftIdx,
const std::vector<double> &arrX,
const std::vector<double> &arrY);
EasingFunction linear(
const std::vector<double> &pointsX,
const std::vector<double> &pointsY);
} // namespace reanimated::css
@@ -1,13 +0,0 @@
#include <reanimated/CSS/easing/steps.h>
namespace reanimated::css {
EasingFunction steps(
const std::vector<double> &pointsX,
const std::vector<double> &pointsY) {
return [=](double x) {
size_t stepIdx = firstSmallerOrEqual(x, pointsX);
return pointsY[stepIdx];
};
}
} // namespace reanimated::css
@@ -1,14 +0,0 @@
#pragma once
#include <reanimated/CSS/common/definitions.h>
#include <reanimated/CSS/utils/algorithms.h>
#include <vector>
namespace reanimated::css {
EasingFunction steps(
const std::vector<double> &pointsX,
const std::vector<double> &pointsY);
} // namespace reanimated::css
@@ -1,134 +0,0 @@
#include <reanimated/CSS/interpolation/InterpolatorFactory.h>
namespace reanimated::css {
class RecordInterpolatorFactory : public PropertyInterpolatorFactory {
public:
explicit RecordInterpolatorFactory(
const InterpolatorFactoriesRecord &factories)
: PropertyInterpolatorFactory(), factories_(factories) {}
const CSSValue &getDefaultValue() const override {
static EmptyObjectValue emptyObjectValue;
return emptyObjectValue;
}
std::shared_ptr<PropertyInterpolator> create(
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
const override {
return std::make_shared<RecordPropertiesInterpolator>(
factories_, propertyPath, viewStylesRepository);
}
private:
// Helper private type just for a default value
struct EmptyObjectValue : public CSSValue {
folly::dynamic toDynamic() const override {
return folly::dynamic::object;
}
std::string toString() const override {
return "{}";
}
};
const InterpolatorFactoriesRecord factories_;
};
class ArrayInterpolatorFactory : public PropertyInterpolatorFactory {
public:
explicit ArrayInterpolatorFactory(const InterpolatorFactoriesArray &factories)
: PropertyInterpolatorFactory(), factories_(factories) {}
const CSSValue &getDefaultValue() const override {
static EmptyArrayValue emptyArrayValue;
return emptyArrayValue;
}
std::shared_ptr<PropertyInterpolator> create(
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
const override {
return std::make_shared<ArrayPropertiesInterpolator>(
factories_, propertyPath, viewStylesRepository);
}
private:
// Helper private type just for a default value
struct EmptyArrayValue : public CSSValue {
folly::dynamic toDynamic() const override {
return folly::dynamic::array;
}
std::string toString() const override {
return "[]";
}
};
const InterpolatorFactoriesArray factories_;
};
class TransformsInterpolatorFactory : public PropertyInterpolatorFactory {
public:
explicit TransformsInterpolatorFactory(
const std::shared_ptr<TransformInterpolators> &interpolators)
: PropertyInterpolatorFactory(), interpolators_(interpolators) {}
const CSSValue &getDefaultValue() const override {
static EmptyTransformsValue emptyTransformsValue;
return emptyTransformsValue;
}
std::shared_ptr<PropertyInterpolator> create(
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
const override {
return std::make_shared<TransformsStyleInterpolator>(
propertyPath, interpolators_, viewStylesRepository);
}
private:
static TransformMatrix3D &getIdentityMatrix() {
static TransformMatrix3D identityMatrix = TransformMatrix3D::Identity();
return identityMatrix;
}
// Helper private type just for a default value
struct EmptyTransformsValue : public CSSValue {
folly::dynamic toDynamic() const override {
return getIdentityMatrix().toDynamic();
}
std::string toString() const override {
return getIdentityMatrix().toString();
}
};
const std::shared_ptr<TransformInterpolators> interpolators_;
};
// Non-template function implementations
std::shared_ptr<PropertyInterpolatorFactory> record(
const InterpolatorFactoriesRecord &factories) {
return std::make_shared<RecordInterpolatorFactory>(factories);
}
std::shared_ptr<PropertyInterpolatorFactory> array(
const InterpolatorFactoriesArray &factories) {
return std::make_shared<ArrayInterpolatorFactory>(factories);
}
std::shared_ptr<PropertyInterpolatorFactory> transforms(
const std::unordered_map<
std::string,
std::shared_ptr<TransformInterpolator>> &interpolators) {
TransformInterpolators result;
for (const auto &[property, interpolator] : interpolators) {
result[getTransformOperationType(property)] = interpolator;
}
return std::make_shared<TransformsInterpolatorFactory>(
std::make_shared<TransformInterpolators>(result));
}
} // namespace reanimated::css
@@ -1,194 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/PropertyInterpolator.h>
#include <reanimated/CSS/interpolation/groups/ArrayPropertiesInterpolator.h>
#include <reanimated/CSS/interpolation/groups/RecordPropertiesInterpolator.h>
#include <reanimated/CSS/interpolation/transforms/TransformOperation.h>
#include <reanimated/CSS/interpolation/transforms/TransformOperationInterpolator.h>
#include <reanimated/CSS/interpolation/transforms/TransformsStyleInterpolator.h>
#include <reanimated/CSS/interpolation/values/ResolvableValueInterpolator.h>
#include <reanimated/CSS/interpolation/values/SimpleValueInterpolator.h>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
namespace reanimated::css {
// Template class implementations
template <typename... AllowedTypes>
class SimpleValueInterpolatorFactory : public PropertyInterpolatorFactory {
public:
template <typename TValue>
explicit SimpleValueInterpolatorFactory(const TValue &defaultValue)
: PropertyInterpolatorFactory(), defaultValue_(defaultValue) {}
bool isDiscreteProperty() const override {
// The property is considered discrete if all of the allowed types are
// discrete
return (Discrete<AllowedTypes> && ...);
}
const CSSValue &getDefaultValue() const override {
return defaultValue_;
}
std::shared_ptr<PropertyInterpolator> create(
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
const override {
return std::make_shared<SimpleValueInterpolator<AllowedTypes...>>(
propertyPath, defaultValue_, viewStylesRepository);
}
private:
const CSSValueVariant<AllowedTypes...> defaultValue_;
};
template <typename... AllowedTypes>
class ResolvableValueInterpolatorFactory : public PropertyInterpolatorFactory {
public:
template <typename TValue>
explicit ResolvableValueInterpolatorFactory(
RelativeTo relativeTo,
const std::string &relativeProperty,
const TValue &defaultValue)
: PropertyInterpolatorFactory(),
relativeTo_(relativeTo),
relativeProperty_(relativeProperty),
defaultValue_(defaultValue) {}
const CSSValue &getDefaultValue() const override {
return defaultValue_;
}
std::shared_ptr<PropertyInterpolator> create(
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
const override {
return std::make_shared<ResolvableValueInterpolator<AllowedTypes...>>(
propertyPath,
defaultValue_,
viewStylesRepository,
relativeTo_,
relativeProperty_);
}
private:
const RelativeTo relativeTo_;
const std::string relativeProperty_;
const CSSValueVariant<AllowedTypes...> defaultValue_;
};
/**
* Helper function to create a concrete CSSValue from defaultValue
*/
template <typename... AllowedTypes>
CSSValueVariant<AllowedTypes...> createCSSValue(const auto &defaultValue) {
using ValueType = decltype(defaultValue);
CSSValueVariant<AllowedTypes...> result;
auto tryOne = [&]<typename TCSSValue>() -> bool {
if constexpr (std::is_constructible_v<TCSSValue, ValueType>) {
if constexpr (ValueConstructibleCSSValue<TCSSValue, ValueType>) {
// For construction from a non-jsi::Value, we perform a runtime
// canConstruct check only if the type has a canConstruct method.
// (this is needed e.g. when different CSS value types can be
// constructed from the same value type, like CSSLength and CSSKeyword)
if (!TCSSValue::canConstruct(defaultValue)) {
return false;
}
}
result = CSSValueVariant<AllowedTypes...>(
std::variant<AllowedTypes...>(TCSSValue(defaultValue)));
return true;
}
return false;
};
// Try constructing with each allowed type until one succeeds
if (!(tryOne.template operator()<AllowedTypes>() || ...)) {
throw std::runtime_error(
"[Reanimated] No compatible type found for construction from defaultValue");
}
return result;
}
/**
* Value interpolator factories
*/
template <typename... AllowedTypes>
auto value(const auto &defaultValue) -> std::enable_if_t<
(std::is_constructible_v<AllowedTypes, decltype(defaultValue)> || ...),
std::shared_ptr<PropertyInterpolatorFactory>> {
// Create a concrete CSSValue from the defaultValue
auto cssValue = createCSSValue<AllowedTypes...>(defaultValue);
return std::make_shared<SimpleValueInterpolatorFactory<AllowedTypes...>>(
std::move(cssValue));
}
template <typename... AllowedTypes>
auto value(
RelativeTo relativeTo,
const std::string &relativeProperty,
const auto &defaultValue)
-> std::enable_if_t<
(std::is_constructible_v<AllowedTypes, decltype(defaultValue)> || ...),
std::shared_ptr<PropertyInterpolatorFactory>> {
// Create a concrete CSSValue from the defaultValue
auto cssValue = createCSSValue<AllowedTypes...>(defaultValue);
return std::make_shared<ResolvableValueInterpolatorFactory<AllowedTypes...>>(
relativeTo, relativeProperty, std::move(cssValue));
}
/**
* Transform operation interpolator factories
*/
template <typename TOperation>
auto transformOp(const auto &defaultValue) -> std::enable_if_t<
std::is_base_of_v<TransformOperation, TOperation> &&
std::is_constructible_v<TOperation, decltype(defaultValue)>,
std::shared_ptr<TransformInterpolator>> {
return std::make_shared<TransformOperationInterpolator<TOperation>>(
std::make_shared<TOperation>(defaultValue));
}
template <typename TOperation>
auto transformOp(
RelativeTo relativeTo,
const std::string &relativeProperty,
const auto &defaultValue)
-> std::enable_if_t<
std::is_base_of_v<TransformOperation, TOperation> &&
std::is_constructible_v<TOperation, decltype(defaultValue)> &&
ResolvableOperation<TOperation>,
std::shared_ptr<TransformInterpolator>> {
return std::make_shared<TransformOperationInterpolator<TOperation>>(
std::make_shared<TOperation>(defaultValue), relativeTo, relativeProperty);
}
/**
* Record property interpolator factory
*/
std::shared_ptr<PropertyInterpolatorFactory> record(
const InterpolatorFactoriesRecord &factories);
/**
* Array property interpolator factory
*/
std::shared_ptr<PropertyInterpolatorFactory> array(
const InterpolatorFactoriesArray &factories);
/**
* Transform interpolators
*/
std::shared_ptr<PropertyInterpolatorFactory> transforms(
const std::unordered_map<
std::string,
std::shared_ptr<TransformInterpolator>> &interpolators);
} // namespace reanimated::css
@@ -1,17 +0,0 @@
#include <reanimated/CSS/interpolation/PropertyInterpolator.h>
#include <utility>
namespace reanimated::css {
PropertyInterpolator::PropertyInterpolator(
PropertyPath propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
: propertyPath_(std::move(propertyPath)),
viewStylesRepository_(viewStylesRepository) {}
bool PropertyInterpolatorFactory::isDiscreteProperty() const {
return false;
}
} // namespace reanimated::css
@@ -1,72 +0,0 @@
#pragma once
#include <reanimated/CSS/common/definitions.h>
#include <reanimated/CSS/common/values/CSSValue.h>
#include <reanimated/CSS/misc/ViewStylesRepository.h>
#include <reanimated/CSS/progress/KeyframeProgressProvider.h>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace reanimated::css {
class PropertyInterpolator {
public:
explicit PropertyInterpolator(
PropertyPath propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository);
virtual folly::dynamic getStyleValue(
const std::shared_ptr<const ShadowNode> &shadowNode) const = 0;
virtual folly::dynamic getResetStyle(
const std::shared_ptr<const ShadowNode> &shadowNode) const = 0;
virtual folly::dynamic getFirstKeyframeValue() const = 0;
virtual folly::dynamic getLastKeyframeValue() const = 0;
virtual bool equalsReversingAdjustedStartValue(
const folly::dynamic &propertyValue) const = 0;
virtual void updateKeyframes(
jsi::Runtime &rt,
const jsi::Value &keyframes) = 0;
virtual void updateKeyframesFromStyleChange(
const folly::dynamic &oldStyleValue,
const folly::dynamic &newStyleValue,
const folly::dynamic &lastUpdateValue) = 0;
virtual folly::dynamic interpolate(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::shared_ptr<KeyframeProgressProvider> &progressProvider)
const = 0;
protected:
const PropertyPath propertyPath_;
const std::shared_ptr<ViewStylesRepository> viewStylesRepository_;
};
class PropertyInterpolatorFactory {
public:
PropertyInterpolatorFactory() = default;
virtual ~PropertyInterpolatorFactory() = default;
virtual bool isDiscreteProperty() const;
virtual const CSSValue &getDefaultValue() const = 0;
virtual std::shared_ptr<PropertyInterpolator> create(
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
const = 0;
};
using PropertyInterpolatorsRecord =
std::unordered_map<std::string, std::shared_ptr<PropertyInterpolator>>;
using InterpolatorFactoriesRecord = std::
unordered_map<std::string, std::shared_ptr<PropertyInterpolatorFactory>>;
using PropertyInterpolatorsArray =
std::vector<std::shared_ptr<PropertyInterpolator>>;
using InterpolatorFactoriesArray =
std::vector<std::shared_ptr<PropertyInterpolatorFactory>>;
} // namespace reanimated::css
@@ -1,107 +0,0 @@
#include <reanimated/CSS/interpolation/groups/ArrayPropertiesInterpolator.h>
#include <algorithm>
namespace reanimated::css {
ArrayPropertiesInterpolator::ArrayPropertiesInterpolator(
const InterpolatorFactoriesArray &factories,
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
: GroupPropertiesInterpolator(propertyPath, viewStylesRepository),
factories_(factories) {}
bool ArrayPropertiesInterpolator::equalsReversingAdjustedStartValue(
const folly::dynamic &propertyValue) const {
if (!propertyValue.isArray()) {
return false;
}
const auto valuesCount = propertyValue.size();
if (valuesCount != interpolators_.size()) {
return false;
}
for (size_t i = 0; i < valuesCount; ++i) {
if (!interpolators_[i]->equalsReversingAdjustedStartValue(
propertyValue[i])) {
return false;
}
}
return true;
}
void ArrayPropertiesInterpolator::updateKeyframes(
jsi::Runtime &rt,
const jsi::Value &keyframes) {
const jsi::Array keyframesArray = keyframes.asObject(rt).asArray(rt);
const size_t valuesCount = keyframesArray.size(rt);
resizeInterpolators(valuesCount);
for (size_t i = 0; i < valuesCount; ++i) {
interpolators_[i]->updateKeyframes(
rt, keyframesArray.getValueAtIndex(rt, i));
}
}
void ArrayPropertiesInterpolator::updateKeyframesFromStyleChange(
const folly::dynamic &oldStyleValue,
const folly::dynamic &newStyleValue,
const folly::dynamic &lastUpdateValue) {
const auto emptyArray = folly::dynamic::array();
const auto null = folly::dynamic();
const auto &oldStyleArray =
oldStyleValue.empty() ? emptyArray : oldStyleValue;
const auto &newStyleArray =
newStyleValue.empty() ? emptyArray : newStyleValue;
const auto &lastUpdateArray =
lastUpdateValue.empty() ? emptyArray : lastUpdateValue;
const size_t oldSize = oldStyleArray.size();
const size_t newSize = newStyleArray.size();
const size_t lastSize = lastUpdateArray.size();
const size_t valuesCount = std::max(oldSize, newSize);
resizeInterpolators(valuesCount);
for (size_t i = 0; i < valuesCount; ++i) {
// These index checks ensure that interpolation works between 2 arrays
// with different lengths
interpolators_[i]->updateKeyframesFromStyleChange(
i < oldSize ? oldStyleArray[i] : null,
i < newSize ? newStyleArray[i] : null,
i < lastSize ? lastUpdateArray[i] : null);
}
}
folly::dynamic ArrayPropertiesInterpolator::mapInterpolators(
const std::function<folly::dynamic(PropertyInterpolator &)> &callback)
const {
auto result = folly::dynamic::array();
for (size_t i = 0; i < interpolators_.size(); ++i) {
result.push_back(callback(*interpolators_[i]));
}
return result;
}
void ArrayPropertiesInterpolator::resizeInterpolators(size_t valuesCount) {
// Remove excess interpolators if the array size has decreased
if (interpolators_.size() > valuesCount) {
interpolators_.resize(valuesCount);
}
while (interpolators_.size() < valuesCount) {
interpolators_.emplace_back(createPropertyInterpolator(
interpolators_.size(),
propertyPath_,
factories_,
viewStylesRepository_));
}
}
} // namespace reanimated::css
@@ -1,39 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/groups/GroupPropertiesInterpolator.h>
#include <reanimated/CSS/utils/interpolators.h>
#include <memory>
namespace reanimated::css {
class ArrayPropertiesInterpolator : public GroupPropertiesInterpolator {
public:
ArrayPropertiesInterpolator(
const InterpolatorFactoriesArray &factories,
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository);
virtual ~ArrayPropertiesInterpolator() = default;
bool equalsReversingAdjustedStartValue(
const folly::dynamic &propertyValue) const override;
void updateKeyframes(jsi::Runtime &rt, const jsi::Value &keyframes) override;
void updateKeyframesFromStyleChange(
const folly::dynamic &oldStyleValue,
const folly::dynamic &newStyleValue,
const folly::dynamic &lastUpdateValue) override;
protected:
folly::dynamic mapInterpolators(
const std::function<folly::dynamic(PropertyInterpolator &)> &callback)
const override;
private:
const InterpolatorFactoriesArray &factories_;
PropertyInterpolatorsArray interpolators_;
void resizeInterpolators(size_t valuesCount);
};
} // namespace reanimated::css
@@ -1,49 +0,0 @@
#include <reanimated/CSS/interpolation/groups/GroupPropertiesInterpolator.h>
namespace reanimated::css {
GroupPropertiesInterpolator::GroupPropertiesInterpolator(
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
: PropertyInterpolator(propertyPath, viewStylesRepository) {}
folly::dynamic GroupPropertiesInterpolator::getStyleValue(
const std::shared_ptr<const ShadowNode> &shadowNode) const {
return mapInterpolators(
[&](PropertyInterpolator &interpolator) -> folly::dynamic {
return interpolator.getStyleValue(shadowNode);
});
}
folly::dynamic GroupPropertiesInterpolator::getResetStyle(
const std::shared_ptr<const ShadowNode> &shadowNode) const {
return mapInterpolators(
[&](PropertyInterpolator &interpolator) -> folly::dynamic {
return interpolator.getResetStyle(shadowNode);
});
}
folly::dynamic GroupPropertiesInterpolator::getFirstKeyframeValue() const {
return mapInterpolators(
[&](PropertyInterpolator &interpolator) -> folly::dynamic {
return interpolator.getFirstKeyframeValue();
});
}
folly::dynamic GroupPropertiesInterpolator::getLastKeyframeValue() const {
return mapInterpolators(
[&](PropertyInterpolator &interpolator) -> folly::dynamic {
return interpolator.getLastKeyframeValue();
});
}
folly::dynamic GroupPropertiesInterpolator::interpolate(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::shared_ptr<KeyframeProgressProvider> &progressProvider) const {
return mapInterpolators(
[&](PropertyInterpolator &interpolator) -> folly::dynamic {
return interpolator.interpolate(shadowNode, progressProvider);
});
}
} // namespace reanimated::css
@@ -1,35 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/PropertyInterpolator.h>
#include <reanimated/CSS/misc/ViewStylesRepository.h>
#include <reanimated/CSS/progress/KeyframeProgressProvider.h>
#include <memory>
namespace reanimated::css {
class GroupPropertiesInterpolator : public PropertyInterpolator {
public:
GroupPropertiesInterpolator(
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository);
folly::dynamic getStyleValue(
const std::shared_ptr<const ShadowNode> &shadowNode) const override;
folly::dynamic getResetStyle(
const std::shared_ptr<const ShadowNode> &shadowNode) const override;
folly::dynamic getFirstKeyframeValue() const override;
folly::dynamic getLastKeyframeValue() const override;
folly::dynamic interpolate(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::shared_ptr<KeyframeProgressProvider> &progressProvider)
const override;
protected:
virtual folly::dynamic mapInterpolators(
const std::function<folly::dynamic(PropertyInterpolator &)> &callback)
const = 0;
};
} // namespace reanimated::css
@@ -1,102 +0,0 @@
#include <reanimated/CSS/interpolation/groups/RecordPropertiesInterpolator.h>
#include <unordered_set>
namespace reanimated::css {
RecordPropertiesInterpolator::RecordPropertiesInterpolator(
const InterpolatorFactoriesRecord &factories,
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
: GroupPropertiesInterpolator(propertyPath, viewStylesRepository),
factories_(factories) {}
bool RecordPropertiesInterpolator::equalsReversingAdjustedStartValue(
const folly::dynamic &propertyValue) const {
return std::ranges::all_of(propertyValue.items(), [this](const auto &item) {
const auto &[propName, propValue] = item;
const auto it = interpolators_.find(propName.getString());
return it != interpolators_.end() &&
it->second->equalsReversingAdjustedStartValue(propValue);
});
}
void RecordPropertiesInterpolator::updateKeyframes(
jsi::Runtime &rt,
const jsi::Value &keyframes) {
// TODO - maybe add a possibility to remove interpolators that are no longer
// used (for now, for simplicity, we only add new ones)
const jsi::Object keyframesObject = keyframes.asObject(rt);
jsi::Array propertyNames = keyframesObject.getPropertyNames(rt);
size_t propertiesCount = propertyNames.size(rt);
for (size_t i = 0; i < propertiesCount; ++i) {
const std::string propertyName =
propertyNames.getValueAtIndex(rt, i).asString(rt).utf8(rt);
const jsi::Value &propertyKeyframes = keyframesObject.getProperty(
rt, jsi::PropNameID::forUtf8(rt, propertyName));
maybeCreateInterpolator(propertyName);
interpolators_[propertyName]->updateKeyframes(rt, propertyKeyframes);
}
}
void RecordPropertiesInterpolator::updateKeyframesFromStyleChange(
const folly::dynamic &oldStyleValue,
const folly::dynamic &newStyleValue,
const folly::dynamic &lastUpdateValue) {
// TODO - maybe add a possibility to remove interpolators that are no longer
// used (for now, for simplicity, we only add new ones)
const folly::dynamic emptyObject = folly::dynamic::object();
const auto null = folly::dynamic();
const auto &oldStyleObject =
oldStyleValue.empty() ? emptyObject : oldStyleValue;
const auto &newStyleObject =
newStyleValue.empty() ? emptyObject : newStyleValue;
const auto &lastUpdateObject =
lastUpdateValue.empty() ? emptyObject : lastUpdateValue;
std::unordered_set<std::string> propertyNamesSet;
for (const auto &key : oldStyleObject.keys()) {
propertyNamesSet.insert(key.asString());
}
for (const auto &key : newStyleObject.keys()) {
propertyNamesSet.insert(key.asString());
}
for (const auto &propertyName : propertyNamesSet) {
maybeCreateInterpolator(propertyName);
interpolators_[propertyName]->updateKeyframesFromStyleChange(
oldStyleObject.getDefault(propertyName, null),
newStyleObject.getDefault(propertyName, null),
lastUpdateObject.getDefault(propertyName, null));
}
}
folly::dynamic RecordPropertiesInterpolator::mapInterpolators(
const std::function<folly::dynamic(PropertyInterpolator &)> &callback)
const {
folly::dynamic result = folly::dynamic::object;
for (const auto &[propertyName, interpolator] : interpolators_) {
const auto value = callback(*interpolator);
if (!value.isNull()) {
result[propertyName] = value;
}
}
return result;
}
void RecordPropertiesInterpolator::maybeCreateInterpolator(
const std::string &propertyName) {
if (interpolators_.find(propertyName) == interpolators_.end()) {
const auto newInterpolator = createPropertyInterpolator(
propertyName, propertyPath_, factories_, viewStylesRepository_);
interpolators_.emplace(propertyName, newInterpolator);
}
}
} // namespace reanimated::css
@@ -1,40 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/groups/GroupPropertiesInterpolator.h>
#include <reanimated/CSS/utils/interpolators.h>
#include <memory>
#include <string>
namespace reanimated::css {
class RecordPropertiesInterpolator : public GroupPropertiesInterpolator {
public:
RecordPropertiesInterpolator(
const InterpolatorFactoriesRecord &factories,
const PropertyPath &propertyPath,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository);
virtual ~RecordPropertiesInterpolator() = default;
bool equalsReversingAdjustedStartValue(
const folly::dynamic &propertyValue) const override;
void updateKeyframes(jsi::Runtime &rt, const jsi::Value &keyframes) override;
void updateKeyframesFromStyleChange(
const folly::dynamic &oldStyleValue,
const folly::dynamic &newStyleValue,
const folly::dynamic &lastUpdateValue) override;
protected:
folly::dynamic mapInterpolators(
const std::function<folly::dynamic(PropertyInterpolator &)> &callback)
const override;
void maybeCreateInterpolator(const std::string &propertyName);
private:
const InterpolatorFactoriesRecord &factories_;
PropertyInterpolatorsRecord interpolators_;
};
} // namespace reanimated::css
@@ -1,29 +0,0 @@
#pragma once
#include <reanimated/CSS/InterpolatorRegistry.h>
#include <reanimated/CSS/interpolation/groups/RecordPropertiesInterpolator.h>
#include <memory>
#include <string>
namespace reanimated::css {
// We can just re-use the logic from the RecordPropertiesInterpolator class as
// interpolating multiple properties from the view style during animation is the
// same as interpolating record properties
class AnimationStyleInterpolator : public RecordPropertiesInterpolator {
public:
explicit AnimationStyleInterpolator(
jsi::Runtime &rt,
const jsi::Value &keyframes,
const std::string &componentName,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
: RecordPropertiesInterpolator(
getComponentInterpolators(componentName),
{},
viewStylesRepository) {
updateKeyframes(rt, keyframes);
}
};
} // namespace reanimated::css
@@ -1,115 +0,0 @@
#include <reanimated/CSS/interpolation/styles/TransitionStyleInterpolator.h>
namespace reanimated::css {
TransitionStyleInterpolator::TransitionStyleInterpolator(
const std::string &componentName,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
: componentName_(componentName),
viewStylesRepository_(viewStylesRepository) {}
std::unordered_set<std::string>
TransitionStyleInterpolator::getReversedPropertyNames(
const folly::dynamic &newPropertyValues) const {
std::unordered_set<std::string> reversedProperties;
if (!newPropertyValues.isObject()) {
return reversedProperties;
}
for (const auto &[propertyName, propertyValue] : newPropertyValues.items()) {
const auto propertyNameStr = propertyName.getString();
const auto it = interpolators_.find(propertyNameStr);
if (it != interpolators_.end() &&
// First keyframe value of the previous transition is the reversing
// adjusted start value
it->second->equalsReversingAdjustedStartValue(propertyValue)) {
reversedProperties.insert(propertyNameStr);
}
}
return reversedProperties;
}
folly::dynamic TransitionStyleInterpolator::interpolate(
const std::shared_ptr<const ShadowNode> &shadowNode,
const TransitionProgressProvider &transitionProgressProvider) const {
return mapInterpolators(
transitionProgressProvider,
[&](const std::shared_ptr<PropertyInterpolator> &interpolator,
const std::shared_ptr<KeyframeProgressProvider> &progressProvider) {
return interpolator->interpolate(shadowNode, progressProvider);
});
}
void TransitionStyleInterpolator::discardFinishedInterpolators(
const TransitionProgressProvider &transitionProgressProvider) {
for (const auto &propertyName :
transitionProgressProvider.getRemovedProperties()) {
interpolators_.erase(propertyName);
}
}
void TransitionStyleInterpolator::discardIrrelevantInterpolators(
const std::unordered_set<std::string> &transitionPropertyNames) {
for (auto it = interpolators_.begin(); it != interpolators_.end();) {
// Remove property interpolators for properties not specified in the
// transition property names
if (transitionPropertyNames.find(it->first) ==
transitionPropertyNames.end()) {
it = interpolators_.erase(it);
} else {
++it;
}
}
}
void TransitionStyleInterpolator::updateInterpolatedProperties(
const ChangedProps &changedProps,
const folly::dynamic &lastUpdateValue) {
const auto &oldPropsObj = changedProps.oldProps;
const auto &newPropsObj = changedProps.newProps;
const auto empty = folly::dynamic();
for (const auto &propertyName : changedProps.changedPropertyNames) {
auto it = interpolators_.find(propertyName);
const auto shouldCreateInterpolator = it == interpolators_.end();
if (shouldCreateInterpolator) {
const auto newInterpolator = createPropertyInterpolator(
propertyName,
{},
getComponentInterpolators(componentName_),
viewStylesRepository_);
it = interpolators_.emplace(propertyName, newInterpolator).first;
}
const auto &oldValue = oldPropsObj.getDefault(propertyName, empty);
const auto &newValue = newPropsObj.getDefault(propertyName, empty);
// Pass lastValue only if the interpolator is updated (no new interpolator
// was created), otherwise pass an empty value
const auto &lastValue =
(shouldCreateInterpolator || lastUpdateValue.empty())
? empty
: lastUpdateValue.getDefault(propertyName, empty);
it->second->updateKeyframesFromStyleChange(oldValue, newValue, lastValue);
}
}
folly::dynamic TransitionStyleInterpolator::mapInterpolators(
const TransitionProgressProvider &transitionProgressProvider,
const MapInterpolatorsCallback &callback) const {
folly::dynamic result = folly::dynamic::object;
for (const auto &[propertyName, progressProvider] :
transitionProgressProvider.getPropertyProgressProviders()) {
result[propertyName] =
callback(interpolators_.at(propertyName), progressProvider);
}
return result;
}
} // namespace reanimated::css
@@ -1,51 +0,0 @@
#pragma once
#include <reanimated/CSS/InterpolatorRegistry.h>
#include <reanimated/CSS/common/definitions.h>
#include <reanimated/CSS/interpolation/groups/RecordPropertiesInterpolator.h>
#include <reanimated/CSS/progress/TransitionProgressProvider.h>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
namespace reanimated::css {
class TransitionStyleInterpolator {
public:
TransitionStyleInterpolator(
const std::string &componentName,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository);
std::unordered_set<std::string> getReversedPropertyNames(
const folly::dynamic &newPropertyValues) const;
folly::dynamic interpolate(
const std::shared_ptr<const ShadowNode> &shadowNode,
const TransitionProgressProvider &transitionProgressProvider) const;
void discardFinishedInterpolators(
const TransitionProgressProvider &transitionProgressProvider);
void discardIrrelevantInterpolators(
const std::unordered_set<std::string> &transitionPropertyNames);
void updateInterpolatedProperties(
const ChangedProps &changedProps,
const folly::dynamic &lastUpdateValue);
private:
using MapInterpolatorsCallback = std::function<folly::dynamic(
const std::shared_ptr<PropertyInterpolator> &,
const std::shared_ptr<KeyframeProgressProvider> &)>;
const std::string componentName_;
const std::shared_ptr<ViewStylesRepository> viewStylesRepository_;
PropertyInterpolatorsRecord interpolators_;
folly::dynamic mapInterpolators(
const TransitionProgressProvider &transitionProgressProvider,
const MapInterpolatorsCallback &callback) const;
};
} // namespace reanimated::css
@@ -1,85 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/transforms/TransformOperation.h>
#include <reanimated/CSS/misc/ViewStylesRepository.h>
#include <memory>
#include <unordered_map>
namespace reanimated::css {
class TransformInterpolator {
public:
using Interpolators =
std::unordered_map<TransformOp, std::shared_ptr<TransformInterpolator>>;
struct UpdateContext {
const std::shared_ptr<const ShadowNode> &node;
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository;
const std::shared_ptr<Interpolators> &interpolators;
};
virtual ~TransformInterpolator() = default;
virtual std::shared_ptr<TransformOperation> getDefaultOperation() const = 0;
virtual std::shared_ptr<TransformOperation> interpolate(
double progress,
const std::shared_ptr<TransformOperation> &from,
const std::shared_ptr<TransformOperation> &to,
const UpdateContext &context) const = 0;
virtual std::shared_ptr<TransformOperation> resolveOperation(
const std::shared_ptr<TransformOperation> &operation,
const UpdateContext &context) const = 0;
};
template <typename TOperation>
class TransformInterpolatorBase : public TransformInterpolator {
public:
explicit TransformInterpolatorBase(
std::shared_ptr<TOperation> defaultOperation)
: defaultOperation_(defaultOperation) {}
std::shared_ptr<TransformOperation> getDefaultOperation() const override {
return defaultOperation_;
}
std::shared_ptr<TransformOperation> interpolate(
double progress,
const std::shared_ptr<TransformOperation> &from,
const std::shared_ptr<TransformOperation> &to,
const UpdateContext &context) const override {
return std::make_shared<TOperation>(interpolate(
progress,
*std::static_pointer_cast<TOperation>(from),
*std::static_pointer_cast<TOperation>(to),
context));
}
std::shared_ptr<TransformOperation> resolveOperation(
const std::shared_ptr<TransformOperation> &operation,
const UpdateContext &context) const override {
return std::make_shared<TOperation>(resolveOperation(
*std::static_pointer_cast<TOperation>(operation), context));
}
protected:
virtual TOperation interpolate(
double progress,
const TOperation &from,
const TOperation &to,
const UpdateContext &context) const = 0;
virtual TOperation resolveOperation(
const TOperation &operation,
const UpdateContext &context) const {
return operation;
}
private:
std::shared_ptr<TOperation> defaultOperation_;
};
using TransformInterpolators = TransformInterpolator::Interpolators;
using TransformInterpolatorUpdateContext = TransformInterpolator::UpdateContext;
} // namespace reanimated::css
@@ -1,225 +0,0 @@
#include <reanimated/CSS/interpolation/transforms/TransformOperation.h>
#include <reanimated/CSS/interpolation/transforms/operations/matrix.h>
#include <reanimated/CSS/interpolation/transforms/operations/perspective.h>
#include <reanimated/CSS/interpolation/transforms/operations/rotate.h>
#include <reanimated/CSS/interpolation/transforms/operations/scale.h>
#include <reanimated/CSS/interpolation/transforms/operations/skew.h>
#include <reanimated/CSS/interpolation/transforms/operations/translate.h>
namespace reanimated::css {
#ifndef NDEBUG
std::ostream &operator<<(
std::ostream &os,
const TransformOperation &operation) {
os << operation.getOperationName() << "("
<< operation.stringifyOperationValue() << ")";
return os;
}
#endif // NDEBUG
bool TransformOperation::canConvertTo(const TransformOp targetType) const {
return false;
}
void TransformOperation::assertCanConvertTo(
const TransformOp targetType) const {
if (!canConvertTo(targetType)) {
throw std::invalid_argument(
"[Reanimated] Cannot convert transform operation to type: " +
getOperationNameFromType(targetType));
}
}
TransformOperations TransformOperation::convertTo(
const TransformOp targetType) const {
throw std::invalid_argument(
"[Reanimated] Cannot convert transform operation to type: " +
getOperationNameFromType(targetType));
}
std::string TransformOperation::getOperationName() const {
return getOperationNameFromType(type());
}
bool TransformOperation::isRelative() const {
return false;
}
std::shared_ptr<TransformOperation> TransformOperation::fromJSIValue(
jsi::Runtime &rt,
const jsi::Value &value) {
if (!value.isObject()) {
throw std::invalid_argument(
"[Reanimated] TransformOperation must be an object.");
}
jsi::Object obj = value.asObject(rt);
auto propertyNames = obj.getPropertyNames(rt);
if (propertyNames.size(rt) != 1) {
throw std::invalid_argument(
"[Reanimated] TransformOperation must have exactly one property.");
}
const auto propertyName =
propertyNames.getValueAtIndex(rt, 0).asString(rt).utf8(rt);
const auto propertyValue =
obj.getProperty(rt, jsi::PropNameID::forUtf8(rt, propertyName));
TransformOp operationType = getTransformOperationType(propertyName);
switch (operationType) {
case TransformOp::Perspective:
return std::make_shared<PerspectiveOperation>(propertyValue.asNumber());
case TransformOp::Rotate:
return std::make_shared<RotateOperation>(
propertyValue.asString(rt).utf8(rt));
case TransformOp::RotateX:
return std::make_shared<RotateXOperation>(
propertyValue.asString(rt).utf8(rt));
case TransformOp::RotateY:
return std::make_shared<RotateYOperation>(
propertyValue.asString(rt).utf8(rt));
case TransformOp::RotateZ:
return std::make_shared<RotateZOperation>(
propertyValue.asString(rt).utf8(rt));
case TransformOp::Scale:
return std::make_shared<ScaleOperation>(propertyValue.asNumber());
case TransformOp::ScaleX:
return std::make_shared<ScaleXOperation>(propertyValue.asNumber());
case TransformOp::ScaleY:
return std::make_shared<ScaleYOperation>(propertyValue.asNumber());
case TransformOp::TranslateX: {
if (propertyValue.isNumber()) {
return std::make_shared<TranslateXOperation>(propertyValue.asNumber());
}
return std::make_shared<TranslateXOperation>(
propertyValue.asString(rt).utf8(rt));
}
case TransformOp::TranslateY: {
if (propertyValue.isNumber()) {
return std::make_shared<TranslateYOperation>(propertyValue.asNumber());
}
return std::make_shared<TranslateYOperation>(
propertyValue.asString(rt).utf8(rt));
}
case TransformOp::SkewX:
return std::make_shared<SkewXOperation>(
propertyValue.asString(rt).utf8(rt));
case TransformOp::SkewY:
return std::make_shared<SkewYOperation>(
propertyValue.asString(rt).utf8(rt));
case TransformOp::Matrix:
return std::make_shared<MatrixOperation>(
TransformMatrix3D(rt, propertyValue));
default:
throw std::invalid_argument(
"[Reanimated] Unknown transform operation: " + propertyName);
}
}
std::shared_ptr<TransformOperation> TransformOperation::fromDynamic(
const folly::dynamic &value) {
if (!value.isObject()) {
throw std::invalid_argument(
"[Reanimated] TransformOperation must be an object.");
}
auto &obj = value;
if (obj.size() != 1) {
throw std::invalid_argument(
"[Reanimated] TransformOperation must have exactly one property.");
}
auto propertyName = obj.items().begin()->first.getString();
auto propertyValue = obj.items().begin()->second;
TransformOp operationType = getTransformOperationType(propertyName);
switch (operationType) {
case TransformOp::Perspective:
return std::make_shared<PerspectiveOperation>(propertyValue.getDouble());
case TransformOp::Rotate:
return std::make_shared<RotateOperation>(propertyValue.getString());
case TransformOp::RotateX:
return std::make_shared<RotateXOperation>(propertyValue.getString());
case TransformOp::RotateY:
return std::make_shared<RotateYOperation>(propertyValue.getString());
case TransformOp::RotateZ:
return std::make_shared<RotateZOperation>(propertyValue.getString());
case TransformOp::Scale:
return std::make_shared<ScaleOperation>(propertyValue.getDouble());
case TransformOp::ScaleX:
return std::make_shared<ScaleXOperation>(propertyValue.getDouble());
case TransformOp::ScaleY:
return std::make_shared<ScaleYOperation>(propertyValue.getDouble());
case TransformOp::TranslateX: {
if (propertyValue.isNumber()) {
return std::make_shared<TranslateXOperation>(propertyValue.getDouble());
}
return std::make_shared<TranslateXOperation>(propertyValue.getString());
}
case TransformOp::TranslateY: {
if (propertyValue.isNumber()) {
return std::make_shared<TranslateYOperation>(propertyValue.getDouble());
}
return std::make_shared<TranslateYOperation>(propertyValue.getString());
}
case TransformOp::SkewX:
return std::make_shared<SkewXOperation>(propertyValue.getString());
case TransformOp::SkewY:
return std::make_shared<SkewYOperation>(propertyValue.getString());
case TransformOp::Matrix:
return std::make_shared<MatrixOperation>(
TransformMatrix3D(propertyValue));
default:
throw std::invalid_argument(
"[Reanimated] Unknown transform operation: " + propertyName);
}
}
folly::dynamic TransformOperation::toDynamic() const {
return folly::dynamic::object(getOperationName(), valueToDynamic());
}
// Specialization for the matrix operation
#ifndef NDEBUG
template <TransformOp TOperation, typename TValue>
std::string
TransformOperationBase<TOperation, TValue>::stringifyOperationValue() const {
std::ostringstream ss;
ss << value;
return ss.str();
}
template <>
std::string TransformOperationBase<
TransformOp::Matrix,
std::variant<TransformMatrix3D, TransformOperations>>::
stringifyOperationValue() const {
std::ostringstream ss;
if (std::holds_alternative<TransformMatrix3D>(value)) {
ss << std::get<TransformMatrix3D>(value);
} else {
const auto &operations = std::get<TransformOperations>(value);
std::ostringstream ss;
for (const auto &operation : operations) {
ss << operation->getOperationName() << "("
<< operation->stringifyOperationValue() << "), ";
}
}
return ss.str();
}
#endif // NDEBUG
template struct TransformOperationBase<
TransformOp::Matrix,
std::variant<TransformMatrix3D, TransformOperations>>;
} // namespace reanimated::css
@@ -1,87 +0,0 @@
#pragma once
#include <reanimated/CSS/common/definitions.h>
#include <reanimated/CSS/common/transforms/TransformMatrix3D.h>
#include <reanimated/CSS/common/transforms/TransformOp.h>
#include <reanimated/CSS/common/values/CSSAngle.h>
#include <reanimated/CSS/common/values/CSSLength.h>
#include <reanimated/CSS/common/values/CSSNumber.h>
#include <react/renderer/core/ShadowNode.h>
#include <memory>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>
#ifndef NDEBUG
#include <iostream>
#include <sstream>
#endif // NDEBUG
namespace reanimated::css {
using namespace facebook;
using namespace react;
// Base struct for TransformOperation
struct TransformOperation {
virtual bool operator==(const TransformOperation &other) const = 0;
#ifndef NDEBUG
friend std::ostream &operator<<(
std::ostream &os,
const TransformOperation &operation);
virtual std::string stringifyOperationValue() const = 0;
#endif // NDEBUG
std::string getOperationName() const;
virtual TransformOp type() const = 0;
virtual bool isRelative() const;
static std::shared_ptr<TransformOperation> fromJSIValue(
jsi::Runtime &rt,
const jsi::Value &value);
static std::shared_ptr<TransformOperation> fromDynamic(
const folly::dynamic &value);
folly::dynamic toDynamic() const;
virtual folly::dynamic valueToDynamic() const = 0;
virtual bool canConvertTo(TransformOp type) const;
virtual std::vector<std::shared_ptr<TransformOperation>> convertTo(
TransformOp type) const;
virtual TransformMatrix3D toMatrix() const = 0;
void assertCanConvertTo(TransformOp type) const;
};
using TransformOperations = std::vector<std::shared_ptr<TransformOperation>>;
// Template overload to inherit from in final operation structs
template <TransformOp TOperation, typename TValue>
struct TransformOperationBase : public TransformOperation {
const TValue value;
explicit TransformOperationBase(const TValue &value) : value(value) {}
virtual ~TransformOperationBase() = default;
TransformOp type() const override {
return TOperation;
}
bool operator==(const TransformOperation &other) const override {
if (type() != other.type()) {
return false;
}
const auto &otherOperation =
static_cast<const TransformOperationBase<TOperation, TValue> &>(other);
return value == otherOperation.value;
}
#ifndef NDEBUG
std::string stringifyOperationValue() const override;
#endif // NDEBUG
};
} // namespace reanimated::css
@@ -1,64 +0,0 @@
#include <reanimated/CSS/interpolation/transforms/TransformOperationInterpolator.h>
namespace reanimated::css {
PerspectiveOperation
TransformOperationInterpolator<PerspectiveOperation>::interpolate(
double progress,
const PerspectiveOperation &from,
const PerspectiveOperation &to,
const TransformInterpolatorUpdateContext &context) const {
if (to.value.value == 0)
return PerspectiveOperation(0);
if (from.value.value == 0)
return PerspectiveOperation(to.value);
return PerspectiveOperation(from.value.interpolate(progress, to.value));
}
MatrixOperation TransformOperationInterpolator<MatrixOperation>::interpolate(
double progress,
const MatrixOperation &from,
const MatrixOperation &to,
const TransformInterpolatorUpdateContext &context) const {
const auto fromMatrix = matrixFromOperation(from, context);
const auto toMatrix = matrixFromOperation(to, context);
const auto decomposedFrom = fromMatrix.decompose();
const auto decomposedTo = toMatrix.decompose();
if (!decomposedFrom.has_value() || !decomposedTo.has_value()) {
return MatrixOperation(progress < 0.5 ? fromMatrix : toMatrix);
}
return MatrixOperation(TransformMatrix3D::recompose(
decomposedFrom->interpolate(progress, decomposedTo.value())));
}
TransformMatrix3D
TransformOperationInterpolator<MatrixOperation>::matrixFromOperation(
const MatrixOperation &matrixOperation,
const TransformInterpolatorUpdateContext &context) const {
if (std::holds_alternative<TransformOperations>(matrixOperation.value)) {
const auto &operations =
std::get<TransformOperations>(matrixOperation.value);
TransformMatrix3D matrix = TransformMatrix3D::Identity();
for (int i = static_cast<int>(operations.size()) - 1; i >= 0; i--) {
auto operation = operations[i];
if (operation->isRelative()) {
const auto &interpolator = context.interpolators->at(operation->type());
operation = interpolator->resolveOperation(operation, context);
}
matrix *= operation->toMatrix();
}
return matrix;
}
return std::get<TransformMatrix3D>(matrixOperation.value);
}
} // namespace reanimated::css
@@ -1,124 +0,0 @@
#pragma once
#include <reanimated/CSS/common/values/CSSValue.h>
#include <reanimated/CSS/interpolation/transforms/TransformInterpolator.h>
#include <reanimated/CSS/interpolation/transforms/operations/matrix.h>
#include <reanimated/CSS/interpolation/transforms/operations/perspective.h>
#include <memory>
#include <string>
namespace reanimated::css {
template <typename TOperation>
concept ResolvableOperation = requires(TOperation operation) {
{
operation.value
} -> std::convertible_to<
typename std::remove_reference_t<decltype(operation.value)>>;
requires Resolvable<std::remove_reference_t<decltype(operation.value)>>;
}; // NOLINT(readability/braces)
// Base implementation for simple operations
template <typename OperationType>
class TransformOperationInterpolator
: public TransformInterpolatorBase<OperationType> {
public:
TransformOperationInterpolator(
std::shared_ptr<OperationType> defaultOperation)
: TransformInterpolatorBase<OperationType>(defaultOperation) {}
OperationType interpolate(
double progress,
const OperationType &from,
const OperationType &to,
const TransformInterpolatorUpdateContext &context) const override {
return OperationType{from.value.interpolate(progress, to.value)};
}
};
// Specialization for PerspectiveOperation
template <>
class TransformOperationInterpolator<PerspectiveOperation>
: public TransformInterpolatorBase<PerspectiveOperation> {
public:
using TransformInterpolatorBase<
PerspectiveOperation>::TransformInterpolatorBase;
PerspectiveOperation interpolate(
double progress,
const PerspectiveOperation &from,
const PerspectiveOperation &to,
const TransformInterpolatorUpdateContext &context) const override;
};
// Specialization for MatrixOperation
template <>
class TransformOperationInterpolator<MatrixOperation>
: public TransformInterpolatorBase<MatrixOperation> {
public:
using TransformInterpolatorBase<MatrixOperation>::TransformInterpolatorBase;
MatrixOperation interpolate(
double progress,
const MatrixOperation &from,
const MatrixOperation &to,
const TransformInterpolatorUpdateContext &context) const override;
private:
TransformMatrix3D matrixFromOperation(
const MatrixOperation &matrixOperation,
const TransformInterpolatorUpdateContext &context) const;
};
// Specialization for resolvable operations
template <ResolvableOperation TOperation>
class TransformOperationInterpolator<TOperation>
: public TransformInterpolatorBase<TOperation> {
public:
TransformOperationInterpolator(
const std::shared_ptr<TOperation> &defaultOperation,
RelativeTo relativeTo,
const std::string &relativeProperty)
: TransformInterpolatorBase<TOperation>(defaultOperation),
relativeTo_(relativeTo),
relativeProperty_(relativeProperty) {}
TOperation interpolate(
double progress,
const TOperation &from,
const TOperation &to,
const TransformInterpolatorUpdateContext &context) const override {
return TOperation{from.value.interpolate(
progress, to.value, getResolvableValueContext(context))};
}
TOperation resolveOperation(
const TOperation &operation,
const TransformInterpolatorUpdateContext &context) const override {
const auto &resolved =
operation.value.resolve(getResolvableValueContext(context));
if (!resolved.has_value()) {
return TOperation{operation.value};
}
return TOperation{resolved.value()};
}
private:
const RelativeTo relativeTo_;
const std::string relativeProperty_;
CSSResolvableValueInterpolationContext getResolvableValueContext(
const TransformInterpolatorUpdateContext &context) const {
return {
.node = context.node,
.viewStylesRepository = context.viewStylesRepository,
.relativeProperty = relativeProperty_,
.relativeTo = relativeTo_,
};
}
};
} // namespace reanimated::css
@@ -1,405 +0,0 @@
#include <reanimated/CSS/interpolation/transforms/TransformsStyleInterpolator.h>
namespace reanimated::css {
const TransformOperations TransformsStyleInterpolator::defaultStyleValue_ = {
std::make_shared<MatrixOperation>(TransformMatrix3D::Identity())};
TransformsStyleInterpolator::TransformsStyleInterpolator(
const PropertyPath &propertyPath,
const std::shared_ptr<TransformInterpolators> &interpolators,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
: PropertyInterpolator(propertyPath, viewStylesRepository),
interpolators_(interpolators) {}
folly::dynamic TransformsStyleInterpolator::getStyleValue(
const std::shared_ptr<const ShadowNode> &shadowNode) const {
return viewStylesRepository_->getStyleProp(
shadowNode->getTag(), propertyPath_);
}
folly::dynamic TransformsStyleInterpolator::getResetStyle(
const std::shared_ptr<const ShadowNode> &shadowNode) const {
auto styleValue = getStyleValue(shadowNode);
if (!styleValue.isArray()) {
return convertResultToDynamic(defaultStyleValue_);
}
return styleValue;
}
folly::dynamic TransformsStyleInterpolator::getFirstKeyframeValue() const {
return convertResultToDynamic(
keyframes_.front()->fromOperations.value_or(defaultStyleValue_));
}
folly::dynamic TransformsStyleInterpolator::getLastKeyframeValue() const {
return convertResultToDynamic(
keyframes_.back()->toOperations.value_or(defaultStyleValue_));
}
bool TransformsStyleInterpolator::equalsReversingAdjustedStartValue(
const folly::dynamic &propertyValue) const {
const auto propertyOperations = parseTransformOperations(propertyValue);
if (!reversingAdjustedStartValue_.has_value()) {
return !propertyOperations.has_value();
} else if (!propertyOperations.has_value()) {
return false;
}
const auto &reversingAdjustedOperationsValue =
reversingAdjustedStartValue_.value();
const auto &propertyOperationsValue = propertyOperations.value();
if (reversingAdjustedOperationsValue.size() !=
propertyOperationsValue.size()) {
return false;
}
for (size_t i = 0; i < reversingAdjustedOperationsValue.size(); ++i) {
if (*reversingAdjustedOperationsValue[i] != *propertyOperationsValue[i]) {
return false;
}
}
return true;
}
folly::dynamic TransformsStyleInterpolator::interpolate(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::shared_ptr<KeyframeProgressProvider> &progressProvider) const {
const auto currentIndex = getIndexOfCurrentKeyframe(progressProvider);
// Get or create the current keyframe
auto keyframe = keyframes_[currentIndex];
if (!keyframe->fromOperations.has_value() ||
!keyframe->toOperations.has_value()) {
// If the value is nullopt, we would have to read it from the view style
// and build the keyframe again
const auto fallbackValue = getFallbackValue(shadowNode);
keyframe = createTransformKeyframe(
keyframe->fromOffset,
keyframe->toOffset,
keyframe->fromOperations.value_or(fallbackValue),
keyframe->toOperations.value_or(fallbackValue));
}
// Interpolate the current keyframe
TransformOperations result = interpolateOperations(
shadowNode,
progressProvider->getKeyframeProgress(
keyframe->fromOffset, keyframe->toOffset),
keyframe->fromOperations.value(),
keyframe->toOperations.value());
return convertResultToDynamic(result);
}
void TransformsStyleInterpolator::updateKeyframes(
jsi::Runtime &rt,
const jsi::Value &keyframes) {
// Step 1: Parse keyframes
const auto parsedKeyframes = parseJSIKeyframes(rt, keyframes);
// Step 2: Convert keyframes to TransformOperations
std::vector<std::pair<double, std::optional<TransformOperations>>> operations;
operations.reserve(parsedKeyframes.size());
for (const auto &[offset, value] : parsedKeyframes) {
operations.emplace_back(offset, parseTransformOperations(rt, value));
}
// Step 3: Prepare keyframe interpolation pairs (convert keyframe values in
// both keyframes to the same type)
// There will be one less keyframe than the number of keyframes in the jsi
// array as we create interpolation pairs
const auto keyframesCount = operations.size() - 1;
keyframes_.clear();
keyframes_.reserve(keyframesCount);
for (size_t i = 0; i < keyframesCount; ++i) {
keyframes_.push_back(createTransformKeyframe(
operations[i].first,
operations[i + 1].first,
operations[i].second,
operations[i + 1].second));
}
}
void TransformsStyleInterpolator::updateKeyframesFromStyleChange(
const folly::dynamic &oldStyleValue,
const folly::dynamic &newStyleValue,
const folly::dynamic &lastUpdateValue) {
if (oldStyleValue.isNull()) {
reversingAdjustedStartValue_ = std::nullopt;
} else {
reversingAdjustedStartValue_ = parseTransformOperations(oldStyleValue);
}
const auto &prevStyleValue =
lastUpdateValue.isNull() ? oldStyleValue : lastUpdateValue;
keyframes_.clear();
keyframes_.reserve(1);
keyframes_.emplace_back(createTransformKeyframe(
0,
1,
parseTransformOperations(prevStyleValue).value_or(TransformOperations{}),
parseTransformOperations(newStyleValue).value_or(TransformOperations{})));
}
std::optional<TransformOperations>
TransformsStyleInterpolator::parseTransformOperations(
jsi::Runtime &rt,
const jsi::Value &values) {
if (values.isUndefined()) {
return std::nullopt;
}
const auto transformsArray = values.asObject(rt).asArray(rt);
const auto transformsCount = transformsArray.size(rt);
TransformOperations transformOperations;
transformOperations.reserve(transformsCount);
for (size_t i = 0; i < transformsCount; ++i) {
const auto transform = transformsArray.getValueAtIndex(rt, i);
transformOperations.emplace_back(
TransformOperation::fromJSIValue(rt, transform));
}
return transformOperations;
}
std::optional<TransformOperations>
TransformsStyleInterpolator::parseTransformOperations(
const folly::dynamic &values) {
if (values.empty()) {
return std::nullopt;
}
const auto &transformsArray = values;
const auto transformsCount = transformsArray.size();
TransformOperations transformOperations;
transformOperations.reserve(transformsCount);
for (size_t i = 0; i < transformsCount; ++i) {
const auto &transform = transformsArray[i];
transformOperations.emplace_back(
TransformOperation::fromDynamic(transform));
}
return transformOperations;
}
std::shared_ptr<TransformKeyframe>
TransformsStyleInterpolator::createTransformKeyframe(
const double fromOffset,
const double toOffset,
const std::optional<TransformOperations> &fromOperationsOptional,
const std::optional<TransformOperations> &toOperationsOptional) const {
// If nullopt is passed, return values as is (we will have to read the
// transform value from the view style later on and create the new keyframe
// then)
if (!fromOperationsOptional.has_value() ||
!toOperationsOptional.has_value()) {
return std::make_shared<TransformKeyframe>(TransformKeyframe{
fromOffset, toOffset, fromOperationsOptional, toOperationsOptional});
}
const auto [fromOperations, toOperations] = createTransformInterpolationPair(
fromOperationsOptional.value(), toOperationsOptional.value());
return std::make_shared<TransformKeyframe>(
TransformKeyframe{fromOffset, toOffset, fromOperations, toOperations});
}
void TransformsStyleInterpolator::addConvertedOperations(
const std::shared_ptr<TransformOperation> &sourceOperation,
const std::shared_ptr<TransformOperation> &targetOperation,
TransformOperations &sourceResult,
TransformOperations &targetResult) const {
const auto convertedOps = sourceOperation->convertTo(targetOperation->type());
targetResult.emplace_back(targetOperation);
for (size_t k = 0; k < convertedOps.size(); ++k) {
sourceResult.emplace_back(convertedOps[k]);
// Converted operations will contain one operation with the same type and
// can contain more operations derived from the source operation (we need
// to pair them with operations of the same type with default values)
if (k > 0) {
targetResult.emplace_back(
getDefaultOperationOfType(convertedOps[k]->type()));
}
}
}
std::pair<TransformOperations, TransformOperations>
TransformsStyleInterpolator::createTransformInterpolationPair(
const TransformOperations &fromOperations,
const TransformOperations &toOperations) const {
TransformOperations fromOperationsResult, toOperationsResult;
size_t i = 0, j = 0;
bool shouldInterpolateMatrices = false;
// Build index maps and check for matrix operation
std::unordered_map<TransformOp, size_t> lastIndexInFrom, lastIndexInTo;
for (size_t idx = 0; idx < fromOperations.size(); ++idx) {
if (fromOperations[idx]->type() == TransformOp::Matrix) {
shouldInterpolateMatrices = true;
break;
}
lastIndexInFrom[fromOperations[idx]->type()] = idx;
}
for (size_t idx = 0; idx < toOperations.size() && !shouldInterpolateMatrices;
++idx) {
if (toOperations[idx]->type() == TransformOp::Matrix) {
shouldInterpolateMatrices = true;
break;
}
lastIndexInTo[toOperations[idx]->type()] = idx;
}
while (!shouldInterpolateMatrices && i < fromOperations.size() &&
j < toOperations.size()) {
const auto &fromOperation = fromOperations[i];
const auto &toOperation = toOperations[j];
// Case 1: Types match directly
if (fromOperation->type() == toOperation->type()) {
fromOperationsResult.emplace_back(fromOperation);
toOperationsResult.emplace_back(toOperation);
i++;
j++;
} else if (fromOperation->canConvertTo(toOperation->type())) {
// Case 2: Operations can be converted to each other's type
addConvertedOperations(
fromOperation, toOperation, fromOperationsResult, toOperationsResult);
i++;
j++;
} else if (toOperation->canConvertTo(fromOperation->type())) {
addConvertedOperations(
toOperation, fromOperation, toOperationsResult, fromOperationsResult);
i++;
j++;
} else {
// Case 3: Use default values if no conversion possible
bool toExistsLaterInFrom = lastIndexInFrom.count(toOperation->type()) &&
lastIndexInFrom[toOperation->type()] > i;
bool fromExistsLaterInTo = lastIndexInTo.count(fromOperation->type()) &&
lastIndexInTo[fromOperation->type()] > j;
if (toExistsLaterInFrom == fromExistsLaterInTo) {
// If neither exists later, or both exist later (were reordered), we
// cannot interpolate the operations directly and we need to convert
// these operations to matrices
shouldInterpolateMatrices = true;
break;
} else if (!fromExistsLaterInTo) {
// If fromOperation does not exist later in toOperations, we can
// interpolate it to the default value
fromOperationsResult.emplace_back(fromOperation);
toOperationsResult.emplace_back(
getDefaultOperationOfType(fromOperation->type()));
i++;
} else {
// If toOperation does not exist later in fromOperations, we can
// interpolate it from the default value
fromOperationsResult.emplace_back(
getDefaultOperationOfType(toOperation->type()));
toOperationsResult.emplace_back(toOperation);
j++;
}
}
}
// Convert all operations to matrices if matrix interpolation is required
if (shouldInterpolateMatrices) {
return std::make_pair(
TransformOperations{
std::make_shared<MatrixOperation>(MatrixOperation(fromOperations))},
TransformOperations{
std::make_shared<MatrixOperation>(MatrixOperation(toOperations))});
}
// Add remaining operations with default values
for (; i < fromOperations.size(); ++i) {
fromOperationsResult.emplace_back(fromOperations[i]);
toOperationsResult.emplace_back(
getDefaultOperationOfType(fromOperations[i]->type()));
}
for (; j < toOperations.size(); ++j) {
fromOperationsResult.emplace_back(
getDefaultOperationOfType(toOperations[j]->type()));
toOperationsResult.emplace_back(toOperations[j]);
}
return std::make_pair(fromOperationsResult, toOperationsResult);
}
size_t TransformsStyleInterpolator::getIndexOfCurrentKeyframe(
const std::shared_ptr<KeyframeProgressProvider> &progressProvider) const {
const auto progress = progressProvider->getGlobalProgress();
const auto it = std::lower_bound(
keyframes_.begin(),
keyframes_.end(),
progress,
[](const std::shared_ptr<TransformKeyframe> &keyframe, double progress) {
return keyframe->toOffset < progress;
});
// If we're at the end, return the last valid keyframe index
if (it == keyframes_.end()) {
return keyframes_.size() - 1;
}
return std::distance(keyframes_.begin(), it);
}
TransformOperations TransformsStyleInterpolator::getFallbackValue(
const std::shared_ptr<const ShadowNode> &shadowNode) const {
const auto &styleValue = getStyleValue(shadowNode);
return parseTransformOperations(styleValue).value_or(TransformOperations{});
}
std::shared_ptr<TransformOperation>
TransformsStyleInterpolator::getDefaultOperationOfType(
const TransformOp type) const {
return interpolators_->at(type)->getDefaultOperation();
}
TransformOperations TransformsStyleInterpolator::interpolateOperations(
const std::shared_ptr<const ShadowNode> &shadowNode,
const double keyframeProgress,
const TransformOperations &fromOperations,
const TransformOperations &toOperations) const {
TransformOperations result;
result.reserve(fromOperations.size());
const auto transformUpdateContext = TransformInterpolatorUpdateContext{
shadowNode, viewStylesRepository_, interpolators_};
for (size_t i = 0; i < fromOperations.size(); ++i) {
const auto &fromOperation = fromOperations[i];
const auto &toOperation = toOperations[i];
// fromOperation and toOperation have the same type
const auto &interpolator = interpolators_->at(fromOperation->type());
result.emplace_back(interpolator->interpolate(
keyframeProgress, fromOperation, toOperation, transformUpdateContext));
}
return result;
}
folly::dynamic TransformsStyleInterpolator::convertResultToDynamic(
const TransformOperations &operations) {
auto result = folly::dynamic::array();
for (const auto &operation : operations) {
result.push_back(operation->toDynamic());
}
return result;
}
} // namespace reanimated::css
@@ -1,98 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/PropertyInterpolator.h>
#include <reanimated/CSS/interpolation/transforms/TransformInterpolator.h>
#include <reanimated/CSS/interpolation/transforms/operations/matrix.h>
#include <reanimated/CSS/utils/keyframes.h>
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>
namespace reanimated::css {
struct TransformKeyframe {
const double fromOffset;
const double toOffset;
// If the value is nullopt, we would have to read it from the view style
// (in all other cases, both vectors will have the same number of elements of
// corresponding types - elements from the same index will form interpolation
// pairs)
const std::optional<TransformOperations> fromOperations;
const std::optional<TransformOperations> toOperations;
};
class TransformsStyleInterpolator final : public PropertyInterpolator {
public:
TransformsStyleInterpolator(
const PropertyPath &propertyPath,
const std::shared_ptr<TransformInterpolators> &interpolators,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository);
folly::dynamic getStyleValue(
const std::shared_ptr<const ShadowNode> &shadowNode) const override;
folly::dynamic getResetStyle(
const std::shared_ptr<const ShadowNode> &shadowNode) const override;
folly::dynamic getFirstKeyframeValue() const override;
folly::dynamic getLastKeyframeValue() const override;
bool equalsReversingAdjustedStartValue(
const folly::dynamic &propertyValue) const override;
folly::dynamic interpolate(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::shared_ptr<KeyframeProgressProvider> &progressProvider)
const override;
void updateKeyframes(jsi::Runtime &rt, const jsi::Value &keyframes) override;
void updateKeyframesFromStyleChange(
const folly::dynamic &oldStyleValue,
const folly::dynamic &newStyleValue,
const folly::dynamic &lastUpdateValue) override;
private:
const std::shared_ptr<TransformInterpolators> interpolators_;
static const TransformOperations defaultStyleValue_;
std::vector<std::shared_ptr<TransformKeyframe>> keyframes_;
std::optional<TransformOperations> reversingAdjustedStartValue_;
static std::optional<TransformOperations> parseTransformOperations(
jsi::Runtime &rt,
const jsi::Value &values);
static std::optional<TransformOperations> parseTransformOperations(
const folly::dynamic &values);
std::shared_ptr<TransformKeyframe> createTransformKeyframe(
double fromOffset,
double toOffset,
const std::optional<TransformOperations> &fromOperationsOptional,
const std::optional<TransformOperations> &toOperationsOptional) const;
std::pair<TransformOperations, TransformOperations>
createTransformInterpolationPair(
const TransformOperations &fromOperations,
const TransformOperations &toOperations) const;
void addConvertedOperations(
const std::shared_ptr<TransformOperation> &sourceOperation,
const std::shared_ptr<TransformOperation> &targetOperation,
TransformOperations &sourceResult,
TransformOperations &targetResult) const;
std::shared_ptr<TransformOperation> getDefaultOperationOfType(
TransformOp type) const;
size_t getIndexOfCurrentKeyframe(
const std::shared_ptr<KeyframeProgressProvider> &progressProvider) const;
TransformOperations getFallbackValue(
const std::shared_ptr<const ShadowNode> &shadowNode) const;
TransformOperations interpolateOperations(
const std::shared_ptr<const ShadowNode> &shadowNode,
double keyframeProgress,
const TransformOperations &fromOperations,
const TransformOperations &toOperations) const;
static folly::dynamic convertResultToDynamic(
const TransformOperations &operations);
TransformInterpolatorUpdateContext createUpdateContext(
const std::shared_ptr<const ShadowNode> &shadowNode) const;
};
} // namespace reanimated::css
@@ -1,136 +0,0 @@
#include <reanimated/CSS/interpolation/transforms/operations/matrix.h>
namespace reanimated::css {
// Matrix
std::variant<TransformMatrix3D, TransformOperations> simplifyOperations(
const TransformOperations &operations) {
// Initialize the stack with the reversed list of operations
std::vector<std::shared_ptr<TransformOperation>> operationsStack(
operations.begin(), operations.end());
TransformOperations reversedOperations;
TransformMatrix3D simplifiedMatrix = TransformMatrix3D::Identity();
bool hasSimplifications = false;
while (!operationsStack.empty()) {
auto operation = operationsStack.back();
operationsStack.pop_back();
if (operation->type() == TransformOp::Matrix) {
const auto matrixOperation =
std::static_pointer_cast<MatrixOperation>(operation);
if (std::holds_alternative<TransformOperations>(matrixOperation->value)) {
// If the current operation is a matrix created from other operations,
// add all of these operations to the stack
for (auto &op : std::get<TransformOperations>(matrixOperation->value)) {
operationsStack.push_back(op);
}
continue;
}
}
if (!operation->isRelative()) {
// If the operation is not relative, it can be simplified (converted to
// the matrix and multiplied)
const auto operationMatrix = operation->toMatrix();
simplifiedMatrix = hasSimplifications
? (simplifiedMatrix * operationMatrix)
: operationMatrix;
hasSimplifications = true;
} else {
// If the current operation is relative, we need to add the current
// simplified matrix to the list of operations before adding the
// relative operation
if (hasSimplifications) {
reversedOperations.emplace_back(
std::make_shared<MatrixOperation>(simplifiedMatrix));
simplifiedMatrix = TransformMatrix3D::Identity();
hasSimplifications = false;
}
reversedOperations.emplace_back(operation);
}
}
if (hasSimplifications) {
// We can return just a single matrix if there are no operations or the
// only operation is a simplified matrix (when hasSimplifications is true)
if (reversedOperations.size() <= 1) {
return simplifiedMatrix;
}
// Otherwise, add the last simplified matrix to the list of operations
reversedOperations.emplace_back(
std::make_shared<MatrixOperation>(simplifiedMatrix));
}
// Reverse the list of operations to maintain the order
std::reverse(reversedOperations.begin(), reversedOperations.end());
return reversedOperations;
}
MatrixOperation::MatrixOperation(const TransformMatrix3D &value)
: TransformOperationBase<
TransformOp::Matrix,
std::variant<TransformMatrix3D, TransformOperations>>(value) {}
MatrixOperation::MatrixOperation(const TransformOperations &operations)
// Simplify operations to reduce the number of matrix multiplications
// during matrix keyframe interpolation
: TransformOperationBase<
TransformOp::Matrix,
std::variant<TransformMatrix3D, TransformOperations>>(
simplifyOperations(operations)) {}
bool MatrixOperation::operator==(const TransformOperation &other) const {
if (type() != other.type()) {
return false;
}
const auto *otherOperation = dynamic_cast<const MatrixOperation *>(&other);
if (otherOperation == nullptr) {
return false;
}
const auto hasOperations = std::holds_alternative<TransformOperations>(value);
const auto otherHasOperations =
std::holds_alternative<TransformOperations>(otherOperation->value);
if (hasOperations != otherHasOperations) {
return false;
}
if (!hasOperations) {
return std::get<TransformMatrix3D>(value) ==
std::get<TransformMatrix3D>(otherOperation->value);
}
const auto &operations = std::get<TransformOperations>(value);
const auto &otherOperations =
std::get<TransformOperations>(otherOperation->value);
if (operations.size() != otherOperations.size()) {
return false;
}
for (size_t i = 0; i < operations.size(); ++i) {
if (*operations[i] != *otherOperations[i]) {
return false;
}
}
return true;
}
folly::dynamic MatrixOperation::valueToDynamic() const {
if (!std::holds_alternative<TransformMatrix3D>(value)) {
throw std::invalid_argument(
"[Reanimated] Cannot convert unprocessed transform operations to the dynamic value.");
}
return std::get<TransformMatrix3D>(value).toDynamic();
}
TransformMatrix3D MatrixOperation::toMatrix() const {
if (!std::holds_alternative<TransformMatrix3D>(value)) {
throw std::invalid_argument(
"[Reanimated] Cannot convert unprocessed transform operations to the matrix.");
}
return std::get<TransformMatrix3D>(value);
}
} // namespace reanimated::css
@@ -1,29 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/transforms/TransformOperation.h>
#include <memory>
#include <vector>
namespace reanimated::css {
// Matrix
struct MatrixOperation final
: public TransformOperationBase<
TransformOp::Matrix,
std::variant<TransformMatrix3D, TransformOperations>> {
using TransformOperationBase<
TransformOp::Matrix,
std::variant<TransformMatrix3D, TransformOperations>>::
TransformOperationBase;
explicit MatrixOperation(const TransformMatrix3D &value);
explicit MatrixOperation(const TransformOperations &operations);
bool operator==(const TransformOperation &other) const override;
folly::dynamic valueToDynamic() const override;
TransformMatrix3D toMatrix() const override;
};
} // namespace reanimated::css
@@ -1,25 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/transforms/TransformOperation.h>
namespace reanimated::css {
struct PerspectiveOperation final
: public TransformOperationBase<TransformOp::Perspective, CSSDouble> {
using TransformOperationBase<TransformOp::Perspective, CSSDouble>::
TransformOperationBase;
explicit PerspectiveOperation(double value)
: TransformOperationBase<TransformOp::Perspective, CSSDouble>(
CSSDouble(value)) {}
folly::dynamic valueToDynamic() const override {
return value.value != 0 ? value.toDynamic() : folly::dynamic();
}
TransformMatrix3D toMatrix() const override {
return TransformMatrix3D::create<TransformOp::Perspective>(value.value);
}
};
} // namespace reanimated::css
@@ -1,47 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/transforms/TransformOperation.h>
#include <memory>
#include <string>
namespace reanimated::css {
template <TransformOp TOperation>
struct RotateOperationBase
: public TransformOperationBase<TOperation, CSSAngle> {
using TransformOperationBase<TOperation, CSSAngle>::TransformOperationBase;
explicit RotateOperationBase(const std::string &value)
: TransformOperationBase<TOperation, CSSAngle>(CSSAngle(value)) {}
folly::dynamic valueToDynamic() const override {
return this->value.toDynamic();
}
TransformMatrix3D toMatrix() const override {
return TransformMatrix3D::create<TOperation>(this->value.value);
}
};
using RotateOperation = RotateOperationBase<TransformOp::Rotate>;
using RotateXOperation = RotateOperationBase<TransformOp::RotateX>;
using RotateYOperation = RotateOperationBase<TransformOp::RotateY>;
struct RotateZOperation final
: public RotateOperationBase<TransformOp::RotateZ> {
using RotateOperationBase<TransformOp::RotateZ>::RotateOperationBase;
bool canConvertTo(TransformOp type) const override {
return type == TransformOp::Rotate;
}
TransformOperations convertTo(TransformOp type) const override {
assertCanConvertTo(type);
return {std::make_shared<RotateOperation>(value)};
}
};
} // namespace reanimated::css
@@ -1,51 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/transforms/TransformOperation.h>
#include <memory>
namespace reanimated::css {
template <TransformOp TOperation>
struct ScaleOperationBase
: public TransformOperationBase<TOperation, CSSDouble> {
using TransformOperationBase<TOperation, CSSDouble>::TransformOperationBase;
explicit ScaleOperationBase(const double value)
: TransformOperationBase<TOperation, CSSDouble>(CSSDouble(value)) {}
folly::dynamic valueToDynamic() const override {
return this->value.toDynamic();
}
TransformMatrix3D toMatrix() const override {
return TransformMatrix3D::create<TOperation>(this->value.value);
}
};
using ScaleXOperation = ScaleOperationBase<TransformOp::ScaleX>;
using ScaleYOperation = ScaleOperationBase<TransformOp::ScaleY>;
struct ScaleOperation final : public ScaleOperationBase<TransformOp::Scale> {
using ScaleOperationBase<TransformOp::Scale>::ScaleOperationBase;
bool canConvertTo(TransformOp type) const override {
return type == TransformOp::ScaleX || type == TransformOp::ScaleY;
}
TransformOperations convertTo(TransformOp type) const override {
assertCanConvertTo(type);
if (type == TransformOp::ScaleX) {
return {
std::make_shared<ScaleXOperation>(value),
std::make_shared<ScaleYOperation>(value)};
} else {
return {
std::make_shared<ScaleYOperation>(value),
std::make_shared<ScaleXOperation>(value)};
}
}
};
} // namespace reanimated::css
@@ -1,29 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/transforms/TransformOperation.h>
#include <string>
namespace reanimated::css {
template <TransformOp TOperation>
struct SkewOperationBase : public TransformOperationBase<TOperation, CSSAngle> {
using TransformOperationBase<TOperation, CSSAngle>::TransformOperationBase;
explicit SkewOperationBase(const std::string &value)
: TransformOperationBase<TOperation, CSSAngle>(CSSAngle(value)) {}
folly::dynamic valueToDynamic() const override {
return this->value.toDynamic();
}
TransformMatrix3D toMatrix() const override {
return TransformMatrix3D::create<TOperation>(this->value.value);
}
};
using SkewXOperation = SkewOperationBase<TransformOp::SkewX>;
using SkewYOperation = SkewOperationBase<TransformOp::SkewY>;
} // namespace reanimated::css
@@ -1,40 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/transforms/TransformOperation.h>
#include <string>
namespace reanimated::css {
template <TransformOp TOperation>
struct TranslateOperationBase
: public TransformOperationBase<TOperation, CSSLength> {
using TransformOperationBase<TOperation, CSSLength>::TransformOperationBase;
explicit TranslateOperationBase(double value)
: TransformOperationBase<TOperation, CSSLength>(CSSLength(value)) {}
explicit TranslateOperationBase(const std::string &value)
: TransformOperationBase<TOperation, CSSLength>(CSSLength(value)) {}
bool isRelative() const override {
return this->value.isRelative;
}
folly::dynamic valueToDynamic() const override {
return this->value.toDynamic();
}
TransformMatrix3D toMatrix() const override {
if (this->value.isRelative) {
throw std::invalid_argument(
"[Reanimated] Cannot convert relative translate to the matrix.");
}
return TransformMatrix3D::create<TOperation>(this->value.value);
}
};
using TranslateXOperation = TranslateOperationBase<TransformOp::TranslateX>;
using TranslateYOperation = TranslateOperationBase<TransformOp::TranslateY>;
} // namespace reanimated::css
@@ -1,49 +0,0 @@
#include <reanimated/CSS/interpolation/values/ResolvableValueInterpolator.h>
#include <reanimated/CSS/common/values/CSSAngle.h>
#include <reanimated/CSS/common/values/CSSKeyword.h>
#include <reanimated/CSS/common/values/CSSLength.h>
#include <reanimated/CSS/common/values/CSSNumber.h>
#include <folly/dynamic.h>
#include <jsi/jsi.h>
namespace reanimated::css {
template <typename... AllowedTypes>
ResolvableValueInterpolator<AllowedTypes...>::ResolvableValueInterpolator(
const PropertyPath &propertyPath,
const ValueType &defaultStyleValue,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository,
RelativeTo relativeTo,
std::string relativeProperty)
: SimpleValueInterpolator<AllowedTypes...>(
propertyPath,
defaultStyleValue,
viewStylesRepository),
relativeTo_(relativeTo),
relativeProperty_(relativeProperty) {}
template <typename... AllowedTypes>
folly::dynamic ResolvableValueInterpolator<AllowedTypes...>::interpolateValue(
double progress,
const std::shared_ptr<CSSValue> &fromValue,
const std::shared_ptr<CSSValue> &toValue,
const ValueInterpolatorUpdateContext &context) const {
const auto &from = std::static_pointer_cast<ValueType>(fromValue);
const auto &to = std::static_pointer_cast<ValueType>(toValue);
return from
->interpolate(
progress,
*to,
{.node = context.node,
.viewStylesRepository = this->viewStylesRepository_,
.relativeProperty = relativeProperty_,
.relativeTo = relativeTo_})
.toDynamic();
}
template class ResolvableValueInterpolator<CSSLength>;
template class ResolvableValueInterpolator<CSSLength, CSSKeyword>;
} // namespace reanimated::css
@@ -1,49 +0,0 @@
#pragma once
#include <reanimated/CSS/common/values/CSSLength.h>
#include <reanimated/CSS/interpolation/values/SimpleValueInterpolator.h>
#include <memory>
#include <string>
#include <utility>
namespace reanimated::css {
/**
* Concrete implementation of ValueInterpolator for CSS values that require
* resolution before interpolation. This class handles interpolation of relative
* values (e.g., percentage length values) that need to be resolved to absolute
* values using the context describing the ShadowNode before the interpolation
* can occur.
*/
template <typename... AllowedTypes>
class ResolvableValueInterpolator final
: public SimpleValueInterpolator<AllowedTypes...> {
static_assert(
(... && std::is_base_of<CSSValue, AllowedTypes>::value),
"[Reanimated] ResolvableValueInterpolator: All interpolated types must inherit from CSSValue");
public:
using ValueType =
typename SimpleValueInterpolator<AllowedTypes...>::ValueType;
explicit ResolvableValueInterpolator(
const PropertyPath &propertyPath,
const ValueType &defaultStyleValue,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository,
RelativeTo relativeTo,
std::string relativeProperty);
protected:
folly::dynamic interpolateValue(
double progress,
const std::shared_ptr<CSSValue> &fromValue,
const std::shared_ptr<CSSValue> &toValue,
const ValueInterpolatorUpdateContext &context) const override;
private:
RelativeTo relativeTo_;
std::string relativeProperty_;
};
} // namespace reanimated::css
@@ -1,71 +0,0 @@
#include <reanimated/CSS/interpolation/values/SimpleValueInterpolator.h>
#include <folly/dynamic.h>
#include <jsi/jsi.h>
#include <reanimated/CSS/common/values/CSSAngle.h>
#include <reanimated/CSS/common/values/CSSBoolean.h>
#include <reanimated/CSS/common/values/CSSColor.h>
#include <reanimated/CSS/common/values/CSSDiscreteArray.h>
#include <reanimated/CSS/common/values/CSSKeyword.h>
#include <reanimated/CSS/common/values/CSSLength.h>
#include <reanimated/CSS/common/values/CSSNumber.h>
#include <reanimated/CSS/svg/values/SVGLength.h>
#include <reanimated/CSS/svg/values/SVGStrokeDashArray.h>
namespace reanimated::css {
template <typename... AllowedTypes>
SimpleValueInterpolator<AllowedTypes...>::SimpleValueInterpolator(
const PropertyPath &propertyPath,
const ValueType &defaultStyleValue,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
: ValueInterpolator(
propertyPath,
std::make_shared<ValueType>(defaultStyleValue),
viewStylesRepository) {}
template <typename... AllowedTypes>
std::shared_ptr<CSSValue> SimpleValueInterpolator<AllowedTypes...>::createValue(
jsi::Runtime &rt,
const jsi::Value &value) const {
return std::make_shared<ValueType>(rt, value);
}
template <typename... AllowedTypes>
std::shared_ptr<CSSValue> SimpleValueInterpolator<AllowedTypes...>::createValue(
const folly::dynamic &value) const {
return std::make_shared<ValueType>(value);
}
template <typename... AllowedTypes>
folly::dynamic SimpleValueInterpolator<AllowedTypes...>::interpolateValue(
double progress,
const std::shared_ptr<CSSValue> &fromValue,
const std::shared_ptr<CSSValue> &toValue,
const ValueInterpolatorUpdateContext &context) const {
const auto &from = std::static_pointer_cast<ValueType>(fromValue);
const auto &to = std::static_pointer_cast<ValueType>(toValue);
return from->interpolate(progress, *to, context).toDynamic();
}
template class SimpleValueInterpolator<CSSLength>;
template class SimpleValueInterpolator<CSSLength, CSSKeyword>;
template class SimpleValueInterpolator<CSSDouble>;
template class SimpleValueInterpolator<CSSDouble, CSSKeyword>;
template class SimpleValueInterpolator<CSSInteger>;
template class SimpleValueInterpolator<CSSAngle>;
template class SimpleValueInterpolator<CSSColor>;
template class SimpleValueInterpolator<CSSBoolean>;
template class SimpleValueInterpolator<CSSDisplay>;
template class SimpleValueInterpolator<CSSKeyword>;
template class SimpleValueInterpolator<CSSDiscreteArray<CSSKeyword>>;
#ifdef ANDROID
template class SimpleValueInterpolator<CSSShadowRadiusAndroid>;
#endif
template class SimpleValueInterpolator<SVGLength>;
template class SimpleValueInterpolator<SVGLength, CSSKeyword>;
template class SimpleValueInterpolator<SVGStrokeDashArray, CSSKeyword>;
} // namespace reanimated::css
@@ -1,43 +0,0 @@
#pragma once
#include <reanimated/CSS/interpolation/values/ValueInterpolator.h>
#include <memory>
namespace reanimated::css {
/**
* Concrete implementation of ValueInterpolator for simple CSS values that don't
* require resolution before interpolation. This class handles direct
* interpolation between values without any additional processing or resolution.
*/
template <typename... AllowedTypes>
class SimpleValueInterpolator : public ValueInterpolator {
static_assert(
(... && std::is_base_of<CSSValue, AllowedTypes>::value),
"[Reanimated] SimpleValueInterpolator: All interpolated types must inherit from CSSValue");
public:
using ValueType = CSSValueVariant<AllowedTypes...>;
explicit SimpleValueInterpolator(
const PropertyPath &propertyPath,
const ValueType &defaultStyleValue,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository);
protected:
std::shared_ptr<CSSValue> createValue(
jsi::Runtime &rt,
const jsi::Value &value) const override;
std::shared_ptr<CSSValue> createValue(
const folly::dynamic &value) const override;
folly::dynamic interpolateValue(
double progress,
const std::shared_ptr<CSSValue> &fromValue,
const std::shared_ptr<CSSValue> &toValue,
const ValueInterpolatorUpdateContext &context) const override;
};
} // namespace reanimated::css
@@ -1,153 +0,0 @@
#include <reanimated/CSS/interpolation/values/ValueInterpolator.h>
#include <utility>
namespace reanimated::css {
ValueInterpolator::ValueInterpolator(
const PropertyPath &propertyPath,
const std::shared_ptr<CSSValue> &defaultValue,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
: PropertyInterpolator(propertyPath, viewStylesRepository),
defaultStyleValue_(defaultValue),
defaultStyleValueDynamic_(defaultValue->toDynamic()) {}
folly::dynamic ValueInterpolator::getStyleValue(
const std::shared_ptr<const ShadowNode> &shadowNode) const {
return viewStylesRepository_->getStyleProp(
shadowNode->getTag(), propertyPath_);
}
folly::dynamic ValueInterpolator::getResetStyle(
const std::shared_ptr<const ShadowNode> &shadowNode) const {
auto styleValue = getStyleValue(shadowNode);
if (styleValue.isNull()) {
return defaultStyleValueDynamic_;
}
return styleValue;
}
folly::dynamic ValueInterpolator::getFirstKeyframeValue() const {
return convertOptionalToDynamic(keyframes_.front().value);
}
folly::dynamic ValueInterpolator::getLastKeyframeValue() const {
return convertOptionalToDynamic(keyframes_.back().value);
}
bool ValueInterpolator::equalsReversingAdjustedStartValue(
const folly::dynamic &propertyValue) const {
if (reversingAdjustedStartValue_.isNull()) {
return propertyValue.isNull();
}
return reversingAdjustedStartValue_ == propertyValue;
}
void ValueInterpolator::updateKeyframes(
jsi::Runtime &rt,
const jsi::Value &keyframes) {
const auto parsedKeyframes = parseJSIKeyframes(rt, keyframes);
keyframes_.clear();
keyframes_.reserve(parsedKeyframes.size());
for (const auto &[offset, value] : parsedKeyframes) {
if (value.isUndefined()) {
keyframes_.emplace_back(offset, std::nullopt);
} else {
keyframes_.emplace_back(
offset, std::make_optional(createValue(rt, value)));
}
}
}
void ValueInterpolator::updateKeyframesFromStyleChange(
const folly::dynamic &oldStyleValue,
const folly::dynamic &newStyleValue,
const folly::dynamic &lastUpdateValue) {
ValueKeyframe firstKeyframe, lastKeyframe;
if (!lastUpdateValue.isNull()) {
firstKeyframe = ValueKeyframe{0, createValue(lastUpdateValue)};
} else if (!oldStyleValue.isNull()) {
firstKeyframe = ValueKeyframe{0, createValue(oldStyleValue)};
} else {
firstKeyframe = ValueKeyframe{0, defaultStyleValue_};
}
if (newStyleValue.isNull()) {
lastKeyframe = ValueKeyframe{1, defaultStyleValue_};
} else {
lastKeyframe = ValueKeyframe{1, createValue(newStyleValue)};
}
keyframes_ = {std::move(firstKeyframe), std::move(lastKeyframe)};
reversingAdjustedStartValue_ = oldStyleValue;
}
folly::dynamic ValueInterpolator::interpolate(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::shared_ptr<KeyframeProgressProvider> &progressProvider) const {
const auto toIndex = getToKeyframeIndex(progressProvider);
const auto fromIndex = toIndex - 1;
const auto &fromKeyframe = keyframes_[fromIndex];
const auto &toKeyframe = keyframes_[toIndex];
// clang-format off
const auto &fromValue = fromKeyframe.value
? fromKeyframe.value.value()
: getFallbackValue(shadowNode);
const auto &toValue = toKeyframe.value
? toKeyframe.value.value()
: getFallbackValue(shadowNode);
// clang-format on
const auto keyframeProgress = progressProvider->getKeyframeProgress(
fromKeyframe.offset, toKeyframe.offset);
if (keyframeProgress == 1.0) {
return toValue->toDynamic();
}
if (keyframeProgress == 0.0) {
return fromValue->toDynamic();
}
return interpolateValue(
keyframeProgress, fromValue, toValue, {.node = shadowNode});
}
folly::dynamic ValueInterpolator::convertOptionalToDynamic(
const std::optional<std::shared_ptr<CSSValue>> &value) const {
return value ? value.value()->toDynamic() : folly::dynamic();
}
std::shared_ptr<CSSValue> ValueInterpolator::getFallbackValue(
const std::shared_ptr<const ShadowNode> &shadowNode) const {
const auto styleValue = getStyleValue(shadowNode);
return styleValue.isNull() ? defaultStyleValue_ : createValue(styleValue);
}
size_t ValueInterpolator::getToKeyframeIndex(
const std::shared_ptr<KeyframeProgressProvider> &progressProvider) const {
const auto progress = progressProvider->getGlobalProgress();
const auto it = std::upper_bound(
keyframes_.begin(),
keyframes_.end(),
progress,
[](double progress, const ValueKeyframe &keyframe) {
return progress < keyframe.offset;
});
// If we're at the end, return the last valid keyframe index
if (it == keyframes_.end()) {
return keyframes_.size() - 1;
}
return std::distance(keyframes_.begin(), it);
}
} // namespace reanimated::css
@@ -1,78 +0,0 @@
#pragma once
#include <reanimated/CSS/common/values/CSSValueVariant.h>
#include <reanimated/CSS/interpolation/PropertyInterpolator.h>
#include <reanimated/CSS/utils/keyframes.h>
#include <memory>
#include <optional>
#include <vector>
namespace reanimated::css {
struct ValueKeyframe {
double offset;
std::optional<std::shared_ptr<CSSValue>> value;
};
/**
* Base class for CSS value interpolators that provides common functionality
* for interpolating CSS values during animations. This class should be extended
* by concrete implementations for specific CSS value types to provide
* type-specific interpolation logic.
*/
class ValueInterpolator : public PropertyInterpolator {
public:
explicit ValueInterpolator(
const PropertyPath &propertyPath,
const std::shared_ptr<CSSValue> &defaultValue,
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository);
virtual ~ValueInterpolator() = default;
folly::dynamic getStyleValue(
const std::shared_ptr<const ShadowNode> &shadowNode) const override;
folly::dynamic getResetStyle(
const std::shared_ptr<const ShadowNode> &shadowNode) const override;
folly::dynamic getFirstKeyframeValue() const override;
folly::dynamic getLastKeyframeValue() const override;
bool equalsReversingAdjustedStartValue(
const folly::dynamic &propertyValue) const override;
void updateKeyframes(jsi::Runtime &rt, const jsi::Value &keyframes) override;
void updateKeyframesFromStyleChange(
const folly::dynamic &oldStyleValue,
const folly::dynamic &newStyleValue,
const folly::dynamic &lastUpdateValue) override;
folly::dynamic interpolate(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::shared_ptr<KeyframeProgressProvider> &progressProvider)
const override;
protected:
std::vector<ValueKeyframe> keyframes_;
std::shared_ptr<CSSValue> defaultStyleValue_;
folly::dynamic defaultStyleValueDynamic_;
folly::dynamic reversingAdjustedStartValue_;
virtual std::shared_ptr<CSSValue> createValue(
jsi::Runtime &rt,
const jsi::Value &value) const = 0;
virtual std::shared_ptr<CSSValue> createValue(
const folly::dynamic &value) const = 0;
virtual folly::dynamic interpolateValue(
double progress,
const std::shared_ptr<CSSValue> &fromValue,
const std::shared_ptr<CSSValue> &toValue,
const ValueInterpolatorUpdateContext &context) const = 0;
private:
folly::dynamic convertOptionalToDynamic(
const std::optional<std::shared_ptr<CSSValue>> &value) const;
std::shared_ptr<CSSValue> getFallbackValue(
const std::shared_ptr<const ShadowNode> &shadowNode) const;
size_t getToKeyframeIndex(
const std::shared_ptr<KeyframeProgressProvider> &progressProvider) const;
};
} // namespace reanimated::css
@@ -1,164 +0,0 @@
#include <reanimated/CSS/misc/ViewStylesRepository.h>
namespace reanimated::css {
ViewStylesRepository::ViewStylesRepository(
const std::shared_ptr<StaticPropsRegistry> &staticPropsRegistry,
const std::shared_ptr<AnimatedPropsRegistry> &animatedPropsRegistry)
: staticPropsRegistry_(staticPropsRegistry),
animatedPropsRegistry_(animatedPropsRegistry) {}
jsi::Value ViewStylesRepository::getNodeProp(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::string &propName) {
int tag = shadowNode->getTag();
auto &cachedNode = shadowNodeCache_[tag];
updateCacheIfNeeded(cachedNode, shadowNode);
if (propName == "width" || propName == "height" || propName == "top" ||
propName == "left") {
const auto &layoutMetrics = cachedNode.layoutMetrics;
if (propName == "width") {
return {layoutMetrics.frame.size.width};
} else if (propName == "height") {
return {layoutMetrics.frame.size.height};
} else if (propName == "top") {
return {layoutMetrics.frame.origin.y};
} else if (propName == "left") {
return {layoutMetrics.frame.origin.x};
}
} else {
const auto &viewProps = cachedNode.viewProps;
if (propName == "opacity") {
return {viewProps->opacity};
} else if (propName == "zIndex") {
return viewProps->zIndex.has_value() ? jsi::Value(*viewProps->zIndex)
: jsi::Value(0);
} else if (propName == "backgroundColor") {
return {static_cast<int32_t>(*viewProps->backgroundColor)};
}
}
throw std::runtime_error("[Reanimated] Unsupported property: " + propName);
}
jsi::Value ViewStylesRepository::getParentNodeProp(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::string &propName) {
const auto surfaceId = shadowNode->getSurfaceId();
const auto &shadowTreeRegistry = uiManager_->getShadowTreeRegistry();
std::shared_ptr<const ShadowNode> parentNode = nullptr;
shadowTreeRegistry.visit(surfaceId, [&](ShadowTree const &shadowTree) {
auto currentRevision = shadowTree.getCurrentRevision();
parentNode =
dom::getParentNode(currentRevision.rootShadowNode, *shadowNode);
});
if (!parentNode) {
return jsi::Value::undefined();
}
return getNodeProp(parentNode, propName);
}
folly::dynamic ViewStylesRepository::getStyleProp(
const Tag tag,
const PropertyPath &propertyPath) {
auto animatedValue =
getPropertyValue(animatedPropsRegistry_->get(tag), propertyPath);
if (!animatedValue.isNull()) {
return animatedValue;
}
return getPropertyValue(staticPropsRegistry_->get(tag), propertyPath);
}
void ViewStylesRepository::clearNodesCache() {
shadowNodeCache_.clear();
}
void ViewStylesRepository::updateCacheIfNeeded(
CachedShadowNode &cachedNode,
const std::shared_ptr<const ShadowNode> &shadowNode) {
auto newestCloneOfShadowNode =
uiManager_->getNewestCloneOfShadowNode(*shadowNode);
// Check if newestCloneOfShadowNode is valid (is already mounted / not
// yet unmounted)
if (!newestCloneOfShadowNode) {
return;
}
auto layoutableShadowNode =
dynamic_cast<const LayoutableShadowNode *>(newestCloneOfShadowNode.get());
if (!layoutableShadowNode) {
return;
}
cachedNode.layoutMetrics = layoutableShadowNode->layoutMetrics_;
cachedNode.viewProps = std::static_pointer_cast<const ViewProps>(
newestCloneOfShadowNode->getProps());
}
folly::dynamic ViewStylesRepository::getPropertyValue(
const folly::dynamic &value,
const PropertyPath &propertyPath) {
const folly::dynamic *currentValue = &value;
for (size_t i = 0; i < propertyPath.size(); ++i) {
if (currentValue->isNull() || currentValue->empty()) {
return {};
}
const auto &propName = propertyPath[i];
if (!currentValue->isObject()) {
return {};
}
if (propName == "transform") {
auto transformIt = currentValue->find("transform");
if (transformIt == currentValue->items().end()) {
return {};
}
const auto &transform = transformIt->second;
if (!transform.isArray()) {
return {};
}
if (i + 1 >= propertyPath.size()) {
return transform;
}
const std::string &transformPropName = propertyPath[i + 1];
for (const auto &transformEntry : transform) {
if (transformEntry.isObject()) {
auto transformPropIt = transformEntry.find(transformPropName);
if (transformPropIt != transformEntry.items().end()) {
return transformPropIt->second;
}
}
}
return {};
}
auto propIt = currentValue->find(propName);
if (propIt == currentValue->items().end()) {
return {};
}
currentValue = &propIt->second;
}
return *currentValue;
}
} // namespace reanimated::css
@@ -1,61 +0,0 @@
#pragma once
#include <reanimated/CSS/common/definitions.h>
#include <reanimated/CSS/registries/StaticPropsRegistry.h>
#include <reanimated/Fabric/updates/AnimatedPropsRegistry.h>
#include <react/renderer/components/view/ViewProps.h>
#include <react/renderer/core/LayoutableShadowNode.h>
#include <react/renderer/dom/DOM.h>
#include <memory>
#include <string>
#include <unordered_map>
namespace reanimated::css {
using namespace facebook;
using namespace react;
struct CachedShadowNode {
LayoutMetrics layoutMetrics;
std::shared_ptr<const ViewProps> viewProps;
};
class ViewStylesRepository {
public:
ViewStylesRepository(
const std::shared_ptr<StaticPropsRegistry> &staticPropsRegistry,
const std::shared_ptr<AnimatedPropsRegistry> &animatedPropsRegistry);
void setUIManager(const std::shared_ptr<UIManager> &uiManager) {
uiManager_ = uiManager;
}
jsi::Value getNodeProp(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::string &propName);
jsi::Value getParentNodeProp(
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::string &propName);
folly::dynamic getStyleProp(Tag tag, const PropertyPath &propertyPath);
void clearNodesCache();
private:
std::shared_ptr<UIManager> uiManager_;
std::shared_ptr<StaticPropsRegistry> staticPropsRegistry_;
std::shared_ptr<AnimatedPropsRegistry> animatedPropsRegistry_;
std::unordered_map<int, CachedShadowNode> shadowNodeCache_;
void updateCacheIfNeeded(
CachedShadowNode &cachedNode,
const std::shared_ptr<const ShadowNode> &shadowNode);
static folly::dynamic getPropertyValue(
const folly::dynamic &value,
const PropertyPath &propertyPath);
};
} // namespace reanimated::css
@@ -1,194 +0,0 @@
#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
@@ -1,68 +0,0 @@
#pragma once
#include <reanimated/CSS/configs/CSSAnimationConfig.h>
#include <reanimated/CSS/configs/CSSKeyframesConfig.h>
#include <reanimated/CSS/easing/EasingFunctions.h>
#include <reanimated/CSS/progress/KeyframeProgressProvider.h>
#include <reanimated/CSS/progress/RawProgressProvider.h>
#include <memory>
namespace reanimated::css {
enum class AnimationProgressState {
Pending, // When the animation is waiting for the delay to pass
Running,
Paused,
Finished
};
class AnimationProgressProvider final : public KeyframeProgressProvider,
public RawProgressProvider {
public:
AnimationProgressProvider(
double timestamp,
double duration,
double delay,
double iterationCount,
AnimationDirection direction,
EasingFunction easingFunction,
const std::shared_ptr<KeyframeEasingFunctions> &keyframeEasingFunctions);
void setIterationCount(double iterationCount);
void setDirection(AnimationDirection direction);
void setEasingFunction(const EasingFunction &easingFunction);
AnimationDirection getDirection() const;
double getGlobalProgress() const override;
double getKeyframeProgress(double fromOffset, double toOffset) const override;
AnimationProgressState getState(double timestamp) const;
double getPauseTimestamp() const;
double getTotalPausedTime(double timestamp) const;
double getStartTimestamp(double timestamp) const;
void pause(double timestamp);
void play(double timestamp);
void resetProgress() override;
protected:
std::optional<double> calculateRawProgress(double timestamp) override;
private:
double iterationCount_;
AnimationDirection direction_;
EasingFunction easingFunction_;
std::shared_ptr<KeyframeEasingFunctions> keyframeEasingFunctions_;
unsigned currentIteration_ = 1;
double previousIterationsDuration_ = 0;
double pauseTimestamp_ = 0;
double totalPausedTime_ = 0;
bool shouldFinish(double timestamp) const;
double updateIterationProgress(double currentIterationElapsedTime);
double applyAnimationDirection(double iterationProgress) const;
};
} // namespace reanimated::css
@@ -1,13 +0,0 @@
#pragma once
namespace reanimated::css {
class KeyframeProgressProvider {
public:
virtual double getGlobalProgress() const = 0;
virtual double getKeyframeProgress(double fromOffset, double toOffset)
const = 0;
};
} // namespace reanimated::css
@@ -1,44 +0,0 @@
#include <reanimated/CSS/progress/RawProgressProvider.h>
namespace reanimated::css {
RawProgressProvider::RawProgressProvider(
const double timestamp,
const double duration,
const double delay)
: duration_(duration), delay_(delay), creationTimestamp_(timestamp) {}
void RawProgressProvider::setDuration(double duration) {
duration_ = duration;
}
void RawProgressProvider::setDelay(double delay) {
delay_ = delay;
}
void RawProgressProvider::resetProgress() {
rawProgress_.reset();
previousRawProgress_.reset();
}
void RawProgressProvider::update(const double timestamp) {
previousRawProgress_ = rawProgress_;
if (timestamp - creationTimestamp_ < delay_) {
rawProgress_.reset();
return;
}
rawProgress_ = calculateRawProgress(timestamp);
if (!rawProgress_.has_value()) {
return;
}
if (rawProgress_.value() < 0) {
rawProgress_.reset();
} else if (rawProgress_.value() >= 1) {
rawProgress_ = 1;
}
}
} // namespace reanimated::css
@@ -1,31 +0,0 @@
#pragma once
#include <optional>
namespace reanimated::css {
class RawProgressProvider {
public:
RawProgressProvider(double timestamp, double duration, double delay);
void setDuration(double duration);
void setDelay(double delay);
virtual void resetProgress();
void update(double timestamp);
protected:
double duration_;
double delay_;
double creationTimestamp_;
std::optional<double> rawProgress_;
std::optional<double> previousRawProgress_;
/**
* Calculates the progress of the animation at the given timestamp without
* applying any decorations (e.g. animation direction, easing)
*/
virtual std::optional<double> calculateRawProgress(double timestamp) = 0;
};
} // namespace reanimated::css
@@ -1,220 +0,0 @@
#include <reanimated/CSS/progress/TransitionProgressProvider.h>
#include <limits>
namespace reanimated::css {
// TransitionPropertyProgressProvider
TransitionPropertyProgressProvider::TransitionPropertyProgressProvider(
const double timestamp,
const double duration,
const double delay,
const EasingFunction &easingFunction)
: RawProgressProvider(timestamp, duration, delay),
easingFunction_(easingFunction) {}
TransitionPropertyProgressProvider::TransitionPropertyProgressProvider(
const double timestamp,
const double duration,
const double delay,
const EasingFunction &easingFunction,
const double reversingShorteningFactor)
: RawProgressProvider(timestamp, duration, delay),
easingFunction_(easingFunction),
reversingShorteningFactor_(reversingShorteningFactor) {}
double TransitionPropertyProgressProvider::getGlobalProgress() const {
return rawProgress_.value_or(0);
}
double TransitionPropertyProgressProvider::getKeyframeProgress(
const double fromOffset,
const double toOffset) const {
if (fromOffset == toOffset) {
return 1;
}
return easingFunction_(getGlobalProgress());
}
double TransitionPropertyProgressProvider::getRemainingDelay(
const double timestamp) const {
return delay_ - (timestamp - creationTimestamp_);
}
double TransitionPropertyProgressProvider::getReversingShorteningFactor()
const {
return reversingShorteningFactor_;
}
TransitionProgressState TransitionPropertyProgressProvider::getState() const {
if (!rawProgress_.has_value()) {
return TransitionProgressState::Pending;
}
const auto rawProgress = rawProgress_.value();
if (rawProgress >= 1) {
return TransitionProgressState::Finished;
}
return TransitionProgressState::Running;
}
std::optional<double> TransitionPropertyProgressProvider::calculateRawProgress(
const double timestamp) {
if (duration_ == 0) {
return 1;
}
return getElapsedTime(timestamp) / duration_;
}
double TransitionPropertyProgressProvider::getElapsedTime(
const double timestamp) const {
return timestamp - (creationTimestamp_ + delay_);
}
// TransitionProgressProvider
TransitionProgressState TransitionProgressProvider::getState() const {
for (const auto &[_, propertyProgressProvider] : propertyProgressProviders_) {
if (propertyProgressProvider->getState() ==
TransitionProgressState::Running) {
return TransitionProgressState::Running;
}
}
return TransitionProgressState::Pending;
}
double TransitionProgressProvider::getMinDelay(const double timestamp) const {
if (propertyProgressProviders_.empty()) {
return 0;
}
auto minDelay = std::numeric_limits<double>::max();
for (const auto &[_, propertyProgressProvider] : propertyProgressProviders_) {
const auto remainingDelay =
propertyProgressProvider->getRemainingDelay(timestamp);
if (remainingDelay < minDelay) {
minDelay = remainingDelay;
}
}
return minDelay;
}
TransitionPropertyProgressProviders
TransitionProgressProvider::getPropertyProgressProviders() const {
return propertyProgressProviders_;
}
std::unordered_set<std::string>
TransitionProgressProvider::getRemovedProperties() const {
return removedProperties_;
}
void TransitionProgressProvider::discardFinishedProgressProviders() {
for (auto it = propertyProgressProviders_.begin();
it != propertyProgressProviders_.end();) {
if (it->second->getState() == TransitionProgressState::Finished) {
it = propertyProgressProviders_.erase(it);
} else {
++it;
}
}
}
void TransitionProgressProvider::discardIrrelevantProgressProviders(
const std::unordered_set<std::string> &transitionPropertyNames) {
for (auto it = propertyProgressProviders_.begin();
it != propertyProgressProviders_.end();) {
// Remove property progress providers for properties not specified in the
// transition property names
if (transitionPropertyNames.find(it->first) ==
transitionPropertyNames.end()) {
it = propertyProgressProviders_.erase(it);
} else {
++it;
}
}
}
void TransitionProgressProvider::runProgressProviders(
const double timestamp,
const CSSTransitionPropertiesSettings &propertiesSettings,
const PropertyNames &changedPropertyNames,
const std::unordered_set<std::string> &reversedPropertyNames) {
for (const auto &propertyName : changedPropertyNames) {
const auto propertySettingsOptional =
getTransitionPropertySettings(propertiesSettings, propertyName);
if (!propertySettingsOptional.has_value()) {
throw std::invalid_argument(
"[Reanimated] Property '" + propertyName +
"' is not a valid transition property");
}
const auto &propertySettings = propertySettingsOptional.value();
const auto it = propertyProgressProviders_.find(propertyName);
if (it != propertyProgressProviders_.end()) {
const auto &progressProvider = it->second;
progressProvider->update(timestamp);
if (reversedPropertyNames.find(propertyName) !=
reversedPropertyNames.end() &&
progressProvider->getState() != TransitionProgressState::Finished) {
// Create reversing shortening progress provider for interrupted
// reversing transition
propertyProgressProviders_.insert_or_assign(
propertyName,
createReversingShorteningProgressProvider(
timestamp, propertySettings, *progressProvider));
continue;
}
}
// Create progress provider with the new settings
propertyProgressProviders_.insert_or_assign(
propertyName,
std::make_shared<TransitionPropertyProgressProvider>(
timestamp,
propertySettings.duration,
propertySettings.delay,
propertySettings.easingFunction));
}
}
void TransitionProgressProvider::update(const double timestamp) {
removedProperties_.clear();
for (const auto &[propertyName, propertyProgressProvider] :
propertyProgressProviders_) {
propertyProgressProvider->update(timestamp);
if (propertyProgressProvider->getState() ==
TransitionProgressState::Finished) {
removedProperties_.insert(propertyName);
}
}
}
std::shared_ptr<TransitionPropertyProgressProvider>
TransitionProgressProvider::createReversingShorteningProgressProvider(
const double timestamp,
const CSSTransitionPropertySettings &propertySettings,
const TransitionPropertyProgressProvider &existingProgressProvider) {
const auto oldProgress = existingProgressProvider.getKeyframeProgress(0, 1);
const auto oldReversingShorteningFactor =
existingProgressProvider.getReversingShorteningFactor();
auto newReversingShorteningFactor =
oldProgress * oldReversingShorteningFactor +
(1 - oldReversingShorteningFactor);
return std::make_shared<TransitionPropertyProgressProvider>(
timestamp,
propertySettings.duration * newReversingShorteningFactor,
propertySettings.delay < 0
? newReversingShorteningFactor * propertySettings.delay
: propertySettings.delay,
propertySettings.easingFunction,
newReversingShorteningFactor);
}
} // namespace reanimated::css
@@ -1,82 +0,0 @@
#pragma once
#include <reanimated/CSS/configs/CSSTransitionConfig.h>
#include <reanimated/CSS/progress/KeyframeProgressProvider.h>
#include <reanimated/CSS/progress/RawProgressProvider.h>
#include <reanimated/CSS/utils/props.h>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
namespace reanimated::css {
enum class TransitionProgressState { Pending, Running, Finished };
class TransitionPropertyProgressProvider final
: public KeyframeProgressProvider,
public RawProgressProvider {
public:
TransitionPropertyProgressProvider(
double timestamp,
double duration,
double delay,
const EasingFunction &easingFunction);
TransitionPropertyProgressProvider(
double timestamp,
double duration,
double delay,
const EasingFunction &easingFunction,
double reversingShorteningFactor);
double getGlobalProgress() const override;
double getKeyframeProgress(double fromOffset, double toOffset) const override;
double getRemainingDelay(double timestamp) const;
double getReversingShorteningFactor() const;
TransitionProgressState getState() const;
protected:
std::optional<double> calculateRawProgress(double timestamp) override;
private:
EasingFunction easingFunction_;
double reversingShorteningFactor_ = 1;
double getElapsedTime(double timestamp) const;
};
using TransitionPropertyProgressProviders = std::unordered_map<
std::string,
std::shared_ptr<TransitionPropertyProgressProvider>>;
class TransitionProgressProvider final {
public:
TransitionProgressState getState() const;
double getMinDelay(double timestamp) const;
TransitionPropertyProgressProviders getPropertyProgressProviders() const;
std::unordered_set<std::string> getRemovedProperties() const;
void discardFinishedProgressProviders();
void discardIrrelevantProgressProviders(
const std::unordered_set<std::string> &transitionPropertyNames);
void runProgressProviders(
double timestamp,
const CSSTransitionPropertiesSettings &propertiesSettings,
const PropertyNames &changedPropertyNames,
const std::unordered_set<std::string> &reversedPropertyNames);
void update(double timestamp);
private:
TransitionPropertyProgressProviders propertyProgressProviders_;
std::unordered_set<std::string> removedProperties_;
std::shared_ptr<TransitionPropertyProgressProvider>
createReversingShorteningProgressProvider(
double timestamp,
const CSSTransitionPropertySettings &propertySettings,
const TransitionPropertyProgressProvider &existingProgressProvider);
};
} // namespace reanimated::css
@@ -1,349 +0,0 @@
#include <reanimated/CSS/registries/CSSAnimationsRegistry.h>
namespace reanimated::css {
bool CSSAnimationsRegistry::isEmpty() const {
// The registry is empty if has no registered animations and no updates
// stored in the updates registry
return UpdatesRegistry::isEmpty() && registry_.empty();
}
bool CSSAnimationsRegistry::hasUpdates() const {
return !runningAnimationIndicesMap_.empty() ||
!delayedAnimationsManager_.empty() || !animationsToRevertMap_.empty();
}
void CSSAnimationsRegistry::apply(
jsi::Runtime &rt,
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::optional<std::vector<std::string>> &animationNames,
const CSSAnimationsMap &newAnimations,
const CSSAnimationSettingsUpdatesMap &settingsUpdates,
double timestamp) {
const auto animationsVector =
buildAnimationsVector(rt, shadowNode, animationNames, newAnimations);
const auto viewTag = shadowNode->getTag();
if (animationsVector.empty()) {
remove(viewTag);
return;
}
registry_.erase(viewTag);
registry_.emplace(
viewTag,
RegistryEntry{
std::move(animationsVector),
buildAnimationToIndexMap(animationsVector)});
runningAnimationIndicesMap_[viewTag].clear();
std::vector<size_t> updatedIndices;
for (const auto &[index, _] : newAnimations) {
updatedIndices.push_back(index);
}
for (const auto &[index, _] : settingsUpdates) {
updatedIndices.push_back(index);
}
updateAnimationSettings(animationsVector, settingsUpdates, timestamp);
for (size_t i = 0; i < animationsVector.size(); ++i) {
scheduleOrActivateAnimation(i, animationsVector[i], timestamp);
}
updateViewAnimations(viewTag, updatedIndices, timestamp, false);
applyViewAnimationsStyle(viewTag, timestamp);
}
void CSSAnimationsRegistry::remove(const Tag viewTag) {
removeViewAnimations(viewTag);
removeFromUpdatesRegistry(viewTag);
registry_.erase(viewTag);
}
void CSSAnimationsRegistry::update(const double timestamp) {
// Activate all delayed animations that should start now
activateDelayedAnimations(timestamp);
// Update styles in the registry for views which animations were reverted
handleAnimationsToRevert(timestamp);
// Iterate over active animations and update them
for (auto it = runningAnimationIndicesMap_.begin();
it != runningAnimationIndicesMap_.end();) {
const auto viewTag = it->first;
const std::vector<size_t> animationIndices = {
it->second.begin(), it->second.end()};
updateViewAnimations(viewTag, animationIndices, timestamp, true);
if (runningAnimationIndicesMap_[viewTag].empty()) {
it = runningAnimationIndicesMap_.erase(it);
} else {
++it;
}
}
}
CSSAnimationsVector CSSAnimationsRegistry::buildAnimationsVector(
jsi::Runtime &rt,
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::optional<std::vector<std::string>> &animationNames,
const std::optional<CSSAnimationsMap> &newAnimations) const {
const auto registryIt = registry_.find(shadowNode->getTag());
// If animationNames has no value, that means no animations were added,
// removed or reordered, so we can return the current animations vector from
// the registry
if (!animationNames.has_value()) {
if (registryIt != registry_.end()) {
return std::move(registryIt->second.animationsVector);
}
}
CSSAnimationsVector animationsVector;
const auto &animationNamesVector = animationNames.value();
const auto animationNamesSize = animationNamesVector.size();
animationsVector.reserve(animationNamesSize);
std::unordered_map<std::string, CSSAnimationsVector> oldAnimationsMap;
CSSAnimationsMap emptyAnimationsMap;
const auto &newAnimationsMap = newAnimations.value_or(emptyAnimationsMap);
if (registryIt != registry_.end()) {
const auto &oldAnimations = registryIt->second.animationsVector;
// Fill the map while maintaining reverse order (for quick pop from the end)
for (auto it = oldAnimations.rbegin(); it != oldAnimations.rend(); ++it) {
oldAnimationsMap[(*it)->getName()].emplace_back(*it);
}
}
for (size_t i = 0; i < animationNamesSize; ++i) {
const auto &newAnimationIt = newAnimationsMap.find(i);
if (newAnimationIt != newAnimationsMap.end()) {
animationsVector.emplace_back(newAnimationIt->second);
continue;
}
const auto &name = animationNamesVector[i];
const auto &oldAnimationIt = oldAnimationsMap.find(name);
if (oldAnimationIt == oldAnimationsMap.end()) {
throw std::runtime_error(
"[Reanimated] There is no animation with name " + name +
" available to use at index " + std::to_string(i));
}
animationsVector.emplace_back(oldAnimationIt->second.back());
oldAnimationIt->second.pop_back();
if (oldAnimationIt->second.empty()) {
oldAnimationsMap.erase(oldAnimationIt);
}
}
return animationsVector;
}
CSSAnimationsRegistry::AnimationToIndexMap
CSSAnimationsRegistry::buildAnimationToIndexMap(
const CSSAnimationsVector &animationsVector) const {
AnimationToIndexMap animationToIndexMap;
for (size_t i = 0; i < animationsVector.size(); ++i) {
animationToIndexMap[animationsVector[i]] = i;
}
return animationToIndexMap;
}
void CSSAnimationsRegistry::updateAnimationSettings(
const CSSAnimationsVector &animationsVector,
const CSSAnimationSettingsUpdatesMap &settingsUpdates,
const double timestamp) {
for (size_t i = 0; i < animationsVector.size(); ++i) {
const auto &animation = animationsVector[i];
const auto it = settingsUpdates.find(i);
if (it != settingsUpdates.end()) {
animation->updateSettings(it->second, timestamp);
}
}
}
void CSSAnimationsRegistry::updateViewAnimations(
const Tag viewTag,
const std::vector<size_t> &animationIndices,
const double timestamp,
const bool addToBatch) {
folly::dynamic result = folly::dynamic::object;
std::shared_ptr<const ShadowNode> shadowNode = nullptr;
bool hasUpdates = false;
for (const auto animationIndex : animationIndices) {
const auto &animation = registry_[viewTag].animationsVector[animationIndex];
if (!shadowNode) {
shadowNode = animation->getShadowNode();
}
if (animation->getState(timestamp) == AnimationProgressState::Pending) {
animation->run(timestamp);
}
bool updatesAddedToBatch = false;
const auto updates = animation->update(timestamp);
const auto newState = animation->getState(timestamp);
if (newState == AnimationProgressState::Finished) {
// Revert changes applied during animation if there is no forwards fill
// mode
if (addToBatch && !animation->hasForwardsFillMode()) {
// We also have to manually commit style values
// reverting the changes applied by the animation.
hasUpdates =
addStyleUpdates(result, animation->getResetStyle(), false) ||
hasUpdates;
updatesAddedToBatch = true;
// We want to remove style changes applied by the animation that is
// finished and has no forwards fill mode. We cannot simply remove
// properties from the style in the registry as it may be overridden
// by the next animation. Instead, we are creating the new style
// object without reverted (finished without forwards fill mode)
// animations.
animationsToRevertMap_[viewTag].insert(animationIndex);
}
}
if (addToBatch && !updatesAddedToBatch) {
hasUpdates = addStyleUpdates(result, updates, true) || hasUpdates;
}
if (newState != AnimationProgressState::Running) {
runningAnimationIndicesMap_[viewTag].erase(animationIndex);
}
}
if (hasUpdates) {
addUpdatesToBatch(shadowNode, result);
}
}
void CSSAnimationsRegistry::scheduleOrActivateAnimation(
const size_t animationIndex,
const std::shared_ptr<CSSAnimation> &animation,
const double timestamp) {
// Remove the animation from delayed (if it is already added to
// delayed animations)
delayedAnimationsManager_.remove(animation);
const auto startTimestamp = animation->getStartTimestamp(timestamp);
if (startTimestamp > timestamp) {
// If the animation is delayed, schedule it for activation
// (Only if it isn't paused)
if (animation->getState(timestamp) != AnimationProgressState::Paused) {
delayedAnimationsManager_.add(startTimestamp, animation);
}
} else {
const auto viewTag = animation->getShadowNode()->getTag();
runningAnimationIndicesMap_[viewTag].insert(animationIndex);
}
}
void CSSAnimationsRegistry::removeViewAnimations(const Tag viewTag) {
const auto it = registry_.find(viewTag);
if (it == registry_.end()) {
return;
}
for (const auto &animation : it->second.animationsVector) {
delayedAnimationsManager_.remove(animation);
}
runningAnimationIndicesMap_.erase(viewTag);
}
void CSSAnimationsRegistry::applyViewAnimationsStyle(
const Tag viewTag,
const double timestamp) {
const auto it = registry_.find(viewTag);
// Remove the style from the registry if there are no animations for the view
if (it == registry_.end() || it->second.animationsVector.empty()) {
removeFromUpdatesRegistry(viewTag);
return;
}
folly::dynamic updatedStyle = folly::dynamic::object;
std::shared_ptr<const ShadowNode> shadowNode = nullptr;
for (const auto &animation : it->second.animationsVector) {
const auto startTimestamp = animation->getStartTimestamp(timestamp);
folly::dynamic style;
const auto &currentState = animation->getState(timestamp);
if (startTimestamp > timestamp && animation->hasBackwardsFillMode()) {
style = animation->getBackwardsFillStyle();
} else if (
currentState == AnimationProgressState::Running ||
// Animation is paused after start (was running before)
(currentState == AnimationProgressState::Paused &&
timestamp >= animation->getStartTimestamp(timestamp)) ||
// Animation is finished and has fill forwards fill mode
(currentState == AnimationProgressState::Finished &&
animation->hasForwardsFillMode())) {
style = animation->getCurrentInterpolationStyle();
}
if (!shadowNode) {
shadowNode = animation->getShadowNode();
}
if (style.isObject()) {
updatedStyle.update(style);
}
}
setInUpdatesRegistry(shadowNode, updatedStyle);
}
void CSSAnimationsRegistry::activateDelayedAnimations(const double timestamp) {
while (!delayedAnimationsManager_.empty() &&
delayedAnimationsManager_.top().timestamp <= timestamp) {
const auto [_, animation] = delayedAnimationsManager_.pop();
const auto viewTag = animation->getShadowNode()->getTag();
// Add only these animations which weren't removed in the meantime
if (registry_.find(viewTag) == registry_.end()) {
continue;
}
const auto &animationToIndexMap = registry_[viewTag].animationToIndexMap;
if (animationToIndexMap.find(animation) == animationToIndexMap.end()) {
continue;
}
const auto animationIndex = animationToIndexMap.at(animation);
runningAnimationIndicesMap_[viewTag].insert(animationIndex);
}
}
void CSSAnimationsRegistry::handleAnimationsToRevert(const double timestamp) {
for (const auto &[viewTag, _] : animationsToRevertMap_) {
applyViewAnimationsStyle(viewTag, timestamp);
}
animationsToRevertMap_.clear();
}
bool CSSAnimationsRegistry::addStyleUpdates(
folly::dynamic &target,
const folly::dynamic &updates,
bool shouldOverride) {
if (!updates.isObject()) {
return false;
}
bool hasUpdates = false;
for (const auto &[propertyName, propertyValue] : updates.items()) {
if (shouldOverride || !target.count(propertyName) ||
target[propertyName].isNull()) {
target[propertyName] = propertyValue;
hasUpdates = true;
}
}
return hasUpdates;
}
} // namespace reanimated::css
@@ -1,95 +0,0 @@
#pragma once
#include <reanimated/CSS/configs/CSSAnimationConfig.h>
#include <reanimated/CSS/core/CSSAnimation.h>
#include <reanimated/CSS/utils/DelayedItemsManager.h>
#include <reanimated/CSS/utils/props.h>
#include <reanimated/Fabric/updates/UpdatesRegistry.h>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
namespace reanimated::css {
using CSSAnimationsMap =
std::unordered_map<size_t, std::shared_ptr<CSSAnimation>>;
using CSSAnimationsVector = std::vector<std::shared_ptr<CSSAnimation>>;
class CSSAnimationsRegistry
: public UpdatesRegistry,
std::enable_shared_from_this<CSSAnimationsRegistry> {
public:
using SettingsUpdates =
std::vector<std::pair<size_t, PartialCSSAnimationSettings>>;
bool isEmpty() const override;
bool hasUpdates() const;
void apply(
jsi::Runtime &rt,
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::optional<std::vector<std::string>> &animationNames,
const CSSAnimationsMap &newAnimations,
const CSSAnimationSettingsUpdatesMap &settingsUpdates,
double timestamp);
void remove(Tag viewTag) override;
void update(double timestamp);
private:
using AnimationToIndexMap =
std::unordered_map<std::shared_ptr<CSSAnimation>, size_t>;
using RunningAnimationIndicesMap = std::unordered_map<Tag, std::set<size_t>>;
using AnimationsToRevertMap =
std::unordered_map<Tag, std::unordered_set<size_t>>;
struct RegistryEntry {
const CSSAnimationsVector animationsVector;
const AnimationToIndexMap animationToIndexMap;
};
using Registry = std::unordered_map<Tag, RegistryEntry>;
Registry registry_;
RunningAnimationIndicesMap runningAnimationIndicesMap_;
AnimationsToRevertMap animationsToRevertMap_;
DelayedItemsManager<std::shared_ptr<CSSAnimation>> delayedAnimationsManager_;
CSSAnimationsVector buildAnimationsVector(
jsi::Runtime &rt,
const std::shared_ptr<const ShadowNode> &shadowNode,
const std::optional<std::vector<std::string>> &animationNames,
const std::optional<CSSAnimationsMap> &newAnimations) const;
AnimationToIndexMap buildAnimationToIndexMap(
const CSSAnimationsVector &animationsVector) const;
void updateAnimationSettings(
const CSSAnimationsVector &animationsVector,
const CSSAnimationSettingsUpdatesMap &settingsUpdates,
double timestamp);
void updateViewAnimations(
Tag viewTag,
const std::vector<size_t> &animationIndices,
double timestamp,
bool addToBatch);
void scheduleOrActivateAnimation(
size_t animationIndex,
const std::shared_ptr<CSSAnimation> &animation,
double timestamp);
void removeViewAnimations(Tag viewTag);
void applyViewAnimationsStyle(Tag viewTag, double timestamp);
void activateDelayedAnimations(double timestamp);
void handleAnimationsToRevert(double timestamp);
static bool addStyleUpdates(
folly::dynamic &target,
const folly::dynamic &updates,
bool shouldOverride);
};
} // namespace reanimated::css
@@ -1,47 +0,0 @@
#include <reanimated/CSS/registries/CSSKeyframesRegistry.h>
namespace reanimated::css {
CSSKeyframesRegistry::CSSKeyframesRegistry(
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository)
: viewStylesRepository_(viewStylesRepository) {}
const CSSKeyframesConfig &CSSKeyframesRegistry::get(
const std::string &animationName,
const std::string &componentName) {
const auto &registryIt = registry_.find(animationName);
if (registryIt == registry_.end()) {
throw std::runtime_error(
"[Reanimated] No keyframes with name `" + animationName +
"` were registered");
}
const auto &keyframesByComponentName = registryIt->second;
const auto &keyframesByComponentNameIt =
keyframesByComponentName.find(componentName);
if (keyframesByComponentNameIt == keyframesByComponentName.end()) {
throw std::runtime_error(
"[Reanimated] No keyframes with name `" + animationName +
"` were registered for component `" + componentName + "`");
}
return keyframesByComponentNameIt->second;
}
void CSSKeyframesRegistry::set(
const std::string &animationName,
const std::string &componentName,
CSSKeyframesConfig &&config) {
registry_[animationName][componentName] = std::move(config);
}
void CSSKeyframesRegistry::remove(
const std::string &animationName,
const std::string &componentName) {
registry_[animationName].erase(componentName);
if (registry_[animationName].empty()) {
registry_.erase(animationName);
}
}
} // namespace reanimated::css
@@ -1,38 +0,0 @@
#pragma once
#include <reanimated/CSS/configs/CSSKeyframesConfig.h>
#include <reanimated/CSS/interpolation/styles/AnimationStyleInterpolator.h>
#include <reanimated/CSS/misc/ViewStylesRepository.h>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
namespace reanimated::css {
class CSSKeyframesRegistry {
public:
CSSKeyframesRegistry(
const std::shared_ptr<ViewStylesRepository> &viewStylesRepository);
const CSSKeyframesConfig &get(
const std::string &animationName,
const std::string &componentName);
void set(
const std::string &animationName,
const std::string &componentName,
CSSKeyframesConfig &&config);
void remove(
const std::string &animationName,
const std::string &componentName);
private:
using ConfigsByComponentName =
std::unordered_map<std::string, CSSKeyframesConfig>;
std::unordered_map<std::string, ConfigsByComponentName> registry_;
const std::shared_ptr<ViewStylesRepository> viewStylesRepository_;
};
} // namespace reanimated::css

Some files were not shown because too many files have changed in this diff Show More