mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-06-09 17:30:58 +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 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user