mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-06-06 08:22:23 +00:00
fix: Make native methods synchronous
This commit is contained in:
@@ -16,12 +16,10 @@ import android.graphics.RectF;
|
||||
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 @@ class RNSVGRenderableManager extends ReactContextBaseJavaModule {
|
||||
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);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ReactMethod
|
||||
public void isPointInStroke(int tag, ReadableMap options, Callback successCallback) {
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
public boolean isPointInFill(int tag, ReadableMap options) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
successCallback.invoke(false);
|
||||
return;
|
||||
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(isBlockingSynchronousMethod = true)
|
||||
public boolean isPointInStroke(int tag, ReadableMap options) {
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
if (svg == null) {
|
||||
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) {
|
||||
@ReactMethod(isBlockingSynchronousMethod = true)
|
||||
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");
|
||||
RenderableView svg = RenderableViewManager.getRenderableViewByTag(tag);
|
||||
PathMeasure pm = new PathMeasure(svg.getPath(null, null), false);
|
||||
float pathLength = pm.getLength();
|
||||
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 @@ class RNSVGRenderableManager extends ReactContextBaseJavaModule {
|
||||
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 @@ class RNSVGRenderableManager extends ReactContextBaseJavaModule {
|
||||
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 @@ class RNSVGRenderableManager extends ReactContextBaseJavaModule {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,237 +38,211 @@ RCT_EXPORT_VIEW_PROPERTY(strokeMiterlimit, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(vectorEffect, int)
|
||||
RCT_EXPORT_VIEW_PROPERTY(propList, NSArray<NSString *>)
|
||||
|
||||
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<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)
|
||||
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
|
||||
|
||||
|
||||
|
||||
+32
-107
@@ -264,117 +264,42 @@ export default class Shape<P> extends Component<P> {
|
||||
) => {
|
||||
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 RNSVGRenderableManager.getBBox(handle, {
|
||||
fill,
|
||||
stroke,
|
||||
markers,
|
||||
clipped,
|
||||
});
|
||||
};
|
||||
getCTM = (): SVGMatrix => {
|
||||
const handle = findNodeHandle(this.root as Component);
|
||||
return new SVGMatrix(RNSVGRenderableManager.getCTM(handle));
|
||||
};
|
||||
getScreenCTM = (): SVGMatrix => {
|
||||
const handle = findNodeHandle(this.root as Component);
|
||||
return new SVGMatrix(RNSVGRenderableManager.getScreenCTM(handle));
|
||||
};
|
||||
isPointInFill = (options: DOMPointInit): boolean => {
|
||||
const handle = findNodeHandle(this.root as Component);
|
||||
return RNSVGRenderableManager.isPointInFill(handle, options);
|
||||
};
|
||||
isPointInStroke = (options: DOMPointInit): boolean => {
|
||||
const handle = findNodeHandle(this.root as Component);
|
||||
return RNSVGRenderableManager.isPointInStroke(handle, options);
|
||||
};
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user