Implement useNativeDriver support for animation of transform style

This commit is contained in:
Mikael Sand
2018-10-11 07:09:12 +03:00
parent a87096d3ee
commit fb4e877c2b
7 changed files with 375 additions and 15 deletions
@@ -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();
+2
View File
@@ -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);
+2
View File
@@ -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];
+133
View File
@@ -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)