From 14a4dde54fb45870fb427549fa5d6383a3a85d1d Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 25 Feb 2024 15:27:50 +0530 Subject: [PATCH 01/15] value unit in transform operations --- .../Libraries/StyleSheet/processTransform.js | 8 +++ .../LayoutAnimationKeyFrameManager.cpp | 13 +++-- .../LayoutAnimationKeyFrameManager.h | 4 +- .../components/view/ViewPropsInterpolation.h | 7 ++- .../renderer/components/view/conversions.h | 3 +- .../react/renderer/graphics/Transform.cpp | 58 +++++++++++-------- .../react/renderer/graphics/Transform.h | 19 ++++-- .../react/renderer/graphics/ValueUnit.cpp | 30 ++++++++++ .../react/renderer/graphics/ValueUnit.h | 3 + 9 files changed, 106 insertions(+), 39 deletions(-) create mode 100644 packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.cpp diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index a703ac47161462..195d186a5a525b 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -256,6 +256,14 @@ function _validateTransform( break; case 'translateX': case 'translateY': + invariant( + typeof value === 'number' || + (typeof value === 'string' && value.endsWith('%')), + 'Transform with key of "%s" must be number or a percentage. Passed value: %s.', + key, + stringifySafe(transformation), + ); + break; case 'scale': case 'scaleX': case 'scaleY': diff --git a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp index db99dd64fd1653..983f182108d492 100644 --- a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp @@ -1118,7 +1118,10 @@ ShadowView LayoutAnimationKeyFrameManager::createInterpolatedShadowView( propsParserContext, progress, startingView.props, - finalView.props); + finalView.props, + finalView.layoutMetrics.frame.size.width, + finalView.layoutMetrics.frame.size.height + ); react_native_assert(mutatedShadowView.props != nullptr); if (mutatedShadowView.props == nullptr) { @@ -1627,7 +1630,9 @@ Props::Shared LayoutAnimationKeyFrameManager::interpolateProps( const PropsParserContext& context, Float animationProgress, const Props::Shared& props, - const Props::Shared& newProps) const { + const Props::Shared& newProps, + Float viewWidth, + Float viewHeight) const { #ifdef ANDROID // On Android only, the merged props should have the same RawProps as the // final props struct @@ -1640,11 +1645,11 @@ Props::Shared LayoutAnimationKeyFrameManager::interpolateProps( Props::Shared interpolatedPropsShared = componentDescriptor.cloneProps(context, newProps, {}); #endif - + if (componentDescriptor.getTraits().check( ShadowNodeTraits::Trait::ViewKind)) { interpolateViewProps( - animationProgress, props, newProps, interpolatedPropsShared); + animationProgress, props, newProps, interpolatedPropsShared, viewWidth, viewHeight); } return interpolatedPropsShared; diff --git a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h index 04b5acc45ed915..8b16bb15d16c0c 100644 --- a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h +++ b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h @@ -179,7 +179,9 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, const PropsParserContext& context, Float animationProgress, const Props::Shared& props, - const Props::Shared& newProps) const; + const Props::Shared& newProps, + Float viewWidth, + Float viewHeight) const; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h b/packages/react-native/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h index 2e95558c493273..a710b42a65725e 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h @@ -21,7 +21,9 @@ static inline void interpolateViewProps( Float animationProgress, const Props::Shared& oldPropsShared, const Props::Shared& newPropsShared, - Props::Shared& interpolatedPropsShared) { + Props::Shared& interpolatedPropsShared, + Float viewWidth, + Float viewHeight) { const ViewProps* oldViewProps = static_cast(oldPropsShared.get()); const ViewProps* newViewProps = @@ -31,9 +33,8 @@ static inline void interpolateViewProps( interpolatedProps->opacity = oldViewProps->opacity + (newViewProps->opacity - oldViewProps->opacity) * animationProgress; - interpolatedProps->transform = Transform::Interpolate( - animationProgress, oldViewProps->transform, newViewProps->transform); + animationProgress, oldViewProps->transform, newViewProps->transform, viewWidth, viewHeight); // Android uses RawProps, not props, to update props on the platform... // Since interpolated props don't interpolate at all using RawProps, we need diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h index aa6fb367daab7d..445c1004805c59 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h @@ -511,8 +511,9 @@ inline void fromRawValue( for (auto number : numbers) { transformMatrix.matrix[i++] = number; } + auto Zero = ValueUnit(0, UnitType::Point); transformMatrix.operations.push_back( - TransformOperation{TransformOperationType::Arbitrary, 0, 0, 0}); + TransformOperation{TransformOperationType::Arbitrary, Zero, Zero, Zero}); } else if (operation == "perspective") { transformMatrix = transformMatrix * Transform::Perspective((Float)parameters); diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp index 3cd227e0292df3..068be5ea1c9832 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp +++ b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp @@ -41,8 +41,9 @@ Transform Transform::HorizontalInversion() { Transform Transform::Perspective(Float perspective) { auto transform = Transform{}; + auto Zero = ValueUnit(0, UnitType::Point); transform.operations.push_back(TransformOperation{ - TransformOperationType::Perspective, perspective, 0, 0}); + TransformOperationType::Perspective, ValueUnit(perspective, UnitType::Point), Zero, Zero}); transform.matrix[11] = -1 / perspective; return transform; } @@ -54,7 +55,7 @@ Transform Transform::Scale(Float x, Float y, Float z) { Float zprime = isZero(z) ? 0 : z; if (xprime != 1 || yprime != 1 || zprime != 1) { transform.operations.push_back(TransformOperation{ - TransformOperationType::Scale, xprime, yprime, zprime}); + TransformOperationType::Scale, ValueUnit(xprime, UnitType::Point), ValueUnit(yprime, UnitType::Point), ValueUnit(zprime, UnitType::Point)}); transform.matrix[0] = xprime; transform.matrix[5] = yprime; transform.matrix[10] = zprime; @@ -69,7 +70,7 @@ Transform Transform::Translate(Float x, Float y, Float z) { Float zprime = isZero(z) ? 0 : z; if (xprime != 0 || yprime != 0 || zprime != 0) { transform.operations.push_back(TransformOperation{ - TransformOperationType::Translate, xprime, yprime, zprime}); + TransformOperationType::Translate, ValueUnit(xprime, UnitType::Point), ValueUnit(yprime, UnitType::Point), ValueUnit(zprime, UnitType::Point)}); transform.matrix[12] = xprime; transform.matrix[13] = yprime; transform.matrix[14] = zprime; @@ -82,7 +83,7 @@ Transform Transform::Skew(Float x, Float y) { Float xprime = isZero(x) ? 0 : x; Float yprime = isZero(y) ? 0 : y; transform.operations.push_back( - TransformOperation{TransformOperationType::Skew, xprime, yprime, 0}); + TransformOperation{TransformOperationType::Skew, ValueUnit(xprime, UnitType::Point), ValueUnit(yprime, UnitType::Point), ValueUnit(0, UnitType::Point)}); transform.matrix[4] = std::tan(xprime); transform.matrix[1] = std::tan(yprime); return transform; @@ -91,8 +92,9 @@ Transform Transform::Skew(Float x, Float y) { Transform Transform::RotateX(Float radians) { auto transform = Transform{}; if (!isZero(radians)) { + auto Zero = ValueUnit(0, UnitType::Point); transform.operations.push_back( - TransformOperation{TransformOperationType::Rotate, radians, 0, 0}); + TransformOperation{TransformOperationType::Rotate, ValueUnit(radians, UnitType::Point), Zero, Zero}); transform.matrix[5] = std::cos(radians); transform.matrix[6] = std::sin(radians); transform.matrix[9] = -std::sin(radians); @@ -104,8 +106,9 @@ Transform Transform::RotateX(Float radians) { Transform Transform::RotateY(Float radians) { auto transform = Transform{}; if (!isZero(radians)) { + auto Zero = ValueUnit(0, UnitType::Point); transform.operations.push_back( - TransformOperation{TransformOperationType::Rotate, 0, radians, 0}); + TransformOperation{TransformOperationType::Rotate, Zero, ValueUnit(radians, UnitType::Point), Zero}); transform.matrix[0] = std::cos(radians); transform.matrix[2] = -std::sin(radians); transform.matrix[8] = std::sin(radians); @@ -117,8 +120,9 @@ Transform Transform::RotateY(Float radians) { Transform Transform::RotateZ(Float radians) { auto transform = Transform{}; if (!isZero(radians)) { + auto Zero = ValueUnit(0, UnitType::Point); transform.operations.push_back( - TransformOperation{TransformOperationType::Rotate, 0, 0, radians}); + TransformOperation{TransformOperationType::Rotate, Zero, Zero, ValueUnit(radians, UnitType::Point)}); transform.matrix[0] = std::cos(radians); transform.matrix[1] = std::sin(radians); transform.matrix[4] = -std::sin(radians); @@ -130,7 +134,7 @@ Transform Transform::RotateZ(Float radians) { Transform Transform::Rotate(Float x, Float y, Float z) { auto transform = Transform{}; transform.operations.push_back( - TransformOperation{TransformOperationType::Rotate, x, y, z}); + TransformOperation{TransformOperationType::Rotate, ValueUnit(x, UnitType::Point), ValueUnit(y, UnitType::Point), ValueUnit(z, UnitType::Point)}); if (!isZero(x)) { transform = transform * Transform::RotateX(x); } @@ -144,24 +148,24 @@ Transform Transform::Rotate(Float x, Float y, Float z) { } Transform Transform::FromTransformOperation( - TransformOperation transformOperation) { + TransformOperation transformOperation, Float viewWidth, Float viewHeight) { if (transformOperation.type == TransformOperationType::Perspective) { - return Transform::Perspective(transformOperation.x); + return Transform::Perspective(transformOperation.x.value); } if (transformOperation.type == TransformOperationType::Scale) { return Transform::Scale( - transformOperation.x, transformOperation.y, transformOperation.z); + transformOperation.x.value, transformOperation.y.value, transformOperation.z.value); } if (transformOperation.type == TransformOperationType::Translate) { return Transform::Translate( - transformOperation.x, transformOperation.y, transformOperation.z); + transformOperation.x.value, transformOperation.y.value, transformOperation.z.value); } if (transformOperation.type == TransformOperationType::Skew) { - return Transform::Skew(transformOperation.x, transformOperation.y); + return Transform::Skew(transformOperation.x.value, transformOperation.y.value); } if (transformOperation.type == TransformOperationType::Rotate) { return Transform::Rotate( - transformOperation.x, transformOperation.y, transformOperation.z); + transformOperation.x.value, transformOperation.y.value, transformOperation.z.value); } // Identity or Arbitrary @@ -170,29 +174,33 @@ Transform Transform::FromTransformOperation( TransformOperation Transform::DefaultTransformOperation( TransformOperationType type) { + auto Zero = ValueUnit{0, UnitType::Point}; + auto One = ValueUnit{1, UnitType::Point}; switch (type) { case TransformOperationType::Arbitrary: - return TransformOperation{TransformOperationType::Arbitrary, 0, 0, 0}; + return TransformOperation{TransformOperationType::Arbitrary, Zero, Zero, Zero}; case TransformOperationType::Perspective: - return TransformOperation{TransformOperationType::Perspective, 0, 0, 0}; + return TransformOperation{TransformOperationType::Perspective, Zero, Zero, Zero}; case TransformOperationType::Scale: - return TransformOperation{TransformOperationType::Scale, 1, 1, 1}; + return TransformOperation{TransformOperationType::Scale, One, One, One}; case TransformOperationType::Translate: - return TransformOperation{TransformOperationType::Translate, 0, 0, 0}; + return TransformOperation{TransformOperationType::Translate, Zero, Zero, Zero}; case TransformOperationType::Rotate: - return TransformOperation{TransformOperationType::Rotate, 0, 0, 0}; + return TransformOperation{TransformOperationType::Rotate, Zero, Zero, Zero}; case TransformOperationType::Skew: - return TransformOperation{TransformOperationType::Skew, 0, 0, 0}; + return TransformOperation{TransformOperationType::Skew, Zero, Zero, Zero}; default: case TransformOperationType::Identity: - return TransformOperation{TransformOperationType::Identity, 0, 0, 0}; + return TransformOperation{TransformOperationType::Identity, Zero, Zero, Zero}; } } Transform Transform::Interpolate( Float animationProgress, const Transform& lhs, - const Transform& rhs) { + const Transform& rhs, + Float viewWidth, + Float viewHeight) { // Iterate through operations and reconstruct an interpolated resulting // transform If at any point we hit an "Arbitrary" Transform, return at that // point @@ -240,9 +248,9 @@ Transform Transform::Interpolate( result = result * Transform::FromTransformOperation(TransformOperation{ type, - lhsOp.x + (rhsOp.x - lhsOp.x) * animationProgress, - lhsOp.y + (rhsOp.y - lhsOp.y) * animationProgress, - lhsOp.z + (rhsOp.z - lhsOp.z) * animationProgress}); + ValueUnit(lhsOp.x.value + (rhsOp.x.value - lhsOp.x.value) * animationProgress, UnitType::Point), + ValueUnit(lhsOp.y.value + (rhsOp.y.value - lhsOp.y.value) * animationProgress, UnitType::Point), + ValueUnit(lhsOp.z.value + (rhsOp.z.value - lhsOp.z.value) * animationProgress, UnitType::Point)}, viewWidth, viewHeight); } return result; diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h index 1ed8353862b8ee..3004d038d5e6e2 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h +++ b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h @@ -17,6 +17,7 @@ #include #include #include +#include #ifdef ANDROID #include @@ -47,9 +48,15 @@ enum class TransformOperationType { struct TransformOperation { TransformOperationType type; - Float x; - Float y; - Float z; + ValueUnit x; + ValueUnit y; + ValueUnit z; + bool operator==(const TransformOperation& other) const { + return type == other.type && x == other.x && y == other.y && z == other.z; + } + bool operator!=(const TransformOperation& other) const { + return !(*this == other); + } }; struct TransformOrigin { @@ -88,7 +95,7 @@ struct Transform { * Given a TransformOperation, return the proper transform. */ static Transform FromTransformOperation( - TransformOperation transformOperation); + TransformOperation transformOperation, Float viewWidth, Float viewHeight); static TransformOperation DefaultTransformOperation( TransformOperationType type); @@ -151,7 +158,9 @@ struct Transform { static Transform Interpolate( Float animationProgress, const Transform& lhs, - const Transform& rhs); + const Transform& rhs, + Float viewWidth, + Float viewHeight); static bool isVerticalInversion(const Transform& transform); static bool isHorizontalInversion(const Transform& transform); diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.cpp b/packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.cpp new file mode 100644 index 00000000000000..0acef0dd0330c6 --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "ValueUnit.h" + +#include + +namespace facebook::react { + +ValueUnit ValueUnit::getValueUnitFromRawValue(const RawValue& value) { + if (value.hasType()) { + auto number = (float)value; + return ValueUnit(number, UnitType::Point); + } else if (value.hasType()) { + const auto stringValue = (std::string)value; + if (stringValue.back() == '%') { + auto tryValue = folly::tryTo( + std::string_view(stringValue).substr(0, stringValue.length() - 1)); + if (tryValue.hasValue()) { + return ValueUnit(tryValue.value(), UnitType::Percent); + } + } + } + return ValueUnit(); +}; +} // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.h b/packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.h index 6d0d535c3b88e8..8ab6a647771f36 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.h +++ b/packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.h @@ -6,6 +6,7 @@ */ #pragma once +#include namespace facebook::react { @@ -28,5 +29,7 @@ struct ValueUnit { bool operator!=(const ValueUnit& other) const { return !(*this == other); } + + static ValueUnit getValueUnitFromRawValue(const RawValue& value); }; } // namespace facebook::react From 6829aa28badea13b2699d1e319d7020e781ee2a6 Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 25 Feb 2024 16:15:59 +0530 Subject: [PATCH 02/15] translate with percentage --- .../View/RCTViewComponentView.mm | 2 +- .../components/view/BaseViewProps.cpp | 36 +++++++---- .../renderer/components/view/conversions.h | 63 ++++++++++--------- .../react/renderer/graphics/Transform.cpp | 19 +++++- 4 files changed, 78 insertions(+), 42 deletions(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index a015c450ee21b7..cf5d660f6efcba 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -410,7 +410,7 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics _contentView.frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame()); } - if (_props->transformOrigin.isSet()) { + if (_props->transformOrigin.isSet() || _props->transform.operations.size() > 0) { auto newTransform = _props->resolveTransform(layoutMetrics); self.layer.transform = RCTCATransform3DFromTransformMatrix(newTransform); } diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp index 3d55f25c38cb9c..cf94472433f80d 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp @@ -392,19 +392,33 @@ Transform BaseViewProps::resolveTransform( LayoutMetrics const& layoutMetrics) const { float viewWidth = layoutMetrics.frame.size.width; float viewHeight = layoutMetrics.frame.size.height; - if (!transformOrigin.isSet() || (viewWidth == 0 && viewHeight == 0)) { - return transform; + auto transformMatrix = Transform{}; + + if (viewWidth == 0 && viewHeight == 0){ + return transformMatrix; } - std::array translateOffsets = - getTranslateForTransformOrigin(viewWidth, viewHeight, transformOrigin); - auto newTransform = Transform::Translate( - translateOffsets[0], translateOffsets[1], translateOffsets[2]); - newTransform = newTransform * transform; - newTransform = - newTransform * + + // transform is matrix + if (transform.operations.size() == 1 && transform.operations[0].type == TransformOperationType::Arbitrary) { + transformMatrix = transformMatrix * transform; + } else { + for (size_t i = 0; i < transform.operations.size(); i++) { + auto transformFromOperation = transform.FromTransformOperation(transform.operations[i], viewWidth, viewHeight); + transformMatrix = transformMatrix * transformFromOperation; + } + } + + if (transformOrigin.isSet()) { + std::array translateOffsets = + getTranslateForTransformOrigin(viewWidth, viewHeight, transformOrigin); + transformMatrix = Transform::Translate( - -translateOffsets[0], -translateOffsets[1], -translateOffsets[2]); - return newTransform; + translateOffsets[0], translateOffsets[1], translateOffsets[2]) + * transformMatrix + * Transform::Translate(-translateOffsets[0], -translateOffsets[1], -translateOffsets[2]); + } + + return transformMatrix; } bool BaseViewProps::getClipsContentToBounds() const { diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h index 445c1004805c59..743c0408c925aa 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h @@ -502,6 +502,8 @@ inline void fromRawValue( auto pair = configurationPair.begin(); auto operation = pair->first; auto& parameters = pair->second; + auto Zero = ValueUnit(0, UnitType::Point); + auto One = ValueUnit(1, UnitType::Point); if (operation == "matrix") { react_native_expect(parameters.hasType>()); @@ -511,50 +513,53 @@ inline void fromRawValue( for (auto number : numbers) { transformMatrix.matrix[i++] = number; } - auto Zero = ValueUnit(0, UnitType::Point); transformMatrix.operations.push_back( TransformOperation{TransformOperationType::Arbitrary, Zero, Zero, Zero}); } else if (operation == "perspective") { - transformMatrix = - transformMatrix * Transform::Perspective((Float)parameters); + transformMatrix.operations.push_back(TransformOperation{ + TransformOperationType::Perspective, ValueUnit((Float)parameters, UnitType::Point), Zero, Zero}); } else if (operation == "rotateX") { - transformMatrix = transformMatrix * - Transform::Rotate(toRadians(parameters, 0.0f), 0, 0); + transformMatrix.operations.push_back( + TransformOperation{TransformOperationType::Rotate, ValueUnit(toRadians(parameters, 0.0f), UnitType::Point), Zero, Zero}); } else if (operation == "rotateY") { - transformMatrix = transformMatrix * - Transform::Rotate(0, toRadians(parameters, 0.0f), 0); + transformMatrix.operations.push_back( + TransformOperation{TransformOperationType::Rotate, Zero, ValueUnit(toRadians(parameters, 0.0f), UnitType::Point), Zero}); } else if (operation == "rotateZ" || operation == "rotate") { - transformMatrix = transformMatrix * - Transform::Rotate(0, 0, toRadians(parameters, 0.0f)); + transformMatrix.operations.push_back( + TransformOperation{TransformOperationType::Rotate, Zero, Zero, ValueUnit(toRadians(parameters, 0.0f), UnitType::Point)}); } else if (operation == "scale") { - auto number = (Float)parameters; - transformMatrix = - transformMatrix * Transform::Scale(number, number, number); + auto number = ValueUnit((Float)parameters, UnitType::Point); + transformMatrix.operations.push_back(TransformOperation{ + TransformOperationType::Scale, number, number, number}); } else if (operation == "scaleX") { - transformMatrix = - transformMatrix * Transform::Scale((Float)parameters, 1, 1); + transformMatrix.operations.push_back(TransformOperation{ + TransformOperationType::Scale, ValueUnit((Float)parameters, UnitType::Point), One, One}); } else if (operation == "scaleY") { - transformMatrix = - transformMatrix * Transform::Scale(1, (Float)parameters, 1); + transformMatrix.operations.push_back(TransformOperation{ + TransformOperationType::Scale, One, ValueUnit((Float)parameters, UnitType::Point), One}); } else if (operation == "scaleZ") { - transformMatrix = - transformMatrix * Transform::Scale(1, 1, (Float)parameters); + transformMatrix.operations.push_back(TransformOperation{ + TransformOperationType::Scale, One, One, ValueUnit((Float)parameters, UnitType::Point)}); } else if (operation == "translate") { - auto numbers = (std::vector)parameters; - transformMatrix = transformMatrix * - Transform::Translate(numbers.at(0), numbers.at(1), 0); + auto numbers = (std::vector)parameters; + auto valueX = ValueUnit::getValueUnitFromRawValue(numbers.at(0)); + auto valueY = ValueUnit::getValueUnitFromRawValue(numbers.at(1)); + transformMatrix.operations.push_back(TransformOperation{ + TransformOperationType::Translate, valueX, valueY, Zero}); } else if (operation == "translateX") { - transformMatrix = - transformMatrix * Transform::Translate((Float)parameters, 0, 0); + auto value = ValueUnit::getValueUnitFromRawValue(parameters); + transformMatrix.operations.push_back(TransformOperation{ + TransformOperationType::Translate, value, Zero, Zero}); } else if (operation == "translateY") { - transformMatrix = - transformMatrix * Transform::Translate(0, (Float)parameters, 0); + auto value = ValueUnit::getValueUnitFromRawValue(parameters); + transformMatrix.operations.push_back(TransformOperation{ + TransformOperationType::Translate, Zero, value, Zero}); } else if (operation == "skewX") { - transformMatrix = - transformMatrix * Transform::Skew(toRadians(parameters, 0.0f), 0); + transformMatrix.operations.push_back( + TransformOperation{TransformOperationType::Skew, ValueUnit(toRadians(parameters, 0.0f), UnitType::Point), Zero, Zero}); } else if (operation == "skewY") { - transformMatrix = - transformMatrix * Transform::Skew(0, toRadians(parameters, 0.0f)); + transformMatrix.operations.push_back( + TransformOperation{TransformOperationType::Skew, Zero, ValueUnit(toRadians(parameters, 0.0f), UnitType::Point), Zero}); } } diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp index 068be5ea1c9832..bcef2e32cf73ec 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp +++ b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp @@ -157,8 +157,17 @@ Transform Transform::FromTransformOperation( transformOperation.x.value, transformOperation.y.value, transformOperation.z.value); } if (transformOperation.type == TransformOperationType::Translate) { + auto translateX = transformOperation.x.value; + auto translateY = transformOperation.y.value; + if (transformOperation.x.unit == UnitType::Percent) { + translateX = translateX * viewWidth / 100.0; + } + if (transformOperation.y.unit == UnitType::Percent) { + translateY = translateY * viewHeight / 100.0; + } + return Transform::Translate( - transformOperation.x.value, transformOperation.y.value, transformOperation.z.value); + translateX, translateY, transformOperation.z.value); } if (transformOperation.type == TransformOperationType::Skew) { return Transform::Skew(transformOperation.x.value, transformOperation.y.value); @@ -270,6 +279,14 @@ bool Transform::operator==(const Transform& rhs) const { return false; } } + if (this->operations.size() != rhs.operations.size()) { + return false; + } + for (size_t i = 0; i < this->operations.size(); i++) { + if (this->operations[i] != rhs.operations[i]) { + return false; + } + } return true; } From 88799308b8d1b91cd8babef87553970254012d58 Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 25 Feb 2024 17:25:50 +0530 Subject: [PATCH 03/15] translate regex to support % --- .../Libraries/StyleSheet/processTransform.js | 8 ++++++-- .../js/examples/Transform/TransformExample.js | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index 195d186a5a525b..3eba9a50fe3423 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -69,7 +69,7 @@ const _getKeyAndValueFromCSSTransform: ( | $TEMPORARY$string<'translateY'>, args: string, ) => {key: string, value?: number[] | number | string} = (key, args) => { - const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z]+)?/g); + const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z%]+)?/g); switch (key) { case 'matrix': @@ -88,7 +88,11 @@ const _getKeyAndValueFromCSSTransform: ( missingUnitOfMeasurement = true; } - parsedArgs.push(value); + if (unitOfMeasurement === '%') { + parsedArgs.push(`${value}%`); + } else { + parsedArgs.push(value); + } } if (__DEV__) { diff --git a/packages/rn-tester/js/examples/Transform/TransformExample.js b/packages/rn-tester/js/examples/Transform/TransformExample.js index 75370c931cc47c..b163d8ba1587ce 100644 --- a/packages/rn-tester/js/examples/Transform/TransformExample.js +++ b/packages/rn-tester/js/examples/Transform/TransformExample.js @@ -139,6 +139,10 @@ function Flip() { ); } +function TranslatePercentage() { + return ; +} + const styles = StyleSheet.create({ container: { height: 500, @@ -277,6 +281,12 @@ const styles = StyleSheet.create({ height: 100, transformOrigin: 'top left', }, + translatePercentageView: { + transform: 'translate(50%)', + padding: 50, + alignSelf: 'flex-start', + backgroundColor: 'lightblue', + }, }); exports.title = 'Transforms'; @@ -396,4 +406,11 @@ exports.examples = [ return ; }, }, + { + title: 'Translate Percentage', + description: "transform: 'translate(50%)'", + render(): Node { + return ; + }, + }, ]; From 50fd7fba0fb4ed2583da9641da28209e1e83dec7 Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 25 Feb 2024 19:28:52 +0530 Subject: [PATCH 04/15] translate percentage test --- .../__tests__/__snapshots__/processTransform-test.js.snap | 2 +- .../Libraries/StyleSheet/__tests__/processTransform-test.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/StyleSheet/__tests__/__snapshots__/processTransform-test.js.snap b/packages/react-native/Libraries/StyleSheet/__tests__/__snapshots__/processTransform-test.js.snap index dc8d045189ea5e..119c7344a130ba 100644 --- a/packages/react-native/Libraries/StyleSheet/__tests__/__snapshots__/processTransform-test.js.snap +++ b/packages/react-native/Libraries/StyleSheet/__tests__/__snapshots__/processTransform-test.js.snap @@ -32,7 +32,7 @@ exports[`processTransform validation should throw when passing an invalid angle exports[`processTransform validation should throw when passing an invalid angle prop 4`] = `"Rotate transform must be expressed in degrees (deg) or radians (rad): {\\"skewX\\":\\"10drg\\"}"`; -exports[`processTransform validation should throw when passing an invalid value to a number prop 1`] = `"Transform with key of \\"translateY\\" must be a number: {\\"translateY\\":\\"20deg\\"}"`; +exports[`processTransform validation should throw when passing an invalid value to a number prop 1`] = `"Transform with key of \\"translateY\\" must be number or a percentage. Passed value: {\\"translateY\\":\\"20deg\\"}."`; exports[`processTransform validation should throw when passing an invalid value to a number prop 2`] = `"Transform with key of \\"scale\\" must be a number: {\\"scale\\":{\\"x\\":10,\\"y\\":10}}"`; diff --git a/packages/react-native/Libraries/StyleSheet/__tests__/processTransform-test.js b/packages/react-native/Libraries/StyleSheet/__tests__/processTransform-test.js index 66ce708c033897..fe7c5d2851505f 100644 --- a/packages/react-native/Libraries/StyleSheet/__tests__/processTransform-test.js +++ b/packages/react-native/Libraries/StyleSheet/__tests__/processTransform-test.js @@ -34,6 +34,11 @@ describe('processTransform', () => { ); }); + it('should accept a percentage translate transform', () => { + processTransform([{translateY: '20%'}, {translateX: '10%'}]); + processTransform('translateX(10%)'); + }); + it('should throw on object with multiple properties', () => { expect(() => processTransform([{scale: 0.5, translateY: 10}]), From 411c84dd22e98cc0221bbaae65f46f4541ef95d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nishan=20=28o=5E=E2=96=BD=5Eo=29?= Date: Tue, 27 Feb 2024 08:48:29 +0530 Subject: [PATCH 05/15] default in equality operator for transform operation C++ 20 lets you default this operator (does memberwise-comparison) C++ 20 will automatically define != if you define == Co-authored-by: Nick Gerleman --- .../ReactCommon/react/renderer/graphics/Transform.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h index 3004d038d5e6e2..9ab883edd5acb8 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h +++ b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h @@ -51,12 +51,7 @@ struct TransformOperation { ValueUnit x; ValueUnit y; ValueUnit z; - bool operator==(const TransformOperation& other) const { - return type == other.type && x == other.x && y == other.y && z == other.z; - } - bool operator!=(const TransformOperation& other) const { - return !(*this == other); - } + bool operator==(const TransformOperation& other) const = default; }; struct TransformOrigin { From 5a6f86e557e1830ffc7d4ac3564f9f988cf6b14d Mon Sep 17 00:00:00 2001 From: Nishan Date: Tue, 5 Mar 2024 11:25:52 +0530 Subject: [PATCH 06/15] pr feedback: Size struct --- .../animations/LayoutAnimationKeyFrameManager.cpp | 9 ++++----- .../animations/LayoutAnimationKeyFrameManager.h | 3 +-- .../react/renderer/components/view/BaseViewProps.cpp | 6 +++--- .../renderer/components/view/ViewPropsInterpolation.h | 5 ++--- .../ReactCommon/react/renderer/graphics/Transform.cpp | 11 +++++------ .../ReactCommon/react/renderer/graphics/Transform.h | 5 ++--- 6 files changed, 17 insertions(+), 22 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp index 983f182108d492..c8147c64625104 100644 --- a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp @@ -1113,14 +1113,14 @@ ShadowView LayoutAnimationKeyFrameManager::createInterpolatedShadowView( // Animate opacity or scale/transform PropsParserContext propsParserContext{ finalView.surfaceId, *contextContainer_}; + auto finalViewSize = Size{finalView.layoutMetrics.frame.size.width, finalView.layoutMetrics.frame.size.height}; mutatedShadowView.props = interpolateProps( componentDescriptor, propsParserContext, progress, startingView.props, finalView.props, - finalView.layoutMetrics.frame.size.width, - finalView.layoutMetrics.frame.size.height + finalViewSize ); react_native_assert(mutatedShadowView.props != nullptr); @@ -1631,8 +1631,7 @@ Props::Shared LayoutAnimationKeyFrameManager::interpolateProps( Float animationProgress, const Props::Shared& props, const Props::Shared& newProps, - Float viewWidth, - Float viewHeight) const { + const Size& size) const { #ifdef ANDROID // On Android only, the merged props should have the same RawProps as the // final props struct @@ -1649,7 +1648,7 @@ Props::Shared LayoutAnimationKeyFrameManager::interpolateProps( if (componentDescriptor.getTraits().check( ShadowNodeTraits::Trait::ViewKind)) { interpolateViewProps( - animationProgress, props, newProps, interpolatedPropsShared, viewWidth, viewHeight); + animationProgress, props, newProps, interpolatedPropsShared, size); } return interpolatedPropsShared; diff --git a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h index 8b16bb15d16c0c..82342767c4ce50 100644 --- a/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h +++ b/packages/react-native/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h @@ -180,8 +180,7 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, Float animationProgress, const Props::Shared& props, const Props::Shared& newProps, - Float viewWidth, - Float viewHeight) const; + const Size& size) const; }; } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp index cf94472433f80d..ceb489a2d3fbea 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp @@ -400,11 +400,11 @@ Transform BaseViewProps::resolveTransform( // transform is matrix if (transform.operations.size() == 1 && transform.operations[0].type == TransformOperationType::Arbitrary) { - transformMatrix = transformMatrix * transform; + transformMatrix = transform; } else { + auto viewSize = Size{viewWidth, viewHeight}; for (size_t i = 0; i < transform.operations.size(); i++) { - auto transformFromOperation = transform.FromTransformOperation(transform.operations[i], viewWidth, viewHeight); - transformMatrix = transformMatrix * transformFromOperation; + transformMatrix = transformMatrix * transform.FromTransformOperation(transform.operations[i], viewSize); } } diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h b/packages/react-native/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h index a710b42a65725e..e60064a637d9de 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/ViewPropsInterpolation.h @@ -22,8 +22,7 @@ static inline void interpolateViewProps( const Props::Shared& oldPropsShared, const Props::Shared& newPropsShared, Props::Shared& interpolatedPropsShared, - Float viewWidth, - Float viewHeight) { + const Size& size) { const ViewProps* oldViewProps = static_cast(oldPropsShared.get()); const ViewProps* newViewProps = @@ -34,7 +33,7 @@ static inline void interpolateViewProps( interpolatedProps->opacity = oldViewProps->opacity + (newViewProps->opacity - oldViewProps->opacity) * animationProgress; interpolatedProps->transform = Transform::Interpolate( - animationProgress, oldViewProps->transform, newViewProps->transform, viewWidth, viewHeight); + animationProgress, oldViewProps->transform, newViewProps->transform, size); // Android uses RawProps, not props, to update props on the platform... // Since interpolated props don't interpolate at all using RawProps, we need diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp index bcef2e32cf73ec..ec6c929988ccd2 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp +++ b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp @@ -148,7 +148,7 @@ Transform Transform::Rotate(Float x, Float y, Float z) { } Transform Transform::FromTransformOperation( - TransformOperation transformOperation, Float viewWidth, Float viewHeight) { + TransformOperation transformOperation, const Size& size) { if (transformOperation.type == TransformOperationType::Perspective) { return Transform::Perspective(transformOperation.x.value); } @@ -160,10 +160,10 @@ Transform Transform::FromTransformOperation( auto translateX = transformOperation.x.value; auto translateY = transformOperation.y.value; if (transformOperation.x.unit == UnitType::Percent) { - translateX = translateX * viewWidth / 100.0; + translateX = translateX * size.width / 100.0; } if (transformOperation.y.unit == UnitType::Percent) { - translateY = translateY * viewHeight / 100.0; + translateY = translateY * size.height / 100.0; } return Transform::Translate( @@ -208,8 +208,7 @@ Transform Transform::Interpolate( Float animationProgress, const Transform& lhs, const Transform& rhs, - Float viewWidth, - Float viewHeight) { + const Size& size) { // Iterate through operations and reconstruct an interpolated resulting // transform If at any point we hit an "Arbitrary" Transform, return at that // point @@ -259,7 +258,7 @@ Transform Transform::Interpolate( type, ValueUnit(lhsOp.x.value + (rhsOp.x.value - lhsOp.x.value) * animationProgress, UnitType::Point), ValueUnit(lhsOp.y.value + (rhsOp.y.value - lhsOp.y.value) * animationProgress, UnitType::Point), - ValueUnit(lhsOp.z.value + (rhsOp.z.value - lhsOp.z.value) * animationProgress, UnitType::Point)}, viewWidth, viewHeight); + ValueUnit(lhsOp.z.value + (rhsOp.z.value - lhsOp.z.value) * animationProgress, UnitType::Point)}, size); } return result; diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h index 9ab883edd5acb8..64d8bfe61a7564 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h +++ b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.h @@ -90,7 +90,7 @@ struct Transform { * Given a TransformOperation, return the proper transform. */ static Transform FromTransformOperation( - TransformOperation transformOperation, Float viewWidth, Float viewHeight); + TransformOperation transformOperation, const Size& size); static TransformOperation DefaultTransformOperation( TransformOperationType type); @@ -154,8 +154,7 @@ struct Transform { Float animationProgress, const Transform& lhs, const Transform& rhs, - Float viewWidth, - Float viewHeight); + const Size& size); static bool isVerticalInversion(const Transform& transform); static bool isHorizontalInversion(const Transform& transform); From 9ac7106daf552a39ce18c649eafe9687be015aac Mon Sep 17 00:00:00 2001 From: Nishan Date: Sat, 30 Mar 2024 16:39:57 +0530 Subject: [PATCH 07/15] unit assert in interpolate --- .../ReactCommon/react/renderer/graphics/Transform.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp index ec6c929988ccd2..66cdd2f8c2b004 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp +++ b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp @@ -252,13 +252,16 @@ Transform Transform::Interpolate( : Transform::DefaultTransformOperation(type)); react_native_assert(type == lhsOp.type); react_native_assert(type == rhsOp.type); + react_native_assert(lhsOp.x.unit == rhsOp.x.unit); + react_native_assert(lhsOp.y.unit == rhsOp.y.unit); + react_native_assert(lhsOp.z.unit == rhsOp.z.unit); result = result * Transform::FromTransformOperation(TransformOperation{ type, - ValueUnit(lhsOp.x.value + (rhsOp.x.value - lhsOp.x.value) * animationProgress, UnitType::Point), - ValueUnit(lhsOp.y.value + (rhsOp.y.value - lhsOp.y.value) * animationProgress, UnitType::Point), - ValueUnit(lhsOp.z.value + (rhsOp.z.value - lhsOp.z.value) * animationProgress, UnitType::Point)}, size); + ValueUnit(lhsOp.x.value + (rhsOp.x.value - lhsOp.x.value) * animationProgress, lhsOp.x.unit), + ValueUnit(lhsOp.y.value + (rhsOp.y.value - lhsOp.y.value) * animationProgress, lhsOp.y.unit), + ValueUnit(lhsOp.z.value + (rhsOp.z.value - lhsOp.z.value) * animationProgress, lhsOp.z.unit)}, size); } return result; From 1ed755964bd5b526c65416f72c62e483b8d8d89b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nishan=20=28o=5E=E2=96=BD=5Eo=29?= Date: Sun, 31 Mar 2024 01:27:03 +0530 Subject: [PATCH 08/15] Update packages/react-native/Libraries/StyleSheet/processTransform.js Co-authored-by: Pieter De Baets --- packages/react-native/Libraries/StyleSheet/processTransform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index 3eba9a50fe3423..b88ba2ea6a4ab3 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -69,7 +69,7 @@ const _getKeyAndValueFromCSSTransform: ( | $TEMPORARY$string<'translateY'>, args: string, ) => {key: string, value?: number[] | number | string} = (key, args) => { - const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z%]+)?/g); + const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z]+|%)?/g); switch (key) { case 'matrix': From 371a81657207bc7ff67b81f1b94be6e4bbf1fd9b Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 31 Mar 2024 18:01:50 +0530 Subject: [PATCH 09/15] update transform when bounds change in updateLayoutMetrics --- .../Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index fec5ff92e05076..a7e24f6863dea0 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -415,7 +415,7 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics _contentView.frame = RCTCGRectFromRect(_layoutMetrics.getContentFrame()); } - if (_props->transformOrigin.isSet() || _props->transform.operations.size() > 0) { + if ((_props->transformOrigin.isSet() || _props->transform.operations.size() > 0) && layoutMetrics.frame.size != oldLayoutMetrics.frame.size) { auto newTransform = _props->resolveTransform(layoutMetrics); self.layer.transform = RCTCATransform3DFromTransformMatrix(newTransform); } From 5e63c8a1568de7dca6ea67c6eb52492d598ba070 Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 31 Mar 2024 21:24:24 +0530 Subject: [PATCH 10/15] flow check fix --- packages/react-native/Libraries/StyleSheet/processTransform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index b88ba2ea6a4ab3..49435279de9aa6 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -68,7 +68,7 @@ const _getKeyAndValueFromCSSTransform: ( | $TEMPORARY$string<'translateX'> | $TEMPORARY$string<'translateY'>, args: string, -) => {key: string, value?: number[] | number | string} = (key, args) => { +) => {key: string, value?: number[] | string[] | number | string} = (key, args) => { const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z]+|%)?/g); switch (key) { From b84a0035c2841d71a49619617d2313775cd0e5c2 Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 31 Mar 2024 21:30:52 +0530 Subject: [PATCH 11/15] prettier fix --- .../react-native/Libraries/StyleSheet/processTransform.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index 49435279de9aa6..bcc870d7778ae4 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -68,7 +68,10 @@ const _getKeyAndValueFromCSSTransform: ( | $TEMPORARY$string<'translateX'> | $TEMPORARY$string<'translateY'>, args: string, -) => {key: string, value?: number[] | string[] | number | string} = (key, args) => { +) => {key: string, value?: number[] | string[] | number | string} = ( + key, + args, +) => { const argsWithUnitsRegex = new RegExp(/([+-]?\d+(\.\d+)?)([a-zA-Z]+|%)?/g); switch (key) { From ea3b0afd9071eff46f72f7c504c1a5b36385a611 Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 31 Mar 2024 21:50:16 +0530 Subject: [PATCH 12/15] fix flow check --- packages/react-native/Libraries/StyleSheet/processTransform.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/StyleSheet/processTransform.js b/packages/react-native/Libraries/StyleSheet/processTransform.js index bcc870d7778ae4..6310a4b5c77550 100644 --- a/packages/react-native/Libraries/StyleSheet/processTransform.js +++ b/packages/react-native/Libraries/StyleSheet/processTransform.js @@ -68,7 +68,7 @@ const _getKeyAndValueFromCSSTransform: ( | $TEMPORARY$string<'translateX'> | $TEMPORARY$string<'translateY'>, args: string, -) => {key: string, value?: number[] | string[] | number | string} = ( +) => {key: string, value?: Array | number | string} = ( key, args, ) => { From 783056a399cd92f3c6cbd5ad8a47660c9d11d9d8 Mon Sep 17 00:00:00 2001 From: Nishan Date: Mon, 15 Apr 2024 06:35:18 +0530 Subject: [PATCH 13/15] resolve method in value unit struct --- .../react/renderer/graphics/Transform.cpp | 34 ++++++++----------- .../react/renderer/graphics/ValueUnit.h | 11 ++++++ 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp index 66cdd2f8c2b004..75473e044491c9 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp +++ b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp @@ -150,31 +150,25 @@ Transform Transform::Rotate(Float x, Float y, Float z) { Transform Transform::FromTransformOperation( TransformOperation transformOperation, const Size& size) { if (transformOperation.type == TransformOperationType::Perspective) { - return Transform::Perspective(transformOperation.x.value); + return Transform::Perspective(transformOperation.x.resolve(0)); } if (transformOperation.type == TransformOperationType::Scale) { return Transform::Scale( - transformOperation.x.value, transformOperation.y.value, transformOperation.z.value); + transformOperation.x.resolve(0), transformOperation.y.resolve(0), transformOperation.z.resolve(0)); } if (transformOperation.type == TransformOperationType::Translate) { - auto translateX = transformOperation.x.value; - auto translateY = transformOperation.y.value; - if (transformOperation.x.unit == UnitType::Percent) { - translateX = translateX * size.width / 100.0; - } - if (transformOperation.y.unit == UnitType::Percent) { - translateY = translateY * size.height / 100.0; - } - + auto translateX = transformOperation.x.resolve(size.width); + auto translateY = transformOperation.y.resolve(size.height); + return Transform::Translate( - translateX, translateY, transformOperation.z.value); + translateX, translateY, transformOperation.z.resolve(0)); } if (transformOperation.type == TransformOperationType::Skew) { - return Transform::Skew(transformOperation.x.value, transformOperation.y.value); + return Transform::Skew(transformOperation.x.resolve(0), transformOperation.y.resolve(0)); } if (transformOperation.type == TransformOperationType::Rotate) { return Transform::Rotate( - transformOperation.x.value, transformOperation.y.value, transformOperation.z.value); + transformOperation.x.resolve(0), transformOperation.y.resolve(0), transformOperation.z.resolve(0)); } // Identity or Arbitrary @@ -252,16 +246,16 @@ Transform Transform::Interpolate( : Transform::DefaultTransformOperation(type)); react_native_assert(type == lhsOp.type); react_native_assert(type == rhsOp.type); - react_native_assert(lhsOp.x.unit == rhsOp.x.unit); - react_native_assert(lhsOp.y.unit == rhsOp.y.unit); - react_native_assert(lhsOp.z.unit == rhsOp.z.unit); result = result * Transform::FromTransformOperation(TransformOperation{ type, - ValueUnit(lhsOp.x.value + (rhsOp.x.value - lhsOp.x.value) * animationProgress, lhsOp.x.unit), - ValueUnit(lhsOp.y.value + (rhsOp.y.value - lhsOp.y.value) * animationProgress, lhsOp.y.unit), - ValueUnit(lhsOp.z.value + (rhsOp.z.value - lhsOp.z.value) * animationProgress, lhsOp.z.unit)}, size); + ValueUnit(lhsOp.x.resolve(size.width) + (rhsOp.x.resolve(size.width) - lhsOp.x.resolve(size.width)) + * animationProgress, UnitType::Point), + ValueUnit(lhsOp.y.resolve(size.height) + (rhsOp.y.resolve(size.height) - lhsOp.y.resolve(size.width)) + * animationProgress, UnitType::Point), + ValueUnit(lhsOp.z.resolve(0) + (rhsOp.z.resolve(0) - lhsOp.z.resolve(0)) + * animationProgress, UnitType::Point)}, size); } return result; diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.h b/packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.h index 8ab6a647771f36..bc75f42d167f4c 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.h +++ b/packages/react-native/ReactCommon/react/renderer/graphics/ValueUnit.h @@ -30,6 +30,17 @@ struct ValueUnit { return !(*this == other); } + constexpr float resolve(float referenceLength) { + switch (unit) { + case UnitType::Point: + return value; + case UnitType::Percent: + return value * referenceLength * 0.01f; + default: + return 0.0f; + } + } + static ValueUnit getValueUnitFromRawValue(const RawValue& value); }; } // namespace facebook::react From 776d67db31482ad364022628ee29ef2ee30f5f58 Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 21 Apr 2024 21:36:07 +0530 Subject: [PATCH 14/15] test translate percentage --- .../renderer/graphics/tests/TransformTest.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/tests/TransformTest.cpp b/packages/react-native/ReactCommon/react/renderer/graphics/tests/TransformTest.cpp index dcbb3052e68b10..94f1d331132d80 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/tests/TransformTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/graphics/tests/TransformTest.cpp @@ -28,6 +28,23 @@ TEST(TransformTest, transformingPoint) { EXPECT_EQ(translatedPoint.y, 100); } +TEST(TransformTest, fromTransformOperationPercentage) { + auto point = facebook::react::Point{0, 0}; + Size size = {120, 200}; + + auto operation = TransformOperation{ + TransformOperationType::Translate, ValueUnit{50.0f, UnitType::Percent}, ValueUnit{20.0f, UnitType::Percent}}; + auto translatedPoint = point * Transform::FromTransformOperation(operation, size); + EXPECT_EQ(translatedPoint.x, 60); + EXPECT_EQ(translatedPoint.y, 40); + + operation = TransformOperation{ + TransformOperationType::Translate, ValueUnit{40.0f, UnitType::Percent}, ValueUnit{20.0f, UnitType::Point}}; + translatedPoint = point * Transform::FromTransformOperation(operation, size); + EXPECT_EQ(translatedPoint.x, 48); + EXPECT_EQ(translatedPoint.y, 20); +} + TEST(TransformTest, scalingRect) { auto point = facebook::react::Point{100, 200}; auto size = facebook::react::Size{300, 400}; From e677dd78e0b23494a3fbbc3d966abb613f6ace3b Mon Sep 17 00:00:00 2001 From: Nishan Date: Sun, 21 Apr 2024 23:03:38 +0530 Subject: [PATCH 15/15] translate y resolve height --- .../ReactCommon/react/renderer/graphics/Transform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp index 75473e044491c9..9a598f18d0c2a4 100644 --- a/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp +++ b/packages/react-native/ReactCommon/react/renderer/graphics/Transform.cpp @@ -252,7 +252,7 @@ Transform Transform::Interpolate( type, ValueUnit(lhsOp.x.resolve(size.width) + (rhsOp.x.resolve(size.width) - lhsOp.x.resolve(size.width)) * animationProgress, UnitType::Point), - ValueUnit(lhsOp.y.resolve(size.height) + (rhsOp.y.resolve(size.height) - lhsOp.y.resolve(size.width)) + ValueUnit(lhsOp.y.resolve(size.height) + (rhsOp.y.resolve(size.height) - lhsOp.y.resolve(size.height)) * animationProgress, UnitType::Point), ValueUnit(lhsOp.z.resolve(0) + (rhsOp.z.resolve(0) - lhsOp.z.resolve(0)) * animationProgress, UnitType::Point)}, size);