mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
0975e96d53
Summary:
This diff fixes overflowInset calculation when a shadow node has transform matrix from a transfrom prop in JS. Specifically, this fixed the use case when transform directly used on a view component. When using Animated.View, it will create an invisible wrapper which will behave correctly with existing logic. This diff bring both use cases to work properly.
When a shadow node has transform on it, it will affect the overflowInset values for its parent nodes, but won't affect its own or any of its child nodes overflowInset values. This is obvious for translateX/Y case, but not for scale case. Take a look at the following case:
```
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│Original Layout │ │ Translate AB │ │ Scale AB │
└────────────────┘ └────────────────┘ └────────────────┘
─────▶ ◀───── ─────▶
┌ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ┐ ┌ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ┬──────────┐─ ─ ─ ─ ─ ┐
│ A │ │ A │ │ A │
│ │ │ │ │ │ │ │ ├ ─ ─ ─ ─ ─ ┼ ─ ─┌─────┤─ ─ ─ ─ ─ ┤
─ ─ ─ ─│─ ─ ─┌───┐┼ ─ ─ ─ ─ │ │ ◀─ ─ ─ │ │AB │ ─ ─ ─▶
│ │ │AB ││ │ │ ┌ ─ ─ ┼ ─ ─ ─ ┬──┴┬ ─ ─ ─ ─ ┤ │ │ │ │ │
└─────┤ ├┘ └───────┤AB │ └────┤ │
│ │┌──┴─────────┤ │ │ │ │ │ │ │ │ ┌───┴──────────┤
││ABC │ │┌──┴─────────┐ │ │ABC │
│ │└──┬─────────┤ │ │ │ ││ABC │ │ │ │ │ │ │
┌───ABD───────┴─┐ │ │ │└──┬─────────┘ │ ▼ │ └───┬──────────┘
├─────────────┬─┘ │ │ │ │ ├───ABD───────┴─┐ │ │ │ ├────────────────┴──┐ │ │
─ ─ ─ ─ ─ ─ ─└───┘─ ─ ─ ─ ─ ▼ └─────────────┬─┘ │ ▼ │ ABD │ │
└ ┴ ─ ─ ─ ─ ─ ─ ┴───┴ ─ ─ ─ ─ ┘ ├────────────────┬──┘ │ │
─ ─ ─ ─ ─ ─ ─ ─ ┴─────┴ ─ ─ ─ ─ ─
```
For the translate case, only view A has change on the overflowInset values for `right` and `bottom`. Note that the `left` and `top` are not changed as we union before and after transform is applied.
For the scale case, similar things are happening for view A, and both `left`, `right`, and `bottom` values are increased. However, for View AB or any of its children, they only *appear* to be increased, but that is purely cosmetic as it's caused by transform. The actual values are not changed, which will later be converted during render phase to actual pixels on screen.
In summary, overflowInset is affected from child nodes transform matrix to the current node (bottom up), but not from transform matrix on the current node to child nodes (top down). So the correct way to apply transform is to make it only affect calculating `contentFrame` during layout, which collects child nodes layout information and their transforms. The `contentFrame` is then used to decide the overflowInset values for the parent node. The current transform matrix on parent node is never used as it's not affecting overflowInset for the current node or its child nodes.
This diff reflects the context above with added unit test to cover the scale and translate transform matrix.
Changelog:
[Android/IOS][Fixed] - Fixed how we calculate overflowInset with transform matrix
Reviewed By: sammy-SC
Differential Revision: D34433404
fbshipit-source-id: 0e48e4af4cfd5a6dd32a30e7667686e8ef1a7004
221 lines
5.0 KiB
C++
221 lines
5.0 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <vector>
|
|
|
|
#include <folly/Hash.h>
|
|
#include <react/renderer/graphics/Float.h>
|
|
#include <react/renderer/graphics/Geometry.h>
|
|
|
|
#ifdef ANDROID
|
|
#include <folly/dynamic.h>
|
|
#endif
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
inline bool isZero(Float n) {
|
|
// We use this ternary expression instead of abs, fabsf, etc, because
|
|
// Float can be double or float depending on compilation target.
|
|
return (n < 0 ? n * (-1) : n) < 0.00001;
|
|
}
|
|
|
|
/**
|
|
* Defines operations used to construct a transform matrix.
|
|
* An "Arbitrary" operation means that the transform was seeded with some
|
|
* arbitrary initial result.
|
|
*/
|
|
enum class TransformOperationType {
|
|
Arbitrary,
|
|
Identity,
|
|
Perspective,
|
|
Scale,
|
|
Translate,
|
|
Rotate,
|
|
Skew
|
|
};
|
|
struct TransformOperation {
|
|
TransformOperationType type;
|
|
Float x;
|
|
Float y;
|
|
Float z;
|
|
};
|
|
|
|
/*
|
|
* Defines transform matrix to apply affine transformations.
|
|
*/
|
|
struct Transform {
|
|
std::vector<TransformOperation> operations{};
|
|
|
|
std::array<Float, 16> matrix{
|
|
{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}};
|
|
|
|
/**
|
|
* For debugging only. Prints out the matrix.
|
|
*/
|
|
#ifdef RN_DEBUG_STRING_CONVERTIBLE
|
|
static void print(Transform const &t, std::string prefix);
|
|
#endif
|
|
|
|
/*
|
|
* Given a TransformOperation, return the proper transform.
|
|
*/
|
|
static Transform FromTransformOperation(
|
|
TransformOperation transformOperation);
|
|
static TransformOperation DefaultTransformOperation(
|
|
TransformOperationType type);
|
|
|
|
/*
|
|
* Returns the identity transform (`[1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]`).
|
|
*/
|
|
static Transform Identity();
|
|
|
|
/*
|
|
* Returns a Perspective transform.
|
|
*/
|
|
static Transform Perspective(Float perspective);
|
|
|
|
/*
|
|
* Returns a Scale transform.
|
|
*/
|
|
static Transform Scale(Float factorX, Float factorY, Float factorZ);
|
|
|
|
/*
|
|
* Returns a Translate transform.
|
|
*/
|
|
static Transform Translate(Float x, Float y, Float z);
|
|
|
|
/*
|
|
* Returns a Skew transform.
|
|
*/
|
|
static Transform Skew(Float x, Float y);
|
|
|
|
/*
|
|
* Returns a transform that rotates by `angle` radians along the given axis.
|
|
*/
|
|
static Transform RotateX(Float angle);
|
|
static Transform RotateY(Float angle);
|
|
static Transform RotateZ(Float angle);
|
|
static Transform Rotate(Float angleX, Float angleY, Float angleZ);
|
|
|
|
/**
|
|
* Perform an interpolation between lhs and rhs, given progress.
|
|
* This first decomposes the matrices into translation, scale, and rotation,
|
|
* performs slerp between the two rotations, and a linear interpolation
|
|
* of scale and translation.
|
|
*
|
|
* @param progress
|
|
* @param lhs
|
|
* @param rhs
|
|
* @return
|
|
*/
|
|
static Transform Interpolate(
|
|
Float animationProgress,
|
|
Transform const &lhs,
|
|
Transform const &rhs);
|
|
|
|
/*
|
|
* Equality operators.
|
|
*/
|
|
bool operator==(Transform const &rhs) const;
|
|
bool operator!=(Transform const &rhs) const;
|
|
|
|
/*
|
|
* Matrix subscript.
|
|
*/
|
|
Float &at(int x, int y);
|
|
Float const &at(int x, int y) const;
|
|
|
|
/*
|
|
* Concatenates (multiplies) transform matrices.
|
|
*/
|
|
Transform operator*(Transform const &rhs) const;
|
|
|
|
/**
|
|
* Convert to folly::dynamic.
|
|
*/
|
|
#ifdef ANDROID
|
|
operator folly::dynamic() const {
|
|
return folly::dynamic::array(
|
|
matrix[0],
|
|
matrix[1],
|
|
matrix[2],
|
|
matrix[3],
|
|
matrix[4],
|
|
matrix[5],
|
|
matrix[6],
|
|
matrix[7],
|
|
matrix[8],
|
|
matrix[9],
|
|
matrix[10],
|
|
matrix[11],
|
|
matrix[12],
|
|
matrix[13],
|
|
matrix[14],
|
|
matrix[15]);
|
|
}
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
* Applies tranformation to the given point.
|
|
*/
|
|
Point operator*(Point const &point, Transform const &transform);
|
|
|
|
/*
|
|
* Applies tranformation to the given size.
|
|
*/
|
|
Size operator*(Size const &size, Transform const &transform);
|
|
|
|
/*
|
|
* Applies tranformation to the given rect.
|
|
* ONLY SUPPORTS scale and translation transformation.
|
|
*/
|
|
Rect operator*(Rect const &rect, Transform const &transform);
|
|
|
|
/*
|
|
* Applies tranformation to the given EdgeInsets.
|
|
* ONLY SUPPORTS scale transformation.
|
|
*/
|
|
EdgeInsets operator*(EdgeInsets const &edgeInsets, Transform const &transform);
|
|
|
|
Vector operator*(Transform const &transform, Vector const &vector);
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|
|
|
|
namespace std {
|
|
|
|
template <>
|
|
struct hash<facebook::react::Transform> {
|
|
size_t operator()(const facebook::react::Transform &transform) const {
|
|
return folly::hash::hash_combine(
|
|
0,
|
|
transform.matrix[0],
|
|
transform.matrix[1],
|
|
transform.matrix[2],
|
|
transform.matrix[3],
|
|
transform.matrix[4],
|
|
transform.matrix[5],
|
|
transform.matrix[6],
|
|
transform.matrix[7],
|
|
transform.matrix[8],
|
|
transform.matrix[9],
|
|
transform.matrix[10],
|
|
transform.matrix[11],
|
|
transform.matrix[12],
|
|
transform.matrix[13],
|
|
transform.matrix[14],
|
|
transform.matrix[15]);
|
|
}
|
|
};
|
|
|
|
} // namespace std
|