From 8ce76113e1e01f70cba8da4327c4b6e50b5b0a7a Mon Sep 17 00:00:00 2001 From: Mikael Sand Date: Sat, 5 Oct 2019 18:43:00 +0300 Subject: [PATCH] fix: Make native methods synchronous --- .../horcrux/svg/RNSVGRenderableManager.java | 160 ++++---- ios/ViewManagers/RNSVGRenderableManager.m | 358 ++++++++---------- src/elements/Shape.tsx | 115 +----- 3 files changed, 265 insertions(+), 368 deletions(-) diff --git a/android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java b/android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java index af1775708..d170d0ba8 100644 --- a/android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java +++ b/android/src/main/java/com/horcrux/svg/RNSVGRenderableManager.java @@ -16,12 +16,10 @@ import android.graphics.Region; import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.bridge.WritableMap; import javax.annotation.Nonnull; @@ -37,106 +35,97 @@ public String getName() { 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") - @ReactMethod - public void isPointInFill(int tag, ReadableMap options, Callback successCallback) { - float x = (float) options.getDouble("x"); - float y = (float) options.getDouble("y"); - float[] src = new float[]{x, y}; - isPointInFill(tag, src, successCallback, 0); + @ReactMethod(isBlockingSynchronousMethod = true) + public boolean isPointInFill(int tag, ReadableMap options) { + RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); + if (svg == null) { + return false; + } + + 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 - public void isPointInStroke(int tag, ReadableMap options, Callback successCallback) { + @ReactMethod(isBlockingSynchronousMethod = true) + public boolean isPointInStroke(int tag, ReadableMap options) { RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); if (svg == null) { - successCallback.invoke(false); - return; + return false; } + svg.getPath(null, null); svg.initBounds(); - Region strokeRegion = svg.mStrokeRegion; + float scale = svg.mScale; int x = (int) (options.getDouble("x") * 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") - @ReactMethod - public void getTotalLength(int tag, Callback successCallback) { + @ReactMethod(isBlockingSynchronousMethod = true) + public float getTotalLength(int tag) { RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); - PathMeasure pm = new PathMeasure(svg.getPath(null, null), false); - float scale = svg.mScale; - successCallback.invoke(pm.getLength() / scale); + if (svg == null) { + return 0; + } + + Path path = svg.getPath(null, null); + PathMeasure pm = new PathMeasure(path, false); + return pm.getLength() / svg.mScale; } @SuppressWarnings("unused") - @ReactMethod - public void getPointAtLength(int tag, ReadableMap options, Callback successCallback) { - float length = (float) options.getDouble("length"); + @ReactMethod(isBlockingSynchronousMethod = true) + public WritableMap getPointAtLength(int tag, ReadableMap options) { RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); - PathMeasure pm = new PathMeasure(svg.getPath(null, null), false); - float pathLength = pm.getLength(); + if (svg == null) { + return null; + } + + Path path = svg.getPath(null, null); + PathMeasure pm = new PathMeasure(path, false); + float length = (float) options.getDouble("length"); + float scale = svg.mScale; + float[] pos = 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]); WritableMap result = Arguments.createMap(); - float scale = svg.mScale; result.putDouble("x", pos[0] / scale); result.putDouble("y", pos[1] / scale); result.putDouble("angle", angle); - successCallback.invoke(result); + return result; } @SuppressWarnings("unused") - @ReactMethod - public void getBBox(int tag, ReadableMap options, Callback successCallback) { + @ReactMethod(isBlockingSynchronousMethod = true) + public WritableMap getBBox(int tag, ReadableMap options) { + RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); + if (svg == null) { + return null; + } + boolean fill = options.getBoolean("fill"); boolean stroke = options.getBoolean("stroke"); boolean markers = options.getBoolean("markers"); boolean clipped = options.getBoolean("clipped"); - RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); + Path path = svg.getPath(null, null); + float scale = svg.mScale; svg.initBounds(); + RectF bounds = new RectF(); if (fill) { bounds.union(svg.mFillBounds); @@ -153,26 +142,30 @@ public void getBBox(int tag, ReadableMap options, Callback successCallback) { bounds.intersect(svg.mClipBounds); } } + WritableMap result = Arguments.createMap(); - float scale = svg.mScale; result.putDouble("x", bounds.left / scale); result.putDouble("y", bounds.top / scale); result.putDouble("width", bounds.width() / scale); result.putDouble("height", bounds.height() / scale); - successCallback.invoke(result); + return result; } @SuppressWarnings("unused") - @ReactMethod - public void getCTM(int tag, Callback successCallback) { + @ReactMethod(isBlockingSynchronousMethod = true) + public WritableMap getCTM(int tag) { RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag); - Matrix screenCTM = svg.mCTM; - Matrix invViewBox = svg.getSvgView().mInvViewBoxMatrix; - Matrix ctm = new Matrix(screenCTM); - ctm.preConcat(invViewBox); + if (svg == null) { + return null; + } + + Matrix ctm = new Matrix(svg.mCTM); + Matrix invViewBoxMatrix = svg.getSvgView().mInvViewBoxMatrix; + ctm.preConcat(invViewBoxMatrix); float[] values = new float[9]; ctm.getValues(values); + WritableMap result = Arguments.createMap(); result.putDouble("a", values[Matrix.MSCALE_X]); result.putDouble("b", values[Matrix.MSKEW_Y]); @@ -180,18 +173,23 @@ public void getCTM(int tag, Callback successCallback) { result.putDouble("d", values[Matrix.MSCALE_Y]); result.putDouble("e", values[Matrix.MTRANS_X]); result.putDouble("f", values[Matrix.MTRANS_Y]); - successCallback.invoke(result); + return result; } @SuppressWarnings("unused") - @ReactMethod - public void getScreenCTM(int tag, Callback successCallback) { + @ReactMethod(isBlockingSynchronousMethod = true) + public WritableMap getScreenCTM(int 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(); float scale = svg.mScale; - float[] values = new float[9]; - screenCTM.getValues(values); + WritableMap result = Arguments.createMap(); result.putDouble("a", values[Matrix.MSCALE_X]); result.putDouble("b", values[Matrix.MSKEW_Y]); @@ -199,6 +197,6 @@ public void getScreenCTM(int tag, Callback successCallback) { result.putDouble("d", values[Matrix.MSCALE_Y]); result.putDouble("e", values[Matrix.MTRANS_X] + root.getLeft() / scale); result.putDouble("f", values[Matrix.MTRANS_Y] + root.getTop() / scale); - successCallback.invoke(result); + return result; } } diff --git a/ios/ViewManagers/RNSVGRenderableManager.m b/ios/ViewManagers/RNSVGRenderableManager.m index 46465eedd..b6ee5320f 100644 --- a/ios/ViewManagers/RNSVGRenderableManager.m +++ b/ios/ViewManagers/RNSVGRenderableManager.m @@ -38,237 +38,211 @@ - (RNSVGRenderable *)node RCT_EXPORT_VIEW_PROPERTY(vectorEffect, int) RCT_EXPORT_VIEW_PROPERTY(propList, NSArray) -typedef void (^RNSVGSuccessBlock)(RNSVGRenderable *view); -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 *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) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(isPointInFill:(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) { RCTLogError(@"Invalid options given to isPointInFill, got: %@", options); - callback(@[[NSNumber numberWithBool:false]]); - return; + return [NSNumber numberWithBool:false]; } id xo = [options objectForKey:@"x"]; id yo = [options objectForKey:@"y"]; if (![xo isKindOfClass:NSNumber.class] || ![yo isKindOfClass:NSNumber.class]) { RCTLogError(@"Invalid x or y given to isPointInFill"); - callback(@[[NSNumber numberWithBool:false]]); - return; + return [NSNumber numberWithBool:false]; } - CGFloat x = (CGFloat)[xo floatValue]; - CGFloat y = (CGFloat)[yo floatValue]; + RNSVGRenderable *svg = (RNSVGRenderable *)view; + CGFloat x = (CGFloat)[xo doubleValue]; + CGFloat y = (CGFloat)[yo doubleValue]; CGPoint point = CGPointMake(x, y); - [self - withTag:reactTag - success:^(RNSVGRenderable *svg){ - UIView *target = [svg hitTest:point withEvent:nil]; - BOOL hit = target != nil; - callback(@[[NSNumber numberWithBool:hit]]); - } - fail:^{ - callback(@[[NSNumber numberWithBool:false]]); - } - attempt:0]; + UIView *target = [svg hitTest:point withEvent:nil]; + BOOL hit = target != nil; + return [NSNumber numberWithBool:hit]; } -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) { RCTLogError(@"Invalid options given to isPointInFill, got: %@", options); - callback(@[[NSNumber numberWithBool:false]]); - return; + return [NSNumber numberWithBool:false]; } id xo = [options objectForKey:@"x"]; id yo = [options objectForKey:@"y"]; if (![xo isKindOfClass:NSNumber.class] || ![yo isKindOfClass:NSNumber.class]) { RCTLogError(@"Invalid x or y given to isPointInFill"); - callback(@[[NSNumber numberWithBool:false]]); - return; + return [NSNumber numberWithBool:false]; } - [self - withTag:reactTag - success:^(RNSVGRenderable *svg){ - CGFloat x = (CGFloat)[xo floatValue]; - CGFloat y = (CGFloat)[yo floatValue]; - CGPoint point = CGPointMake(x, y); - BOOL hit = CGPathContainsPoint(svg.strokePath, nil, point, NO); - callback(@[[NSNumber numberWithBool:hit]]); - } - fail:^{ - callback(@[[NSNumber numberWithBool:false]]); - } - attempt:0]; + RNSVGRenderable *svg = (RNSVGRenderable *)view; + CGFloat x = (CGFloat)[xo doubleValue]; + CGFloat y = (CGFloat)[yo doubleValue]; + CGPoint point = CGPointMake(x, y); + BOOL hit = CGPathContainsPoint(svg.strokePath, nil, point, NO); + + return [NSNumber numberWithBool:hit]; } -RCT_EXPORT_METHOD(getTotalLength:(nonnull NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getTotalLength:(nonnull NSNumber *)reactTag) { - [self - withTag:reactTag - success:^(RNSVGRenderable *svg){ - CGPathRef target = [svg getPath:nil]; - RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc]init]; - [measure extractPathData:target]; - CGFloat pathLegth = measure.pathLength; - callback(@[[NSNumber numberWithDouble:pathLegth]]); - } - fail:^{ - callback(@[[NSNumber numberWithBool:false]]); - } - attempt:0]; + __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 numberWithDouble:0]; + } + + RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc]init]; + RNSVGRenderable *svg = (RNSVGRenderable *)view; + CGPathRef target = [svg getPath:nil]; + [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"]; - CGFloat position = (CGFloat)[length floatValue]; - [self - withTag:reactTag - success:^(RNSVGRenderable *svg){ - CGPathRef target = [svg getPath:nil]; - RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc]init]; - [measure extractPathData:target]; - CGFloat angle; - CGFloat x; - CGFloat y; - [measure getPosAndTan:&angle midPoint:fmax(0, fmin(position, measure.pathLength)) x:&x y:&y]; - callback( - @[ - @{ - @"x":@(x), - @"y":@(y), - @"angle":@(angle) - } - ] - ); - } - fail:^{ - callback(@[[NSNumber numberWithBool:false]]); - } - attempt:0]; + __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 nil; + } + + CGFloat position = (CGFloat)[[options objectForKey:@"length"] doubleValue]; + RNSVGPathMeasure *measure = [[RNSVGPathMeasure alloc]init]; + RNSVGRenderable *svg = (RNSVGRenderable *)view; + CGPathRef target = [svg getPath:nil]; + [measure extractPathData:target]; + + CGFloat x; + CGFloat y; + CGFloat angle; + double midPoint = fmax(0, fmin(position, measure.pathLength)); + [measure getPosAndTan:&angle midPoint:midPoint x:&x y:&y]; + + return @{ + @"x":@(x), + @"y":@(y), + @"angle":@(angle) + }; } -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 - withTag:reactTag - success:^(RNSVGRenderable *svg){ - BOOL fill = [[options objectForKey:@"fill"] boolValue]; - BOOL stroke = [[options objectForKey:@"stroke"] boolValue]; - BOOL markers = [[options objectForKey:@"markers"] boolValue]; - BOOL clipped = [[options objectForKey:@"clipped"] boolValue]; - [svg getPath:nil]; - CGRect bounds = CGRectZero; - if (fill) { - bounds = CGRectUnion(bounds, svg.fillBounds); - } - if (stroke) { - bounds = CGRectUnion(bounds, svg.strokeBounds); - } - if (markers) { - bounds = CGRectUnion(bounds, svg.markerBounds); - } - if (clipped) { - CGPathRef clipPath = [svg getClipPath]; - CGRect clipBounds = CGPathGetBoundingBox(clipPath); - if (clipPath && !CGRectIsEmpty(clipBounds)) { - bounds = CGRectIntersection(bounds, clipBounds); - } - } - CGPoint origin = bounds.origin; - CGSize size = bounds.size; - callback( - @[ - @{ - @"x":@(origin.x), - @"y":@(origin.y), - @"width":@(size.width), - @"height":@(size.height) - } - ] - ); - } - fail:^{ - callback(@[[NSNumber numberWithBool:false]]); - } - attempt:0]; + __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 nil; + } + + RNSVGRenderable *svg = (RNSVGRenderable *)view; + BOOL fill = [[options objectForKey:@"fill"] boolValue]; + BOOL stroke = [[options objectForKey:@"stroke"] boolValue]; + BOOL markers = [[options objectForKey:@"markers"] boolValue]; + BOOL clipped = [[options objectForKey:@"clipped"] boolValue]; + [svg getPath:nil]; + + CGRect bounds = CGRectZero; + if (fill) { + bounds = CGRectUnion(bounds, svg.fillBounds); + } + if (stroke) { + bounds = CGRectUnion(bounds, svg.strokeBounds); + } + if (markers) { + bounds = CGRectUnion(bounds, svg.markerBounds); + } + if (clipped) { + CGPathRef clipPath = [svg getClipPath]; + CGRect clipBounds = CGPathGetBoundingBox(clipPath); + if (clipPath && !CGRectIsEmpty(clipBounds)) { + bounds = CGRectIntersection(bounds, clipBounds); + } + } + + CGPoint origin = bounds.origin; + CGSize size = bounds.size; + return @{ + @"x":@(origin.x), + @"y":@(origin.y), + @"width":@(size.width), + @"height":@(size.height) + }; } -RCT_EXPORT_METHOD(getCTM:(nonnull NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getCTM:(nonnull NSNumber *)reactTag) { - [self - withTag:reactTag - success:^(RNSVGRenderable *svg){ - CGAffineTransform ctm = svg.ctm; - callback( - @[ - @{ - @"a":@(ctm.a), - @"b":@(ctm.b), - @"c":@(ctm.c), - @"d":@(ctm.d), - @"e":@(ctm.tx), - @"f":@(ctm.ty) - } - ] - ); - } - fail:^{ - callback(@[[NSNumber numberWithBool:false]]); - } - attempt:0]; + __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 nil; + } + + RNSVGRenderable *svg = (RNSVGRenderable *)view; + CGAffineTransform ctm = svg.ctm; + return @{ + @"a":@(ctm.a), + @"b":@(ctm.b), + @"c":@(ctm.c), + @"d":@(ctm.d), + @"e":@(ctm.tx), + @"f":@(ctm.ty) + }; } -RCT_EXPORT_METHOD(getScreenCTM:(nonnull NSNumber *)reactTag callback:(RCTResponseSenderBlock)callback) +RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getScreenCTM:(nonnull NSNumber *)reactTag) { - [self - withTag:reactTag - success:^(RNSVGRenderable *svg){ - RNSVGSvgView* root = svg.svgView; - CGAffineTransform viewbox = [root getViewBoxTransform]; - CGAffineTransform ctm = CGAffineTransformConcat(svg.ctm, viewbox); - CGPoint offset = [root convertPoint:CGPointZero toView:svg.window]; - callback( - @[ - @{ - @"a":@(ctm.a), - @"b":@(ctm.b), - @"c":@(ctm.c), - @"d":@(ctm.d), - @"e":@(ctm.tx + offset.x), - @"f":@(ctm.ty + offset.y) - } - ] - ); - } - fail:^{ - callback(@[[NSNumber numberWithBool:false]]); - } - attempt:0]; + __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 nil; + } + + RNSVGRenderable *svg = (RNSVGRenderable *)view; + RNSVGSvgView* root = svg.svgView; + CGAffineTransform viewbox = [root getViewBoxTransform]; + CGAffineTransform ctm = CGAffineTransformConcat(svg.ctm, viewbox); + CGPoint offset = [root convertPoint:CGPointZero toView:svg.window]; + + return @{ + @"a":@(ctm.a), + @"b":@(ctm.b), + @"c":@(ctm.c), + @"d":@(ctm.d), + @"e":@(ctm.tx + offset.x), + @"f":@(ctm.ty + offset.y) + }; } @end + diff --git a/src/elements/Shape.tsx b/src/elements/Shape.tsx index d9085ef73..eeec5ca81 100644 --- a/src/elements/Shape.tsx +++ b/src/elements/Shape.tsx @@ -264,117 +264,42 @@ export default class Shape

extends Component

{ ) => { this.root && this.root.setNativeProps(props); }; - getBBox = ( - options?: SVGBoundingBoxOptions, - callback?: (box: SVGRect) => void, - ) => { + getBBox = (options?: SVGBoundingBoxOptions): SVGRect => { const { fill = true, stroke = true, markers = true, clipped = true } = options || {}; const handle = findNodeHandle(this.root as Component); - if (!callback) { - return new Promise(resolve => { - RNSVGRenderableManager.getBBox( - handle, - { - fill, - stroke, - markers, - clipped, - }, - resolve, - ); - }); - } - RNSVGRenderableManager.getBBox( - handle, - { - fill, - stroke, - markers, - clipped, - }, - callback, - ); - return undefined; + return RNSVGRenderableManager.getBBox(handle, { + fill, + stroke, + markers, + clipped, + }); }; - getCTM = (callback: (screenCTM: SVGMatrix) => void) => { + getCTM = (): SVGMatrix => { 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; + return new SVGMatrix(RNSVGRenderableManager.getCTM(handle)); }; - getScreenCTM = (callback: (screenCTM: SVGMatrix) => void) => { + getScreenCTM = (): SVGMatrix => { 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; + return new SVGMatrix(RNSVGRenderableManager.getScreenCTM(handle)); }; - isPointInFill = (options: DOMPointInit, callback: (res: boolean) => void) => { + isPointInFill = (options: DOMPointInit): boolean => { 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; + return RNSVGRenderableManager.isPointInFill(handle, options); }; - isPointInStroke = ( - options: DOMPointInit, - callback?: (res: boolean) => void, - ) => { + isPointInStroke = (options: DOMPointInit): boolean => { 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; + return RNSVGRenderableManager.isPointInStroke(handle, options); }; - getTotalLength = (callback?: (length: number) => void) => { + getTotalLength = (): number => { const handle = findNodeHandle(this.root as Component); - if (!callback) { - return new Promise(resolve => { - RNSVGRenderableManager.getTotalLength(handle, resolve); - }); - } - RNSVGRenderableManager.getTotalLength(handle, callback); - return undefined; + return RNSVGRenderableManager.getTotalLength(handle); }; - getPointAtLength = (length: number, callback: (point: SVGPoint) => void) => { + getPointAtLength = (length: number): SVGPoint => { 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 new SVGPoint( + RNSVGRenderableManager.getPointAtLength(handle, { length }), ); - return undefined; }; } Shape.prototype.ownerSVGElement = ownerSVGElement;