mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-08-16 02:44:58 +00:00
feat: initial implementation of isPointInFill
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2015-present, Horcrux.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the MIT-style license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
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 javax.annotation.Nonnull;
|
||||
|
||||
class RNSVGRenderableManager extends ReactContextBaseJavaModule {
|
||||
RNSVGRenderableManager(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
return;
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
@@ -94,6 +94,12 @@ abstract public class RenderableView extends VirtualView {
|
||||
|
||||
private static final Pattern regex = Pattern.compile("[0-9.-]+");
|
||||
|
||||
@Override
|
||||
public void setId(int id) {
|
||||
super.setId(id);
|
||||
RenderableViewManager.setRenderableView(id, this);
|
||||
}
|
||||
|
||||
@ReactProp(name = "vectorEffect")
|
||||
public void setVectorEffect(int vectorEffect) {
|
||||
this.vectorEffect = vectorEffect;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@@ -41,7 +42,57 @@ import static com.facebook.react.uimanager.MatrixMathHelper.v3Cross;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.v3Dot;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.v3Length;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.v3Normalize;
|
||||
import static com.facebook.react.uimanager.ViewProps.*;
|
||||
import static com.facebook.react.uimanager.ViewProps.ALIGN_CONTENT;
|
||||
import static com.facebook.react.uimanager.ViewProps.ALIGN_ITEMS;
|
||||
import static com.facebook.react.uimanager.ViewProps.ALIGN_SELF;
|
||||
import static com.facebook.react.uimanager.ViewProps.BORDER_BOTTOM_WIDTH;
|
||||
import static com.facebook.react.uimanager.ViewProps.BORDER_END_WIDTH;
|
||||
import static com.facebook.react.uimanager.ViewProps.BORDER_LEFT_WIDTH;
|
||||
import static com.facebook.react.uimanager.ViewProps.BORDER_RIGHT_WIDTH;
|
||||
import static com.facebook.react.uimanager.ViewProps.BORDER_START_WIDTH;
|
||||
import static com.facebook.react.uimanager.ViewProps.BORDER_TOP_WIDTH;
|
||||
import static com.facebook.react.uimanager.ViewProps.BORDER_WIDTH;
|
||||
import static com.facebook.react.uimanager.ViewProps.BOTTOM;
|
||||
import static com.facebook.react.uimanager.ViewProps.COLLAPSABLE;
|
||||
import static com.facebook.react.uimanager.ViewProps.DISPLAY;
|
||||
import static com.facebook.react.uimanager.ViewProps.END;
|
||||
import static com.facebook.react.uimanager.ViewProps.FLEX;
|
||||
import static com.facebook.react.uimanager.ViewProps.FLEX_BASIS;
|
||||
import static com.facebook.react.uimanager.ViewProps.FLEX_DIRECTION;
|
||||
import static com.facebook.react.uimanager.ViewProps.FLEX_GROW;
|
||||
import static com.facebook.react.uimanager.ViewProps.FLEX_SHRINK;
|
||||
import static com.facebook.react.uimanager.ViewProps.FLEX_WRAP;
|
||||
import static com.facebook.react.uimanager.ViewProps.HEIGHT;
|
||||
import static com.facebook.react.uimanager.ViewProps.JUSTIFY_CONTENT;
|
||||
import static com.facebook.react.uimanager.ViewProps.LEFT;
|
||||
import static com.facebook.react.uimanager.ViewProps.MARGIN;
|
||||
import static com.facebook.react.uimanager.ViewProps.MARGIN_BOTTOM;
|
||||
import static com.facebook.react.uimanager.ViewProps.MARGIN_END;
|
||||
import static com.facebook.react.uimanager.ViewProps.MARGIN_HORIZONTAL;
|
||||
import static com.facebook.react.uimanager.ViewProps.MARGIN_LEFT;
|
||||
import static com.facebook.react.uimanager.ViewProps.MARGIN_RIGHT;
|
||||
import static com.facebook.react.uimanager.ViewProps.MARGIN_START;
|
||||
import static com.facebook.react.uimanager.ViewProps.MARGIN_TOP;
|
||||
import static com.facebook.react.uimanager.ViewProps.MARGIN_VERTICAL;
|
||||
import static com.facebook.react.uimanager.ViewProps.MAX_HEIGHT;
|
||||
import static com.facebook.react.uimanager.ViewProps.MAX_WIDTH;
|
||||
import static com.facebook.react.uimanager.ViewProps.MIN_HEIGHT;
|
||||
import static com.facebook.react.uimanager.ViewProps.MIN_WIDTH;
|
||||
import static com.facebook.react.uimanager.ViewProps.OVERFLOW;
|
||||
import static com.facebook.react.uimanager.ViewProps.PADDING;
|
||||
import static com.facebook.react.uimanager.ViewProps.PADDING_BOTTOM;
|
||||
import static com.facebook.react.uimanager.ViewProps.PADDING_END;
|
||||
import static com.facebook.react.uimanager.ViewProps.PADDING_HORIZONTAL;
|
||||
import static com.facebook.react.uimanager.ViewProps.PADDING_LEFT;
|
||||
import static com.facebook.react.uimanager.ViewProps.PADDING_RIGHT;
|
||||
import static com.facebook.react.uimanager.ViewProps.PADDING_START;
|
||||
import static com.facebook.react.uimanager.ViewProps.PADDING_TOP;
|
||||
import static com.facebook.react.uimanager.ViewProps.PADDING_VERTICAL;
|
||||
import static com.facebook.react.uimanager.ViewProps.POSITION;
|
||||
import static com.facebook.react.uimanager.ViewProps.RIGHT;
|
||||
import static com.facebook.react.uimanager.ViewProps.START;
|
||||
import static com.facebook.react.uimanager.ViewProps.TOP;
|
||||
import static com.facebook.react.uimanager.ViewProps.WIDTH;
|
||||
import static com.horcrux.svg.RenderableView.CAP_ROUND;
|
||||
import static com.horcrux.svg.RenderableView.FILL_RULE_NONZERO;
|
||||
import static com.horcrux.svg.RenderableView.JOIN_ROUND;
|
||||
@@ -1250,4 +1301,30 @@ class RenderableViewManager extends ViewGroupManager<VirtualView> {
|
||||
throw new IllegalStateException("Unexpected type " + svgClass.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static final SparseArray<RenderableView> mTagToRenderableView = new SparseArray<>();
|
||||
private static final SparseArray<Runnable> mTagToRunnable = new SparseArray<>();
|
||||
|
||||
static void setRenderableView(int tag, RenderableView svg) {
|
||||
mTagToRenderableView.put(tag, svg);
|
||||
Runnable task = mTagToRunnable.get(tag);
|
||||
if (task != null) {
|
||||
task.run();
|
||||
mTagToRunnable.delete(tag);
|
||||
}
|
||||
}
|
||||
|
||||
static void runWhenViewIsAvailable(int tag, Runnable task) {
|
||||
mTagToRunnable.put(tag, task);
|
||||
}
|
||||
|
||||
static @Nullable RenderableView getRenderableViewByTag(int tag) {
|
||||
return mTagToRenderableView.get(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDropViewInstance(@Nonnull VirtualView view) {
|
||||
super.onDropViewInstance(view);
|
||||
mTagToRenderableView.remove(view.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,10 @@ public class SvgPackage implements ReactPackage {
|
||||
@Nonnull
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(@Nonnull ReactApplicationContext reactContext) {
|
||||
return Collections.<NativeModule>singletonList(new SvgViewModule(reactContext));
|
||||
return Arrays.<NativeModule>asList(
|
||||
new SvgViewModule(reactContext),
|
||||
new RNSVGRenderableManager(reactContext)
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTUIManagerUtils.h>
|
||||
#import "RNSVGRenderableManager.h"
|
||||
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
@@ -34,4 +37,53 @@ RCT_EXPORT_VIEW_PROPERTY(strokeMiterlimit, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(vectorEffect, int)
|
||||
RCT_EXPORT_VIEW_PROPERTY(propList, NSArray<NSString *>)
|
||||
|
||||
- (void)isPointInFill:(nonnull NSNumber *)reactTag point:(CGPoint)point callback:(RCTResponseSenderBlock)callback attempt:(int)attempt {
|
||||
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *,UIView *> *viewRegistry) {
|
||||
__kindof UIView *view = viewRegistry[reactTag];
|
||||
UIView *target;
|
||||
if (!view) {
|
||||
if (attempt < 1) {
|
||||
void (^retryBlock)(void) = ^{
|
||||
[self isPointInFill:reactTag point:point callback:callback attempt:(attempt + 1)];
|
||||
};
|
||||
RCTExecuteOnUIManagerQueue(retryBlock);
|
||||
} else {
|
||||
callback(@[[NSNumber numberWithBool:false]]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ([view isKindOfClass:[RNSVGRenderable class]]) {
|
||||
RNSVGRenderable *svg = view;
|
||||
target = [svg hitTest:point withEvent:nil];
|
||||
BOOL hit = target != nil;
|
||||
callback(@[[NSNumber numberWithBool:hit]]);
|
||||
} else {
|
||||
RCTLogError(@"Invalid svg returned from registry, expecting RNSVGRenderable, got: %@", view);
|
||||
callback(@[[NSNumber numberWithBool:false]]);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(isPointInFill:(nonnull NSNumber *)reactTag options:(NSDictionary *)options callback:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
if (options == nil) {
|
||||
RCTLogError(@"Invalid options given to isPointInFill, got: %@", options);
|
||||
callback(@[[NSNumber numberWithBool:false]]);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
CGFloat x = (CGFloat)[xo floatValue];
|
||||
CGFloat y = (CGFloat)[yo floatValue];
|
||||
CGPoint point = CGPointMake(x, y);
|
||||
[self isPointInFill:reactTag point:point callback:callback attempt:0];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
+69
-1
@@ -1,13 +1,32 @@
|
||||
import { Component } from 'react';
|
||||
import SvgTouchableMixin from '../lib/SvgTouchableMixin';
|
||||
import { NativeMethodsMixinStatic } from 'react-native';
|
||||
import {
|
||||
NativeModules,
|
||||
findNodeHandle,
|
||||
NativeMethodsMixinStatic,
|
||||
} from 'react-native';
|
||||
import { TransformProps } from '../lib/extract/types';
|
||||
|
||||
const RNSVGRenderableManager = NativeModules.RNSVGRenderableManager;
|
||||
const { touchableGetInitialState } = SvgTouchableMixin;
|
||||
const touchKeys = Object.keys(SvgTouchableMixin);
|
||||
const touchVals = touchKeys.map(key => SvgTouchableMixin[key]);
|
||||
const numTouchKeys = touchKeys.length;
|
||||
|
||||
interface SVGBoundingBoxOptions {
|
||||
fill: boolean;
|
||||
stroke: boolean;
|
||||
markers: boolean;
|
||||
clipped: boolean;
|
||||
}
|
||||
|
||||
interface DOMPointInit {
|
||||
x?: number;
|
||||
y?: number;
|
||||
z?: number;
|
||||
w?: number;
|
||||
}
|
||||
|
||||
export default class Shape<P> extends Component<P> {
|
||||
[x: string]: unknown;
|
||||
root: (Shape<P> & NativeMethodsMixinStatic) | null = null;
|
||||
@@ -36,4 +55,53 @@ export default class Shape<P> extends Component<P> {
|
||||
) => {
|
||||
this.root && this.root.setNativeProps(props);
|
||||
};
|
||||
getBBox = (callback: () => void, options?: SVGBoundingBoxOptions) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
const handle = findNodeHandle(this.root as Component);
|
||||
RNSVGRenderableManager.getBBox(handle, options, callback); // TODO
|
||||
};
|
||||
getCTM = (callback: () => void) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
const handle = findNodeHandle(this.root as Component);
|
||||
RNSVGRenderableManager.getCTM(handle, callback); // TODO
|
||||
};
|
||||
getScreenCTM = (callback: () => void) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
const handle = findNodeHandle(this.root as Component);
|
||||
RNSVGRenderableManager.getScreenCTM(handle, callback); // TODO
|
||||
};
|
||||
isPointInFill = (callback: () => void, options: DOMPointInit) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
const handle = findNodeHandle(this.root as Component);
|
||||
RNSVGRenderableManager.isPointInFill(handle, options, callback);
|
||||
};
|
||||
isPointInStroke = (callback: () => void, options: DOMPointInit) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
const handle = findNodeHandle(this.root as Component);
|
||||
RNSVGRenderableManager.isPointInStroke(handle, options, callback); // TODO
|
||||
};
|
||||
getTotalLength = (callback: () => void) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
const handle = findNodeHandle(this.root as Component);
|
||||
RNSVGRenderableManager.getTotalLength(handle, callback); // TODO
|
||||
};
|
||||
getPointAtLength = (callback: () => void, options: Object) => {
|
||||
if (!callback) {
|
||||
return;
|
||||
}
|
||||
const handle = findNodeHandle(this.root as Component);
|
||||
RNSVGRenderableManager.getPointAtLength(handle, options, callback); // TODO
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user