fix: Make native methods synchronous

This commit is contained in:
Mikael Sand
2019-10-05 18:43:00 +03:00
parent 409af915ea
commit 8ce76113e1
3 changed files with 280 additions and 383 deletions
@@ -16,12 +16,10 @@ import android.graphics.RectF;
import android.graphics.Region; import android.graphics.Region;
import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.WritableMap;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@@ -37,106 +35,97 @@ class RNSVGRenderableManager extends ReactContextBaseJavaModule {
return "RNSVGRenderableManager"; return "RNSVGRenderableManager";
} }
private static void isPointInFill(final int tag, final float[] src, final Callback successCallback, final int attempt) {
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
if (attempt < 1) {
RenderableViewManager.runWhenViewIsAvailable(tag, new Runnable() {
@Override
public void run() {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) { // Should never happen
successCallback.invoke(false);
return;
}
isPointInFill(tag, src, successCallback, attempt + 1);
}
});
} else {
successCallback.invoke(false);
}
} else {
float scale = svg.mScale;
src[0] *= scale;
src[1] *= scale;
int i = svg.hitTest(src);
successCallback.invoke(i != -1);
}
}
}
);
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ReactMethod @ReactMethod(isBlockingSynchronousMethod = true)
public void isPointInFill(int tag, ReadableMap options, Callback successCallback) { public boolean isPointInFill(int tag, ReadableMap options) {
float x = (float) options.getDouble("x");
float y = (float) options.getDouble("y");
float[] src = new float[]{x, y};
isPointInFill(tag, src, successCallback, 0);
}
@SuppressWarnings("unused")
@ReactMethod
public void isPointInStroke(int tag, ReadableMap options, Callback successCallback) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) { if (svg == null) {
successCallback.invoke(false); return false;
return;
} }
float scale = svg.mScale;
float x = (float) options.getDouble("x") * scale;
float y = (float) options.getDouble("y") * scale;
int i = svg.hitTest(new float[]{x, y});
return i != -1;
}
@SuppressWarnings("unused")
@ReactMethod(isBlockingSynchronousMethod = true)
public boolean isPointInStroke(int tag, ReadableMap options) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return false;
}
svg.getPath(null, null); svg.getPath(null, null);
svg.initBounds(); svg.initBounds();
Region strokeRegion = svg.mStrokeRegion;
float scale = svg.mScale; float scale = svg.mScale;
int x = (int) (options.getDouble("x") * scale); int x = (int) (options.getDouble("x") * scale);
int y = (int) (options.getDouble("y") * scale); int y = (int) (options.getDouble("y") * scale);
boolean hit = strokeRegion != null && strokeRegion.contains(x, y);
successCallback.invoke(hit); Region strokeRegion = svg.mStrokeRegion;
return strokeRegion != null && strokeRegion.contains(x, y);
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ReactMethod @ReactMethod(isBlockingSynchronousMethod = true)
public void getTotalLength(int tag, Callback successCallback) { public float getTotalLength(int tag) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
PathMeasure pm = new PathMeasure(svg.getPath(null, null), false); if (svg == null) {
float scale = svg.mScale; return 0;
successCallback.invoke(pm.getLength() / scale); }
Path path = svg.getPath(null, null);
PathMeasure pm = new PathMeasure(path, false);
return pm.getLength() / svg.mScale;
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ReactMethod @ReactMethod(isBlockingSynchronousMethod = true)
public void getPointAtLength(int tag, ReadableMap options, Callback successCallback) { public WritableMap getPointAtLength(int tag, ReadableMap options) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return null;
}
Path path = svg.getPath(null, null);
PathMeasure pm = new PathMeasure(path, false);
float length = (float) options.getDouble("length"); float length = (float) options.getDouble("length");
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); float scale = svg.mScale;
PathMeasure pm = new PathMeasure(svg.getPath(null, null), false);
float pathLength = pm.getLength();
float[] pos = new float[2]; float[] pos = new float[2];
float[] tan = new float[2]; float[] tan = new float[2];
pm.getPosTan(Math.max(0, Math.min(length, pathLength)), pos, tan); float distance = Math.max(0, Math.min(length, pm.getLength()));
pm.getPosTan(distance, pos, tan);
double angle = Math.atan2(tan[1], tan[0]); double angle = Math.atan2(tan[1], tan[0]);
WritableMap result = Arguments.createMap(); WritableMap result = Arguments.createMap();
float scale = svg.mScale;
result.putDouble("x", pos[0] / scale); result.putDouble("x", pos[0] / scale);
result.putDouble("y", pos[1] / scale); result.putDouble("y", pos[1] / scale);
result.putDouble("angle", angle); result.putDouble("angle", angle);
successCallback.invoke(result); return result;
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ReactMethod @ReactMethod(isBlockingSynchronousMethod = true)
public void getBBox(int tag, ReadableMap options, Callback successCallback) { public WritableMap getBBox(int tag, ReadableMap options) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
if (svg == null) {
return null;
}
boolean fill = options.getBoolean("fill"); boolean fill = options.getBoolean("fill");
boolean stroke = options.getBoolean("stroke"); boolean stroke = options.getBoolean("stroke");
boolean markers = options.getBoolean("markers"); boolean markers = options.getBoolean("markers");
boolean clipped = options.getBoolean("clipped"); boolean clipped = options.getBoolean("clipped");
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
Path path = svg.getPath(null, null); Path path = svg.getPath(null, null);
float scale = svg.mScale;
svg.initBounds(); svg.initBounds();
RectF bounds = new RectF(); RectF bounds = new RectF();
if (fill) { if (fill) {
bounds.union(svg.mFillBounds); bounds.union(svg.mFillBounds);
@@ -153,26 +142,30 @@ class RNSVGRenderableManager extends ReactContextBaseJavaModule {
bounds.intersect(svg.mClipBounds); bounds.intersect(svg.mClipBounds);
} }
} }
WritableMap result = Arguments.createMap(); WritableMap result = Arguments.createMap();
float scale = svg.mScale;
result.putDouble("x", bounds.left / scale); result.putDouble("x", bounds.left / scale);
result.putDouble("y", bounds.top / scale); result.putDouble("y", bounds.top / scale);
result.putDouble("width", bounds.width() / scale); result.putDouble("width", bounds.width() / scale);
result.putDouble("height", bounds.height() / scale); result.putDouble("height", bounds.height() / scale);
successCallback.invoke(result); return result;
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ReactMethod @ReactMethod(isBlockingSynchronousMethod = true)
public void getCTM(int tag, Callback successCallback) { public WritableMap getCTM(int tag) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
Matrix screenCTM = svg.mCTM; if (svg == null) {
Matrix invViewBox = svg.getSvgView().mInvViewBoxMatrix; return null;
Matrix ctm = new Matrix(screenCTM); }
ctm.preConcat(invViewBox);
Matrix ctm = new Matrix(svg.mCTM);
Matrix invViewBoxMatrix = svg.getSvgView().mInvViewBoxMatrix;
ctm.preConcat(invViewBoxMatrix);
float[] values = new float[9]; float[] values = new float[9];
ctm.getValues(values); ctm.getValues(values);
WritableMap result = Arguments.createMap(); WritableMap result = Arguments.createMap();
result.putDouble("a", values[Matrix.MSCALE_X]); result.putDouble("a", values[Matrix.MSCALE_X]);
result.putDouble("b", values[Matrix.MSKEW_Y]); result.putDouble("b", values[Matrix.MSKEW_Y]);
@@ -180,18 +173,23 @@ class RNSVGRenderableManager extends ReactContextBaseJavaModule {
result.putDouble("d", values[Matrix.MSCALE_Y]); result.putDouble("d", values[Matrix.MSCALE_Y]);
result.putDouble("e", values[Matrix.MTRANS_X]); result.putDouble("e", values[Matrix.MTRANS_X]);
result.putDouble("f", values[Matrix.MTRANS_Y]); result.putDouble("f", values[Matrix.MTRANS_Y]);
successCallback.invoke(result); return result;
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ReactMethod @ReactMethod(isBlockingSynchronousMethod = true)
public void getScreenCTM(int tag, Callback successCallback) { public WritableMap getScreenCTM(int tag) {
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
Matrix screenCTM = svg.mCTM; if (svg == null) {
return null;
}
float[] values = new float[9];
svg.mCTM.getValues(values);
SvgView root = svg.getSvgView(); SvgView root = svg.getSvgView();
float scale = svg.mScale; float scale = svg.mScale;
float[] values = new float[9];
screenCTM.getValues(values);
WritableMap result = Arguments.createMap(); WritableMap result = Arguments.createMap();
result.putDouble("a", values[Matrix.MSCALE_X]); result.putDouble("a", values[Matrix.MSCALE_X]);
result.putDouble("b", values[Matrix.MSKEW_Y]); result.putDouble("b", values[Matrix.MSKEW_Y]);
@@ -199,6 +197,6 @@ class RNSVGRenderableManager extends ReactContextBaseJavaModule {
result.putDouble("d", values[Matrix.MSCALE_Y]); result.putDouble("d", values[Matrix.MSCALE_Y]);
result.putDouble("e", values[Matrix.MTRANS_X] + root.getLeft() / scale); result.putDouble("e", values[Matrix.MTRANS_X] + root.getLeft() / scale);
result.putDouble("f", values[Matrix.MTRANS_Y] + root.getTop() / scale); result.putDouble("f", values[Matrix.MTRANS_Y] + root.getTop() / scale);
successCallback.invoke(result); return result;
} }
} }
+166 -192
View File
@@ -38,237 +38,211 @@ RCT_EXPORT_VIEW_PROPERTY(strokeMiterlimit, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(vectorEffect, int) RCT_EXPORT_VIEW_PROPERTY(vectorEffect, int)
RCT_EXPORT_VIEW_PROPERTY(propList, NSArray<NSString *>) RCT_EXPORT_VIEW_PROPERTY(propList, NSArray<NSString *>)
typedef void (^RNSVGSuccessBlock)(RNSVGRenderable *view); RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isPointInFill:(nonnull NSNumber *)reactTag options:(NSDictionary *)options)
typedef void (^RNSVGFailBlock)(void);
- (void)withTag:(nonnull NSNumber *)reactTag success:(RNSVGSuccessBlock)successBlock fail:(RNSVGFailBlock)failBlock attempt:(int)attempt {
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
__kindof UIView *view = viewRegistry[reactTag];
if (!view) {
if (attempt < 1) {
void (^retryBlock)(void) = ^{
[self withTag:reactTag success:successBlock fail:failBlock attempt:(attempt + 1)];
};
RCTExecuteOnUIManagerQueue(retryBlock);
} else {
failBlock();
}
} else if ([view isKindOfClass:[RNSVGRenderable class]]) {
RNSVGRenderable *svg = view;
successBlock(svg);
} else {
RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view);
failBlock();
}
}];
}
RCT_EXPORT_METHOD(isPointInFill:(nonnull NSNumber *)reactTag options:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback)
{ {
__block UIView *view;
dispatch_sync(dispatch_get_main_queue(), ^{
view = [self.bridge.uiManager viewForReactTag:reactTag];
});
if (![view isKindOfClass:[RNSVGRenderable class]]) {
RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view);
return [NSNumber numberWithBool:false];
}
if (options == nil) { if (options == nil) {
RCTLogError(@"Invalid options given to isPointInFill, got: %@", options); RCTLogError(@"Invalid options given to isPointInFill, got: %@", options);
callback(@[[NSNumber numberWithBool:false]]); return [NSNumber numberWithBool:false];
return;
} }
id xo = [options objectForKey:@"x"]; id xo = [options objectForKey:@"x"];
id yo = [options objectForKey:@"y"]; id yo = [options objectForKey:@"y"];
if (![xo isKindOfClass:NSNumber.class] || if (![xo isKindOfClass:NSNumber.class] ||
![yo isKindOfClass:NSNumber.class]) { ![yo isKindOfClass:NSNumber.class]) {
RCTLogError(@"Invalid x or y given to isPointInFill"); RCTLogError(@"Invalid x or y given to isPointInFill");
callback(@[[NSNumber numberWithBool:false]]); return [NSNumber numberWithBool:false];
return;
} }
CGFloat x = (CGFloat)[xo floatValue]; RNSVGRenderable *svg = (RNSVGRenderable *)view;
CGFloat y = (CGFloat)[yo floatValue]; CGFloat x = (CGFloat)[xo doubleValue];
CGFloat y = (CGFloat)[yo doubleValue];
CGPoint point = CGPointMake(x, y); CGPoint point = CGPointMake(x, y);
[self UIView *target = [svg hitTest:point withEvent:nil];
withTag:reactTag BOOL hit = target != nil;
success:^(RNSVGRenderable *svg){ return [NSNumber numberWithBool:hit];
UIView *target = [svg hitTest:point withEvent:nil];
BOOL hit = target != nil;
callback(@[[NSNumber numberWithBool:hit]]);
}
fail:^{
callback(@[[NSNumber numberWithBool:false]]);
}
attempt:0];
} }
RCT_EXPORT_METHOD(isPointInStroke:(nonnull NSNumber *)reactTag options:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback) RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isPointInStroke:(nonnull NSNumber *)reactTag options:(NSDictionary *)options)
{ {
__block UIView *view;
dispatch_sync(dispatch_get_main_queue(), ^{
view = [self.bridge.uiManager viewForReactTag:reactTag];
});
if (![view isKindOfClass:[RNSVGRenderable class]]) {
RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view);
return [NSNumber numberWithBool:false];
}
if (options == nil) { if (options == nil) {
RCTLogError(@"Invalid options given to isPointInFill, got: %@", options); RCTLogError(@"Invalid options given to isPointInFill, got: %@", options);
callback(@[[NSNumber numberWithBool:false]]); return [NSNumber numberWithBool:false];
return;
} }
id xo = [options objectForKey:@"x"]; id xo = [options objectForKey:@"x"];
id yo = [options objectForKey:@"y"]; id yo = [options objectForKey:@"y"];
if (![xo isKindOfClass:NSNumber.class] || if (![xo isKindOfClass:NSNumber.class] ||
![yo isKindOfClass:NSNumber.class]) { ![yo isKindOfClass:NSNumber.class]) {
RCTLogError(@"Invalid x or y given to isPointInFill"); RCTLogError(@"Invalid x or y given to isPointInFill");
callback(@[[NSNumber numberWithBool:false]]); return [NSNumber numberWithBool:false];
return;
} }
[self RNSVGRenderable *svg = (RNSVGRenderable *)view;
withTag:reactTag CGFloat x = (CGFloat)[xo doubleValue];
success:^(RNSVGRenderable *svg){ CGFloat y = (CGFloat)[yo doubleValue];
CGFloat x = (CGFloat)[xo floatValue]; CGPoint point = CGPointMake(x, y);
CGFloat y = (CGFloat)[yo floatValue]; BOOL hit = CGPathContainsPoint(svg.strokePath, nil, point, NO);
CGPoint point = CGPointMake(x, y);
BOOL hit = CGPathContainsPoint(svg.strokePath, nil, point, NO); return [NSNumber numberWithBool:hit];
callback(@[[NSNumber numberWithBool:hit]]);
}
fail:^{
callback(@[[NSNumber numberWithBool:false]]);
}
attempt:0];
} }
RCT_EXPORT_METHOD(getTotalLength:(nonnull NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getTotalLength:(nonnull NSNumber *)reactTag)
{ {
[self __block UIView *view;
withTag:reactTag dispatch_sync(dispatch_get_main_queue(), ^{
success:^(RNSVGRenderable *svg){ view = [self.bridge.uiManager viewForReactTag:reactTag];
CGPathRef target = [svg getPath:nil]; });
RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc]init]; if (![view isKindOfClass:[RNSVGRenderable class]]) {
[measure extractPathData:target]; RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view);
CGFloat pathLegth = measure.pathLength; return [NSNumber numberWithDouble:0];
callback(@[[NSNumber numberWithDouble:pathLegth]]); }
}
fail:^{ RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc]init];
callback(@[[NSNumber numberWithBool:false]]); RNSVGRenderable *svg = (RNSVGRenderable *)view;
} CGPathRef target = [svg getPath:nil];
attempt:0]; [measure extractPathData:target];
return [NSNumber numberWithDouble:measure.pathLength];
} }
RCT_EXPORT_METHOD(getPointAtLength:(nonnull NSNumber *)reactTag options:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback) RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getPointAtLength:(nonnull NSNumber *)reactTag options:(NSDictionary *)options)
{ {
id length = [options objectForKey:@"length"]; __block UIView *view;
CGFloat position = (CGFloat)[length floatValue]; dispatch_sync(dispatch_get_main_queue(), ^{
[self view = [self.bridge.uiManager viewForReactTag:reactTag];
withTag:reactTag });
success:^(RNSVGRenderable *svg){ if (![view isKindOfClass:[RNSVGRenderable class]]) {
CGPathRef target = [svg getPath:nil]; RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view);
RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc]init]; return nil;
[measure extractPathData:target]; }
CGFloat angle;
CGFloat x; CGFloat position = (CGFloat)[[options objectForKey:@"length"] doubleValue];
CGFloat y; RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc]init];
[measure getPosAndTan:&angle midPoint:fmax(0, fmin(position, measure.pathLength)) x:&x y:&y]; RNSVGRenderable *svg = (RNSVGRenderable *)view;
callback( CGPathRef target = [svg getPath:nil];
@[ [measure extractPathData:target];
@{
@"x":@(x), CGFloat x;
@"y":@(y), CGFloat y;
@"angle":@(angle) CGFloat angle;
} double midPoint = fmax(0, fmin(position, measure.pathLength));
] [measure getPosAndTan:&angle midPoint:midPoint x:&x y:&y];
);
} return @{
fail:^{ @"x":@(x),
callback(@[[NSNumber numberWithBool:false]]); @"y":@(y),
} @"angle":@(angle)
attempt:0]; };
} }
RCT_EXPORT_METHOD(getBBox:(nonnull NSNumber *)reactTag options:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback) RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getBBox:(nonnull NSNumber *)reactTag options:(NSDictionary *)options)
{ {
[self __block UIView *view;
withTag:reactTag dispatch_sync(dispatch_get_main_queue(), ^{
success:^(RNSVGRenderable *svg){ view = [self.bridge.uiManager viewForReactTag:reactTag];
BOOL fill = [[options objectForKey:@"fill"] boolValue]; });
BOOL stroke = [[options objectForKey:@"stroke"] boolValue]; if (![view isKindOfClass:[RNSVGRenderable class]]) {
BOOL markers = [[options objectForKey:@"markers"] boolValue]; RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view);
BOOL clipped = [[options objectForKey:@"clipped"] boolValue]; return nil;
[svg getPath:nil]; }
CGRect bounds = CGRectZero;
if (fill) { RNSVGRenderable *svg = (RNSVGRenderable *)view;
bounds = CGRectUnion(bounds, svg.fillBounds); BOOL fill = [[options objectForKey:@"fill"] boolValue];
} BOOL stroke = [[options objectForKey:@"stroke"] boolValue];
if (stroke) { BOOL markers = [[options objectForKey:@"markers"] boolValue];
bounds = CGRectUnion(bounds, svg.strokeBounds); BOOL clipped = [[options objectForKey:@"clipped"] boolValue];
} [svg getPath:nil];
if (markers) {
bounds = CGRectUnion(bounds, svg.markerBounds); CGRect bounds = CGRectZero;
} if (fill) {
if (clipped) { bounds = CGRectUnion(bounds, svg.fillBounds);
CGPathRef clipPath = [svg getClipPath]; }
CGRect clipBounds = CGPathGetBoundingBox(clipPath); if (stroke) {
if (clipPath && !CGRectIsEmpty(clipBounds)) { bounds = CGRectUnion(bounds, svg.strokeBounds);
bounds = CGRectIntersection(bounds, clipBounds); }
} if (markers) {
} bounds = CGRectUnion(bounds, svg.markerBounds);
CGPoint origin = bounds.origin; }
CGSize size = bounds.size; if (clipped) {
callback( CGPathRef clipPath = [svg getClipPath];
@[ CGRect clipBounds = CGPathGetBoundingBox(clipPath);
@{ if (clipPath && !CGRectIsEmpty(clipBounds)) {
@"x":@(origin.x), bounds = CGRectIntersection(bounds, clipBounds);
@"y":@(origin.y), }
@"width":@(size.width), }
@"height":@(size.height)
} CGPoint origin = bounds.origin;
] CGSize size = bounds.size;
); return @{
} @"x":@(origin.x),
fail:^{ @"y":@(origin.y),
callback(@[[NSNumber numberWithBool:false]]); @"width":@(size.width),
} @"height":@(size.height)
attempt:0]; };
} }
RCT_EXPORT_METHOD(getCTM:(nonnull NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getCTM:(nonnull NSNumber *)reactTag)
{ {
[self __block UIView *view;
withTag:reactTag dispatch_sync(dispatch_get_main_queue(), ^{
success:^(RNSVGRenderable *svg){ view = [self.bridge.uiManager viewForReactTag:reactTag];
CGAffineTransform ctm = svg.ctm; });
callback( if (![view isKindOfClass:[RNSVGRenderable class]]) {
@[ RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view);
@{ return nil;
@"a":@(ctm.a), }
@"b":@(ctm.b),
@"c":@(ctm.c), RNSVGRenderable *svg = (RNSVGRenderable *)view;
@"d":@(ctm.d), CGAffineTransform ctm = svg.ctm;
@"e":@(ctm.tx), return @{
@"f":@(ctm.ty) @"a":@(ctm.a),
} @"b":@(ctm.b),
] @"c":@(ctm.c),
); @"d":@(ctm.d),
} @"e":@(ctm.tx),
fail:^{ @"f":@(ctm.ty)
callback(@[[NSNumber numberWithBool:false]]); };
}
attempt:0];
} }
RCT_EXPORT_METHOD(getScreenCTM:(nonnull NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getScreenCTM:(nonnull NSNumber *)reactTag)
{ {
[self __block UIView *view;
withTag:reactTag dispatch_sync(dispatch_get_main_queue(), ^{
success:^(RNSVGRenderable *svg){ view = [self.bridge.uiManager viewForReactTag:reactTag];
RNSVGSvgView* root = svg.svgView; });
CGAffineTransform viewbox = [root getViewBoxTransform]; if (![view isKindOfClass:[RNSVGRenderable class]]) {
CGAffineTransform ctm = CGAffineTransformConcat(svg.ctm, viewbox); RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view);
CGPoint offset = [root convertPoint:CGPointZero toView:svg.window]; return nil;
callback( }
@[
@{ RNSVGRenderable *svg = (RNSVGRenderable *)view;
@"a":@(ctm.a), RNSVGSvgView* root = svg.svgView;
@"b":@(ctm.b), CGAffineTransform viewbox = [root getViewBoxTransform];
@"c":@(ctm.c), CGAffineTransform ctm = CGAffineTransformConcat(svg.ctm, viewbox);
@"d":@(ctm.d), CGPoint offset = [root convertPoint:CGPointZero toView:svg.window];
@"e":@(ctm.tx + offset.x),
@"f":@(ctm.ty + offset.y) return @{
} @"a":@(ctm.a),
] @"b":@(ctm.b),
); @"c":@(ctm.c),
} @"d":@(ctm.d),
fail:^{ @"e":@(ctm.tx + offset.x),
callback(@[[NSNumber numberWithBool:false]]); @"f":@(ctm.ty + offset.y)
} };
attempt:0];
} }
@end @end
+32 -107
View File
@@ -264,117 +264,42 @@ export default class Shape<P> extends Component<P> {
) => { ) => {
this.root && this.root.setNativeProps(props); this.root && this.root.setNativeProps(props);
}; };
getBBox = ( getBBox = (options?: SVGBoundingBoxOptions): SVGRect => {
options?: SVGBoundingBoxOptions,
callback?: (box: SVGRect) => void,
) => {
const { fill = true, stroke = true, markers = true, clipped = true } = const { fill = true, stroke = true, markers = true, clipped = true } =
options || {}; options || {};
const handle = findNodeHandle(this.root as Component); const handle = findNodeHandle(this.root as Component);
if (!callback) { return RNSVGRenderableManager.getBBox(handle, {
return new Promise(resolve => { fill,
RNSVGRenderableManager.getBBox( stroke,
handle, markers,
{ clipped,
fill, });
stroke, };
markers, getCTM = (): SVGMatrix => {
clipped, const handle = findNodeHandle(this.root as Component);
}, return new SVGMatrix(RNSVGRenderableManager.getCTM(handle));
resolve, };
); getScreenCTM = (): SVGMatrix => {
}); const handle = findNodeHandle(this.root as Component);
} return new SVGMatrix(RNSVGRenderableManager.getScreenCTM(handle));
RNSVGRenderableManager.getBBox( };
handle, isPointInFill = (options: DOMPointInit): boolean => {
{ const handle = findNodeHandle(this.root as Component);
fill, return RNSVGRenderableManager.isPointInFill(handle, options);
stroke, };
markers, isPointInStroke = (options: DOMPointInit): boolean => {
clipped, const handle = findNodeHandle(this.root as Component);
}, return RNSVGRenderableManager.isPointInStroke(handle, options);
callback, };
getTotalLength = (): number => {
const handle = findNodeHandle(this.root as Component);
return RNSVGRenderableManager.getTotalLength(handle);
};
getPointAtLength = (length: number): SVGPoint => {
const handle = findNodeHandle(this.root as Component);
return new SVGPoint(
RNSVGRenderableManager.getPointAtLength(handle, { length }),
); );
return undefined;
};
getCTM = (callback: (screenCTM: SVGMatrix) => void) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
RNSVGRenderableManager.getCTM(handle, (matrix: Matrix) =>
resolve(new SVGMatrix(matrix)),
);
});
}
RNSVGRenderableManager.getCTM(handle, (matrix: Matrix) =>
callback(new SVGMatrix(matrix)),
);
return undefined;
};
getScreenCTM = (callback: (screenCTM: SVGMatrix) => void) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
RNSVGRenderableManager.getScreenCTM(handle, (matrix: Matrix) =>
resolve(new SVGMatrix(matrix)),
);
});
}
RNSVGRenderableManager.getScreenCTM(handle, (matrix: Matrix) =>
callback(new SVGMatrix(matrix)),
);
return undefined;
};
isPointInFill = (options: DOMPointInit, callback: (res: boolean) => void) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
RNSVGRenderableManager.isPointInFill(handle, options, resolve);
});
}
RNSVGRenderableManager.isPointInFill(handle, options, callback);
return undefined;
};
isPointInStroke = (
options: DOMPointInit,
callback?: (res: boolean) => void,
) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
RNSVGRenderableManager.isPointInStroke(handle, options, resolve);
});
}
RNSVGRenderableManager.isPointInStroke(handle, options, callback);
return undefined;
};
getTotalLength = (callback?: (length: number) => void) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
RNSVGRenderableManager.getTotalLength(handle, resolve);
});
}
RNSVGRenderableManager.getTotalLength(handle, callback);
return undefined;
};
getPointAtLength = (length: number, callback: (point: SVGPoint) => void) => {
const handle = findNodeHandle(this.root as Component);
if (!callback) {
return new Promise(resolve => {
RNSVGRenderableManager.getPointAtLength(
handle,
{ length },
(point: Point) => resolve(new SVGPoint(point)),
);
});
}
RNSVGRenderableManager.getPointAtLength(
handle,
{ length },
(point: Point) => callback(new SVGPoint(point)),
);
return undefined;
}; };
} }
Shape.prototype.ownerSVGElement = ownerSVGElement; Shape.prototype.ownerSVGElement = ownerSVGElement;