mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-06-06 16:32:24 +00:00
Implement useNativeDriver support for animation of transform style
This commit is contained in:
@@ -42,6 +42,7 @@ class GroupShadowNode extends RenderableShadowNode {
|
|||||||
void setupGlyphContext(Canvas canvas) {
|
void setupGlyphContext(Canvas canvas) {
|
||||||
RectF clipBounds = new RectF(canvas.getClipBounds());
|
RectF clipBounds = new RectF(canvas.getClipBounds());
|
||||||
mMatrix.mapRect(clipBounds);
|
mMatrix.mapRect(clipBounds);
|
||||||
|
mTransform.mapRect(clipBounds);
|
||||||
mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height());
|
mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,19 +28,19 @@ import com.facebook.imagepipeline.image.CloseableBitmap;
|
|||||||
import com.facebook.imagepipeline.image.CloseableImage;
|
import com.facebook.imagepipeline.image.CloseableImage;
|
||||||
import com.facebook.imagepipeline.request.ImageRequest;
|
import com.facebook.imagepipeline.request.ImageRequest;
|
||||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||||
|
import com.facebook.react.bridge.Dynamic;
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
import com.facebook.react.bridge.ReadableType;
|
||||||
import com.facebook.react.common.ReactConstants;
|
import com.facebook.react.common.ReactConstants;
|
||||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||||
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
|
|
||||||
import com.facebook.react.views.imagehelper.ImageSource;
|
import com.facebook.react.views.imagehelper.ImageSource;
|
||||||
|
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReadableArray;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shadow node for virtual Image view
|
* Shadow node for virtual Image view
|
||||||
*/
|
*/
|
||||||
@@ -127,9 +127,10 @@ class ImageShadowNode extends RenderableShadowNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ReactProp(name = "matrix")
|
@ReactProp(name = "matrix")
|
||||||
public void setMatrix(@Nullable ReadableArray matrixArray) {
|
public void setMatrix(Dynamic matrixArray) {
|
||||||
if (matrixArray != null) {
|
ReadableType type = matrixArray.getType();
|
||||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
if (!matrixArray.isNull() && type.equals(ReadableType.Array)) {
|
||||||
|
int matrixSize = PropHelper.toMatrixData(matrixArray.asArray(), sRawMatrix, mScale);
|
||||||
if (matrixSize == 6) {
|
if (matrixSize == 6) {
|
||||||
if (mMatrix == null) {
|
if (mMatrix == null) {
|
||||||
mMatrix = new Matrix();
|
mMatrix = new Matrix();
|
||||||
|
|||||||
@@ -9,18 +9,35 @@
|
|||||||
|
|
||||||
package com.horcrux.svg;
|
package com.horcrux.svg;
|
||||||
|
|
||||||
|
import android.graphics.Matrix;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.facebook.infer.annotation.Assertions;
|
||||||
import com.facebook.react.bridge.Dynamic;
|
import com.facebook.react.bridge.Dynamic;
|
||||||
import com.facebook.react.bridge.ReadableArray;
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||||
|
import com.facebook.react.uimanager.MatrixMathHelper;
|
||||||
|
import com.facebook.react.uimanager.PixelUtil;
|
||||||
import com.facebook.react.uimanager.ThemedReactContext;
|
import com.facebook.react.uimanager.ThemedReactContext;
|
||||||
|
import com.facebook.react.uimanager.TransformHelper;
|
||||||
import com.facebook.react.uimanager.ViewGroupManager;
|
import com.facebook.react.uimanager.ViewGroupManager;
|
||||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static com.facebook.react.uimanager.MatrixMathHelper.determinant;
|
||||||
|
import static com.facebook.react.uimanager.MatrixMathHelper.inverse;
|
||||||
|
import static com.facebook.react.uimanager.MatrixMathHelper.multiplyVectorByMatrix;
|
||||||
|
import static com.facebook.react.uimanager.MatrixMathHelper.roundTo3Places;
|
||||||
|
import static com.facebook.react.uimanager.MatrixMathHelper.transpose;
|
||||||
|
import static com.facebook.react.uimanager.MatrixMathHelper.v3Combine;
|
||||||
|
import static com.facebook.react.uimanager.MatrixMathHelper.v3Cross;
|
||||||
|
import static com.facebook.react.uimanager.MatrixMathHelper.v3Dot;
|
||||||
|
import static com.facebook.react.uimanager.MatrixMathHelper.v3Length;
|
||||||
|
import static com.facebook.react.uimanager.MatrixMathHelper.v3Normalize;
|
||||||
import static com.horcrux.svg.RenderableShadowNode.CAP_ROUND;
|
import static com.horcrux.svg.RenderableShadowNode.CAP_ROUND;
|
||||||
import static com.horcrux.svg.RenderableShadowNode.FILL_RULE_NONZERO;
|
import static com.horcrux.svg.RenderableShadowNode.FILL_RULE_NONZERO;
|
||||||
import static com.horcrux.svg.RenderableShadowNode.JOIN_ROUND;
|
import static com.horcrux.svg.RenderableShadowNode.JOIN_ROUND;
|
||||||
@@ -53,9 +70,207 @@ class RenderableViewManager<T extends VirtualNode> extends ViewGroupManager<Rend
|
|||||||
|
|
||||||
private final String mClassName;
|
private final String mClassName;
|
||||||
|
|
||||||
|
public static class MatrixDecompositionContext extends MatrixMathHelper.MatrixDecompositionContext {
|
||||||
|
double[] perspective = new double[4];
|
||||||
|
double[] scale = new double[3];
|
||||||
|
double[] skew = new double[3];
|
||||||
|
double[] translation = new double[3];
|
||||||
|
double[] rotationDegrees = new double[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MatrixDecompositionContext sMatrixDecompositionContext =
|
||||||
|
new MatrixDecompositionContext();
|
||||||
|
private static double[] sTransformDecompositionArray = new double[16];
|
||||||
|
|
||||||
|
private static final int PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX = 2;
|
||||||
|
private static final float CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER = 5;
|
||||||
|
|
||||||
|
private static final double EPSILON = .00001d;
|
||||||
|
|
||||||
|
private static boolean isZero(double d) {
|
||||||
|
if (Double.isNaN(d)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Math.abs(d) < EPSILON;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param transformMatrix 16-element array of numbers representing 4x4 transform matrix
|
||||||
|
*/
|
||||||
|
public static void decomposeMatrix(double[] transformMatrix, MatrixDecompositionContext ctx) {
|
||||||
|
Assertions.assertCondition(transformMatrix.length == 16);
|
||||||
|
|
||||||
|
// output values
|
||||||
|
final double[] perspective = ctx.perspective;
|
||||||
|
final double[] scale = ctx.scale;
|
||||||
|
final double[] skew = ctx.skew;
|
||||||
|
final double[] translation = ctx.translation;
|
||||||
|
final double[] rotationDegrees = ctx.rotationDegrees;
|
||||||
|
|
||||||
|
// create normalized, 2d array matrix
|
||||||
|
// and normalized 1d array perspectiveMatrix with redefined 4th column
|
||||||
|
if (isZero(transformMatrix[15])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double[][] matrix = new double[4][4];
|
||||||
|
double[] perspectiveMatrix = new double[16];
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
double value = transformMatrix[(i * 4) + j] / transformMatrix[15];
|
||||||
|
matrix[i][j] = value;
|
||||||
|
perspectiveMatrix[(i * 4) + j] = j == 3 ? 0 : value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
perspectiveMatrix[15] = 1;
|
||||||
|
|
||||||
|
// test for singularity of upper 3x3 part of the perspective matrix
|
||||||
|
if (isZero(determinant(perspectiveMatrix))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// isolate perspective
|
||||||
|
if (!isZero(matrix[0][3]) || !isZero(matrix[1][3]) || !isZero(matrix[2][3])) {
|
||||||
|
// rightHandSide is the right hand side of the equation.
|
||||||
|
// rightHandSide is a vector, or point in 3d space relative to the origin.
|
||||||
|
double[] rightHandSide = { matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3] };
|
||||||
|
|
||||||
|
// Solve the equation by inverting perspectiveMatrix and multiplying
|
||||||
|
// rightHandSide by the inverse.
|
||||||
|
double[] inversePerspectiveMatrix = inverse(
|
||||||
|
perspectiveMatrix
|
||||||
|
);
|
||||||
|
double[] transposedInversePerspectiveMatrix = transpose(
|
||||||
|
inversePerspectiveMatrix
|
||||||
|
);
|
||||||
|
multiplyVectorByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspective);
|
||||||
|
} else {
|
||||||
|
// no perspective
|
||||||
|
perspective[0] = perspective[1] = perspective[2] = 0d;
|
||||||
|
perspective[3] = 1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// translation is simple
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
translation[i] = matrix[3][i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now get scale and shear.
|
||||||
|
// 'row' is a 3 element array of 3 component vectors
|
||||||
|
double[][] row = new double[3][3];
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
row[i][0] = matrix[i][0];
|
||||||
|
row[i][1] = matrix[i][1];
|
||||||
|
row[i][2] = matrix[i][2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute X scale factor and normalize first row.
|
||||||
|
scale[0] = v3Length(row[0]);
|
||||||
|
row[0] = v3Normalize(row[0], scale[0]);
|
||||||
|
|
||||||
|
// Compute XY shear factor and make 2nd row orthogonal to 1st.
|
||||||
|
skew[0] = v3Dot(row[0], row[1]);
|
||||||
|
row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]);
|
||||||
|
|
||||||
|
// Compute XY shear factor and make 2nd row orthogonal to 1st.
|
||||||
|
skew[0] = v3Dot(row[0], row[1]);
|
||||||
|
row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]);
|
||||||
|
|
||||||
|
// Now, compute Y scale and normalize 2nd row.
|
||||||
|
scale[1] = v3Length(row[1]);
|
||||||
|
row[1] = v3Normalize(row[1], scale[1]);
|
||||||
|
skew[0] /= scale[1];
|
||||||
|
|
||||||
|
// Compute XZ and YZ shears, orthogonalize 3rd row
|
||||||
|
skew[1] = v3Dot(row[0], row[2]);
|
||||||
|
row[2] = v3Combine(row[2], row[0], 1.0, -skew[1]);
|
||||||
|
skew[2] = v3Dot(row[1], row[2]);
|
||||||
|
row[2] = v3Combine(row[2], row[1], 1.0, -skew[2]);
|
||||||
|
|
||||||
|
// Next, get Z scale and normalize 3rd row.
|
||||||
|
scale[2] = v3Length(row[2]);
|
||||||
|
row[2] = v3Normalize(row[2], scale[2]);
|
||||||
|
skew[1] /= scale[2];
|
||||||
|
skew[2] /= scale[2];
|
||||||
|
|
||||||
|
// At this point, the matrix (in rows) is orthonormal.
|
||||||
|
// Check for a coordinate system flip. If the determinant
|
||||||
|
// is -1, then negate the matrix and the scaling factors.
|
||||||
|
double[] pdum3 = v3Cross(row[1], row[2]);
|
||||||
|
if (v3Dot(row[0], pdum3) < 0) {
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
scale[i] *= -1;
|
||||||
|
row[i][0] *= -1;
|
||||||
|
row[i][1] *= -1;
|
||||||
|
row[i][2] *= -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, get the rotations out
|
||||||
|
// Based on: http://nghiaho.com/?page_id=846
|
||||||
|
double conv = 180 / Math.PI;
|
||||||
|
rotationDegrees[0] = roundTo3Places(-Math.atan2(row[2][1], row[2][2]) * conv);
|
||||||
|
rotationDegrees[1] = roundTo3Places(-Math.atan2(-row[2][0], Math.sqrt(row[2][1] * row[2][1] + row[2][2] * row[2][2])) * conv);
|
||||||
|
rotationDegrees[2] = roundTo3Places(-Math.atan2(row[1][0], row[0][0]) * conv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setTransformProperty(View view, ReadableArray transforms) {
|
||||||
|
TransformHelper.processTransform(transforms, sTransformDecompositionArray);
|
||||||
|
decomposeMatrix(sTransformDecompositionArray, sMatrixDecompositionContext);
|
||||||
|
view.setTranslationX(
|
||||||
|
PixelUtil.toPixelFromDIP((float) sMatrixDecompositionContext.translation[0]));
|
||||||
|
view.setTranslationY(
|
||||||
|
PixelUtil.toPixelFromDIP((float) sMatrixDecompositionContext.translation[1]));
|
||||||
|
view.setRotation((float) sMatrixDecompositionContext.rotationDegrees[2]);
|
||||||
|
view.setRotationX((float) sMatrixDecompositionContext.rotationDegrees[0]);
|
||||||
|
view.setRotationY((float) sMatrixDecompositionContext.rotationDegrees[1]);
|
||||||
|
view.setScaleX((float) sMatrixDecompositionContext.scale[0]);
|
||||||
|
view.setScaleY((float) sMatrixDecompositionContext.scale[1]);
|
||||||
|
|
||||||
|
double[] perspectiveArray = sMatrixDecompositionContext.perspective;
|
||||||
|
|
||||||
|
if (perspectiveArray.length > PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX) {
|
||||||
|
float invertedCameraDistance = (float) perspectiveArray[PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX];
|
||||||
|
if (invertedCameraDistance == 0) {
|
||||||
|
// Default camera distance, before scale multiplier (1280)
|
||||||
|
invertedCameraDistance = 0.00078125f;
|
||||||
|
}
|
||||||
|
float cameraDistance = -1 / invertedCameraDistance;
|
||||||
|
float scale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
|
||||||
|
|
||||||
|
// The following converts the matrix's perspective to a camera distance
|
||||||
|
// such that the camera perspective looks the same on Android and iOS
|
||||||
|
float normalizedCameraDistance = scale * cameraDistance * CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER;
|
||||||
|
view.setCameraDistance(normalizedCameraDistance);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void resetTransformProperty(View view) {
|
||||||
|
view.setTranslationX(PixelUtil.toPixelFromDIP(0));
|
||||||
|
view.setTranslationY(PixelUtil.toPixelFromDIP(0));
|
||||||
|
view.setRotation(0);
|
||||||
|
view.setRotationX(0);
|
||||||
|
view.setRotationY(0);
|
||||||
|
view.setScaleX(1);
|
||||||
|
view.setScaleY(1);
|
||||||
|
view.setCameraDistance(0);
|
||||||
|
}
|
||||||
|
|
||||||
static RenderableViewManager<GroupShadowNode> createGroupViewManager() {
|
static RenderableViewManager<GroupShadowNode> createGroupViewManager() {
|
||||||
return new RenderableViewManager<>(CLASS_GROUP);
|
return new RenderableViewManager<GroupShadowNode>(CLASS_GROUP){
|
||||||
|
|
||||||
|
@ReactProp(name = "transform")
|
||||||
|
public void setTransform(RenderableView<GroupShadowNode> node, ReadableArray matrix) {
|
||||||
|
|
||||||
|
if (matrix == null) {
|
||||||
|
resetTransformProperty(node);
|
||||||
|
} else {
|
||||||
|
setTransformProperty(node, matrix);
|
||||||
|
Matrix m = node.getMatrix();
|
||||||
|
node.shadowNode.mTransform = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static RenderableViewManager<PathShadowNode> createPathViewManager() {
|
static RenderableViewManager<PathShadowNode> createPathViewManager() {
|
||||||
@@ -221,11 +436,6 @@ class RenderableViewManager<T extends VirtualNode> extends ViewGroupManager<Rend
|
|||||||
public void setMeetOrSlice(RenderableView<ImageShadowNode> node, int meetOrSlice) {
|
public void setMeetOrSlice(RenderableView<ImageShadowNode> node, int meetOrSlice) {
|
||||||
node.shadowNode.setMeetOrSlice(meetOrSlice);
|
node.shadowNode.setMeetOrSlice(meetOrSlice);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactProp(name = "matrix")
|
|
||||||
public void setMatrix(RenderableView<ImageShadowNode> node, @Nullable ReadableArray matrixArray) {
|
|
||||||
node.shadowNode.setMatrix(matrixArray);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,6 +978,11 @@ class RenderableViewManager<T extends VirtualNode> extends ViewGroupManager<Rend
|
|||||||
node.shadowNode.setStrokeLinejoin(strokeLinejoin);
|
node.shadowNode.setStrokeLinejoin(strokeLinejoin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ReactProp(name = "matrix")
|
||||||
|
public void setMatrix(RenderableView<RenderableShadowNode> node, Dynamic matrixArray) {
|
||||||
|
node.shadowNode.setMatrix(matrixArray);
|
||||||
|
}
|
||||||
|
|
||||||
@ReactProp(name = "propList")
|
@ReactProp(name = "propList")
|
||||||
public void setPropList(RenderableView<RenderableShadowNode> node, @Nullable ReadableArray propList) {
|
public void setPropList(RenderableView<RenderableShadowNode> node, @Nullable ReadableArray propList) {
|
||||||
node.shadowNode.setPropList(propList);
|
node.shadowNode.setPropList(propList);
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ import android.graphics.RectF;
|
|||||||
import android.graphics.Region;
|
import android.graphics.Region;
|
||||||
|
|
||||||
import com.facebook.common.logging.FLog;
|
import com.facebook.common.logging.FLog;
|
||||||
|
import com.facebook.react.bridge.Dynamic;
|
||||||
import com.facebook.react.bridge.ReadableArray;
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
|
import com.facebook.react.bridge.ReadableType;
|
||||||
import com.facebook.react.common.ReactConstants;
|
import com.facebook.react.common.ReactConstants;
|
||||||
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||||
@@ -54,6 +56,7 @@ abstract class VirtualNode extends LayoutShadowNode {
|
|||||||
};
|
};
|
||||||
float mOpacity = 1f;
|
float mOpacity = 1f;
|
||||||
Matrix mMatrix = new Matrix();
|
Matrix mMatrix = new Matrix();
|
||||||
|
Matrix mTransform = new Matrix();
|
||||||
Matrix mInvMatrix = new Matrix();
|
Matrix mInvMatrix = new Matrix();
|
||||||
boolean mInvertible = true;
|
boolean mInvertible = true;
|
||||||
private RectF mClientRect;
|
private RectF mClientRect;
|
||||||
@@ -188,6 +191,7 @@ abstract class VirtualNode extends LayoutShadowNode {
|
|||||||
int saveAndSetupCanvas(Canvas canvas) {
|
int saveAndSetupCanvas(Canvas canvas) {
|
||||||
int count = canvas.save();
|
int count = canvas.save();
|
||||||
canvas.concat(mMatrix);
|
canvas.concat(mMatrix);
|
||||||
|
canvas.concat(mTransform);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,9 +238,10 @@ abstract class VirtualNode extends LayoutShadowNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ReactProp(name = "matrix")
|
@ReactProp(name = "matrix")
|
||||||
public void setMatrix(@Nullable ReadableArray matrixArray) {
|
public void setMatrix(Dynamic matrixArray) {
|
||||||
if (matrixArray != null) {
|
ReadableType type = matrixArray.getType();
|
||||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
if (!matrixArray.isNull() && type.equals(ReadableType.Array)) {
|
||||||
|
int matrixSize = PropHelper.toMatrixData(matrixArray.asArray(), sRawMatrix, mScale);
|
||||||
if (matrixSize == 6) {
|
if (matrixSize == 6) {
|
||||||
if (mMatrix == null) {
|
if (mMatrix == null) {
|
||||||
mMatrix = new Matrix();
|
mMatrix = new Matrix();
|
||||||
@@ -250,6 +255,7 @@ abstract class VirtualNode extends LayoutShadowNode {
|
|||||||
} else {
|
} else {
|
||||||
mMatrix = null;
|
mMatrix = null;
|
||||||
mInvMatrix = null;
|
mInvMatrix = null;
|
||||||
|
mInvertible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
super.markUpdated();
|
super.markUpdated();
|
||||||
|
|||||||
@@ -81,6 +81,8 @@
|
|||||||
{
|
{
|
||||||
CGRect clipBounds = CGContextGetClipBoundingBox(context);
|
CGRect clipBounds = CGContextGetClipBoundingBox(context);
|
||||||
clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix);
|
clipBounds = CGRectApplyAffineTransform(clipBounds, self.matrix);
|
||||||
|
CGAffineTransform transform = CATransform3DGetAffineTransform(self.layer.transform);
|
||||||
|
clipBounds = CGRectApplyAffineTransform(clipBounds, transform);
|
||||||
CGFloat width = CGRectGetWidth(clipBounds);
|
CGFloat width = CGRectGetWidth(clipBounds);
|
||||||
CGFloat height = CGRectGetHeight(clipBounds);
|
CGFloat height = CGRectGetHeight(clipBounds);
|
||||||
|
|
||||||
|
|||||||
@@ -171,6 +171,8 @@ UInt32 saturate(double value) {
|
|||||||
// This needs to be painted on a layer before being composited.
|
// This needs to be painted on a layer before being composited.
|
||||||
CGContextSaveGState(context);
|
CGContextSaveGState(context);
|
||||||
CGContextConcatCTM(context, self.matrix);
|
CGContextConcatCTM(context, self.matrix);
|
||||||
|
CGAffineTransform transform = CATransform3DGetAffineTransform(self.layer.transform);
|
||||||
|
CGContextConcatCTM(context, transform);
|
||||||
CGContextSetAlpha(context, self.opacity);
|
CGContextSetAlpha(context, self.opacity);
|
||||||
|
|
||||||
[self beginTransparencyLayer:context];
|
[self beginTransparencyLayer:context];
|
||||||
|
|||||||
@@ -10,8 +10,134 @@
|
|||||||
|
|
||||||
#import "RNSVGNode.h"
|
#import "RNSVGNode.h"
|
||||||
|
|
||||||
|
static const NSUInteger kMatrixArrayLength = 4 * 4;
|
||||||
|
|
||||||
@implementation RNSVGNodeManager
|
@implementation RNSVGNodeManager
|
||||||
|
|
||||||
|
+ (CGFloat)convertToRadians:(id)json
|
||||||
|
{
|
||||||
|
if ([json isKindOfClass:[NSString class]]) {
|
||||||
|
NSString *stringValue = (NSString *)json;
|
||||||
|
if ([stringValue hasSuffix:@"deg"]) {
|
||||||
|
CGFloat degrees = [[stringValue substringToIndex:stringValue.length - 3] floatValue];
|
||||||
|
return degrees * M_PI / 180;
|
||||||
|
}
|
||||||
|
if ([stringValue hasSuffix:@"rad"]) {
|
||||||
|
return [[stringValue substringToIndex:stringValue.length - 3] floatValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [json floatValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (CATransform3D)CATransform3DFromMatrix:(id)json
|
||||||
|
{
|
||||||
|
CATransform3D transform = CATransform3DIdentity;
|
||||||
|
if (!json) {
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
if (![json isKindOfClass:[NSArray class]]) {
|
||||||
|
RCTLogConvertError(json, @"a CATransform3D. Expected array for transform matrix.");
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
if ([json count] != kMatrixArrayLength) {
|
||||||
|
RCTLogConvertError(json, @"a CATransform3D. Expected 4x4 matrix array.");
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
for (NSUInteger i = 0; i < kMatrixArrayLength; i++) {
|
||||||
|
((CGFloat *)&transform)[i] = [RCTConvert CGFloat:json[i]];
|
||||||
|
}
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (CATransform3D)CATransform3D:(id)json
|
||||||
|
{
|
||||||
|
CATransform3D transform = CATransform3DIdentity;
|
||||||
|
if (!json) {
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
if (![json isKindOfClass:[NSArray class]]) {
|
||||||
|
RCTLogConvertError(json, @"a CATransform3D. Did you pass something other than an array?");
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
// legacy matrix support
|
||||||
|
if ([(NSArray *)json count] == kMatrixArrayLength && [json[0] isKindOfClass:[NSNumber class]]) {
|
||||||
|
RCTLogWarn(@"[RCTConvert CATransform3D:] has deprecated a matrix as input. Pass an array of configs (which can contain a matrix key) instead.");
|
||||||
|
return [self CATransform3DFromMatrix:json];
|
||||||
|
}
|
||||||
|
|
||||||
|
CGFloat zeroScaleThreshold = FLT_EPSILON;
|
||||||
|
|
||||||
|
for (NSDictionary *transformConfig in (NSArray<NSDictionary *> *)json) {
|
||||||
|
if (transformConfig.count != 1) {
|
||||||
|
RCTLogConvertError(json, @"a CATransform3D. You must specify exactly one property per transform object.");
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
NSString *property = transformConfig.allKeys[0];
|
||||||
|
id value = transformConfig[property];
|
||||||
|
|
||||||
|
if ([property isEqualToString:@"matrix"]) {
|
||||||
|
transform = [self CATransform3DFromMatrix:value];
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"perspective"]) {
|
||||||
|
transform.m34 = -1 / [value floatValue];
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"rotateX"]) {
|
||||||
|
CGFloat rotate = [self convertToRadians:value];
|
||||||
|
transform = CATransform3DRotate(transform, rotate, 1, 0, 0);
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"rotateY"]) {
|
||||||
|
CGFloat rotate = [self convertToRadians:value];
|
||||||
|
transform = CATransform3DRotate(transform, rotate, 0, 1, 0);
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"rotate"] || [property isEqualToString:@"rotateZ"]) {
|
||||||
|
CGFloat rotate = [self convertToRadians:value];
|
||||||
|
transform = CATransform3DRotate(transform, rotate, 0, 0, 1);
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"scale"]) {
|
||||||
|
CGFloat scale = [value floatValue];
|
||||||
|
scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale;
|
||||||
|
transform = CATransform3DScale(transform, scale, scale, 1);
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"scaleX"]) {
|
||||||
|
CGFloat scale = [value floatValue];
|
||||||
|
scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale;
|
||||||
|
transform = CATransform3DScale(transform, scale, 1, 1);
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"scaleY"]) {
|
||||||
|
CGFloat scale = [value floatValue];
|
||||||
|
scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale;
|
||||||
|
transform = CATransform3DScale(transform, 1, scale, 1);
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"translate"]) {
|
||||||
|
NSArray *array = (NSArray<NSNumber *> *)value;
|
||||||
|
CGFloat translateX = [array[0] floatValue];
|
||||||
|
CGFloat translateY = [array[1] floatValue];
|
||||||
|
CGFloat translateZ = array.count > 2 ? [array[2] floatValue] : 0;
|
||||||
|
transform = CATransform3DTranslate(transform, translateX, translateY, translateZ);
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"translateX"]) {
|
||||||
|
CGFloat translate = [value floatValue];
|
||||||
|
transform = CATransform3DTranslate(transform, translate, 0, 0);
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"translateY"]) {
|
||||||
|
CGFloat translate = [value floatValue];
|
||||||
|
transform = CATransform3DTranslate(transform, 0, translate, 0);
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"skewX"]) {
|
||||||
|
CGFloat skew = [self convertToRadians:value];
|
||||||
|
transform.m21 = tanf(skew);
|
||||||
|
|
||||||
|
} else if ([property isEqualToString:@"skewY"]) {
|
||||||
|
CGFloat skew = [self convertToRadians:value];
|
||||||
|
transform.m12 = tanf(skew);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
RCTLogError(@"Unsupported transform type for a CATransform3D: %@.", property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
|
||||||
RCT_EXPORT_MODULE()
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
- (RNSVGNode *)node
|
- (RNSVGNode *)node
|
||||||
@@ -27,6 +153,13 @@ RCT_EXPORT_MODULE()
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(name, NSString)
|
RCT_EXPORT_VIEW_PROPERTY(name, NSString)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat)
|
RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(matrix, CGAffineTransform)
|
RCT_EXPORT_VIEW_PROPERTY(matrix, CGAffineTransform)
|
||||||
|
RCT_CUSTOM_VIEW_PROPERTY(transform, CATransform3D, RNSVGNode)
|
||||||
|
{
|
||||||
|
view.layer.transform = json ? [RNSVGNodeManager CATransform3D:json] : defaultView.layer.transform;
|
||||||
|
// TODO: Improve this by enabling edge antialiasing only for transforms with rotation or skewing
|
||||||
|
view.layer.allowsEdgeAntialiasing = !CATransform3DIsIdentity(view.layer.transform);
|
||||||
|
[view invalidate];
|
||||||
|
}
|
||||||
RCT_EXPORT_VIEW_PROPERTY(mask, NSString)
|
RCT_EXPORT_VIEW_PROPERTY(mask, NSString)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(clipPath, NSString)
|
RCT_EXPORT_VIEW_PROPERTY(clipPath, NSString)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule)
|
RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule)
|
||||||
|
|||||||
Reference in New Issue
Block a user