mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-23 07:35:21 +00:00
First attempt at nested svg support.
This commit is contained in:
@@ -12,6 +12,8 @@ package com.horcrux.svg;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
|
||||
/**
|
||||
* Shadow node for virtual Defs view
|
||||
*/
|
||||
@@ -20,9 +22,13 @@ class DefsShadowNode extends DefinitionShadowNode {
|
||||
@Override
|
||||
public void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
NodeRunnable markUpdateSeenRecursive = new NodeRunnable() {
|
||||
public void run(VirtualNode node) {
|
||||
public void run(LayoutShadowNode node) {
|
||||
node.markUpdateSeen();
|
||||
node.traverseChildren(this);
|
||||
if (node instanceof VirtualNode) {
|
||||
((VirtualNode) node).traverseChildren(this);
|
||||
} else if (node instanceof SvgViewShadowNode) {
|
||||
((SvgViewShadowNode) node).traverseChildren(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
traverseChildren(markUpdateSeenRecursive);
|
||||
@@ -30,8 +36,10 @@ class DefsShadowNode extends DefinitionShadowNode {
|
||||
|
||||
void saveDefinition() {
|
||||
traverseChildren(new NodeRunnable() {
|
||||
public void run(VirtualNode node) {
|
||||
node.saveDefinition();
|
||||
public void run(LayoutShadowNode node) {
|
||||
if (node instanceof VirtualNode) {
|
||||
((VirtualNode)node).saveDefinition();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import android.graphics.Point;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
import com.facebook.react.uimanager.ReactShadowNode;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
@@ -72,7 +73,9 @@ class GroupShadowNode extends RenderableShadowNode {
|
||||
final SvgViewShadowNode svg = getSvgShadowNode();
|
||||
final GroupShadowNode self = this;
|
||||
traverseChildren(new NodeRunnable() {
|
||||
public void run(VirtualNode node) {
|
||||
public void run(LayoutShadowNode lNode) {
|
||||
if (lNode instanceof VirtualNode) {
|
||||
VirtualNode node = ((VirtualNode)lNode);
|
||||
if (node instanceof RenderableShadowNode) {
|
||||
((RenderableShadowNode)node).mergeProperties(self);
|
||||
}
|
||||
@@ -90,6 +93,10 @@ class GroupShadowNode extends RenderableShadowNode {
|
||||
if (node.isResponsible()) {
|
||||
svg.enableTouchEvents();
|
||||
}
|
||||
} else if (lNode instanceof SvgViewShadowNode) {
|
||||
SvgViewShadowNode svgView = (SvgViewShadowNode)lNode;
|
||||
svgView.drawChildren(canvas);
|
||||
}
|
||||
}
|
||||
});
|
||||
popGlyphContext();
|
||||
@@ -104,8 +111,10 @@ class GroupShadowNode extends RenderableShadowNode {
|
||||
final Path path = new Path();
|
||||
|
||||
traverseChildren(new NodeRunnable() {
|
||||
public void run(VirtualNode node) {
|
||||
path.addPath(node.getPath(canvas, paint));
|
||||
public void run(LayoutShadowNode node) {
|
||||
if (node instanceof VirtualNode) {
|
||||
path.addPath(((VirtualNode)node).getPath(canvas, paint));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -154,8 +163,10 @@ class GroupShadowNode extends RenderableShadowNode {
|
||||
}
|
||||
|
||||
traverseChildren(new NodeRunnable() {
|
||||
public void run(VirtualNode node) {
|
||||
node.saveDefinition();
|
||||
public void run(LayoutShadowNode node) {
|
||||
if (node instanceof VirtualNode) {
|
||||
((VirtualNode)node).saveDefinition();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -163,7 +174,7 @@ class GroupShadowNode extends RenderableShadowNode {
|
||||
@Override
|
||||
public void resetProperties() {
|
||||
traverseChildren(new NodeRunnable() {
|
||||
public void run(VirtualNode node) {
|
||||
public void run(LayoutShadowNode node) {
|
||||
if (node instanceof RenderableShadowNode) {
|
||||
((RenderableShadowNode)node).resetProperties();
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ public class SvgViewShadowNode extends LayoutShadowNode {
|
||||
private float mMinY;
|
||||
private float mVbWidth;
|
||||
private float mVbHeight;
|
||||
private String mbbWidth;
|
||||
private String mbbHeight;
|
||||
private String mAlign;
|
||||
private int mMeetOrSlice;
|
||||
private Matrix mViewBoxMatrix;
|
||||
@@ -77,6 +79,18 @@ public class SvgViewShadowNode extends LayoutShadowNode {
|
||||
markUpdated();
|
||||
}
|
||||
|
||||
@ReactProp(name = "bbWidth")
|
||||
public void setVbWidth(String bbWidth) {
|
||||
mbbWidth = bbWidth;
|
||||
markUpdated();
|
||||
}
|
||||
|
||||
@ReactProp(name = "bbHeight")
|
||||
public void setVbHeight(String bbHeight) {
|
||||
mbbHeight = bbHeight;
|
||||
markUpdated();
|
||||
}
|
||||
|
||||
@ReactProp(name = "align")
|
||||
public void setAlign(String align) {
|
||||
mAlign = align;
|
||||
@@ -117,8 +131,7 @@ public class SvgViewShadowNode extends LayoutShadowNode {
|
||||
(int) getLayoutHeight(),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
|
||||
mCanvas = new Canvas(bitmap);
|
||||
drawChildren(mCanvas);
|
||||
drawChildren(new Canvas(bitmap));
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@@ -126,11 +139,21 @@ public class SvgViewShadowNode extends LayoutShadowNode {
|
||||
return mCanvas.getClipBounds();
|
||||
}
|
||||
|
||||
private void drawChildren(final Canvas canvas) {
|
||||
|
||||
void drawChildren(final Canvas canvas) {
|
||||
mCanvas = canvas;
|
||||
if (mAlign != null) {
|
||||
RectF vbRect = getViewBox();
|
||||
RectF eRect = new RectF(0, 0, getLayoutWidth(), getLayoutHeight());
|
||||
float width = getLayoutWidth();
|
||||
float height = getLayoutHeight();
|
||||
boolean nested = Float.isNaN(width) || Float.isNaN(height);
|
||||
if (nested) {
|
||||
width = Float.parseFloat(mbbWidth) * mScale;
|
||||
height = Float.parseFloat(mbbHeight) * mScale;
|
||||
}
|
||||
RectF eRect = new RectF(0,0, width, height);
|
||||
if (nested) {
|
||||
canvas.clipRect(eRect);
|
||||
}
|
||||
mViewBoxMatrix = ViewBox.getTransform(vbRect, eRect, mAlign, mMeetOrSlice);
|
||||
canvas.concat(mViewBoxMatrix);
|
||||
}
|
||||
@@ -143,13 +166,17 @@ public class SvgViewShadowNode extends LayoutShadowNode {
|
||||
|
||||
|
||||
traverseChildren(new VirtualNode.NodeRunnable() {
|
||||
public void run(VirtualNode node) {
|
||||
node.saveDefinition();
|
||||
public void run(LayoutShadowNode node) {
|
||||
if (node instanceof VirtualNode) {
|
||||
((VirtualNode)node).saveDefinition();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
traverseChildren(new VirtualNode.NodeRunnable() {
|
||||
public void run(VirtualNode node) {
|
||||
public void run(LayoutShadowNode lNode) {
|
||||
if (lNode instanceof VirtualNode) {
|
||||
VirtualNode node = (VirtualNode)lNode;
|
||||
int count = node.saveAndSetupCanvas(canvas);
|
||||
node.draw(canvas, paint, 1f);
|
||||
node.restoreCanvas(canvas, count);
|
||||
@@ -159,6 +186,7 @@ public class SvgViewShadowNode extends LayoutShadowNode {
|
||||
mResponsible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -238,7 +266,7 @@ public class SvgViewShadowNode extends LayoutShadowNode {
|
||||
continue;
|
||||
}
|
||||
|
||||
runner.run((VirtualNode) child);
|
||||
runner.run((LayoutShadowNode) child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import android.graphics.Path;
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
import com.facebook.react.uimanager.ReactShadowNode;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
@@ -177,9 +178,10 @@ class TextShadowNode extends GroupShadowNode {
|
||||
|
||||
void releaseCachedPath() {
|
||||
traverseChildren(new NodeRunnable() {
|
||||
public void run(VirtualNode node) {
|
||||
TextShadowNode text = (TextShadowNode)node;
|
||||
text.releaseCachedPath();
|
||||
public void run(LayoutShadowNode node) {
|
||||
if (node instanceof TextShadowNode) {
|
||||
((TextShadowNode)node).releaseCachedPath();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -308,17 +308,17 @@ abstract class VirtualNode extends LayoutShadowNode {
|
||||
}
|
||||
|
||||
interface NodeRunnable {
|
||||
void run(VirtualNode node);
|
||||
void run(LayoutShadowNode node);
|
||||
}
|
||||
|
||||
void traverseChildren(NodeRunnable runner) {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
ReactShadowNode child = getChildAt(i);
|
||||
if (!(child instanceof VirtualNode)) {
|
||||
if (!(child instanceof VirtualNode) && !(child instanceof SvgViewShadowNode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
runner.run((VirtualNode) child);
|
||||
runner.run((LayoutShadowNode) child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,14 +74,19 @@ class Svg extends Component{
|
||||
|
||||
if (width && height) {
|
||||
dimensions = {
|
||||
width: +width,
|
||||
height: +height,
|
||||
width: width[width.length - 1] === '%' ? width : +width,
|
||||
height: height[height.length - 1] === '%' ? height : +height,
|
||||
flex: 0
|
||||
};
|
||||
}
|
||||
|
||||
const w = `${width}`;
|
||||
const h = `${height}`;
|
||||
|
||||
return <NativeSvgView
|
||||
{...props}
|
||||
bbWidth={w}
|
||||
bbHeight={h}
|
||||
{...extractViewBox({ viewBox, preserveAspectRatio })}
|
||||
ref={ele => {this.root = ele;}}
|
||||
style={[
|
||||
@@ -98,7 +103,9 @@ class Svg extends Component{
|
||||
|
||||
const NativeSvgView = requireNativeComponent('RNSVGSvgView', null, {
|
||||
nativeOnly: {
|
||||
...ViewBoxAttributes
|
||||
...ViewBoxAttributes,
|
||||
width: true,
|
||||
height: true,
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
- (void)parseReference
|
||||
{
|
||||
[self traverseSubviews:^(RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
[node parseReference];
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -34,8 +34,10 @@
|
||||
{
|
||||
[self pushGlyphContext];
|
||||
RNSVGSvgView* svg = [self getSvgView];
|
||||
[self traverseSubviews:^(RNSVGNode *node) {
|
||||
if (node.responsible && !svg.responsible) {
|
||||
[self traverseSubviews:^(UIView *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
RNSVGNode* svgNode = (RNSVGNode*)node;
|
||||
if (svgNode.responsible && !svg.responsible) {
|
||||
svg.responsible = YES;
|
||||
}
|
||||
|
||||
@@ -43,11 +45,19 @@
|
||||
[(RNSVGRenderable*)node mergeProperties:self];
|
||||
}
|
||||
|
||||
[node renderTo:context];
|
||||
[svgNode renderTo:context];
|
||||
|
||||
if ([node isKindOfClass:[RNSVGRenderable class]]) {
|
||||
[(RNSVGRenderable*)node resetProperties];
|
||||
}
|
||||
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
|
||||
RNSVGSvgView* svgView = (RNSVGSvgView*)node;
|
||||
CGRect rect = CGRectMake(0, 0, [svgView.bbWidth floatValue], [svgView.bbHeight floatValue]);
|
||||
CGContextClipToRect(context, rect);
|
||||
[svgView drawToContext:context withRect:(CGRect)rect];
|
||||
} else {
|
||||
RCTLogWarn(@"Not a RNSVGNode: %@", node.class);
|
||||
}
|
||||
|
||||
return YES;
|
||||
}];
|
||||
@@ -89,8 +99,10 @@
|
||||
{
|
||||
CGMutablePathRef __block path = CGPathCreateMutable();
|
||||
[self traverseSubviews:^(RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
CGAffineTransform transform = node.matrix;
|
||||
CGPathAddPath(path, &transform, [node getPath:context]);
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
|
||||
@@ -147,7 +159,9 @@
|
||||
}
|
||||
|
||||
[self traverseSubviews:^(__kindof RNSVGNode *node) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
[node parseReference];
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
@interface RNSVGSvgView : UIView <RNSVGContainer>
|
||||
|
||||
@property (nonatomic, strong) NSString *bbWidth;
|
||||
@property (nonatomic, strong) NSString *bbHeight;
|
||||
@property (nonatomic, assign) CGFloat minX;
|
||||
@property (nonatomic, assign) CGFloat minY;
|
||||
@property (nonatomic, assign) CGFloat vbWidth;
|
||||
@@ -22,6 +24,8 @@
|
||||
@property (nonatomic, strong) NSString *align;
|
||||
@property (nonatomic, assign) RNSVGVBMOS meetOrSlice;
|
||||
@property (nonatomic, assign) BOOL responsible;
|
||||
@property (nonatomic, assign) CGRect boundingBox;
|
||||
|
||||
|
||||
/**
|
||||
* define <ClipPath></ClipPath> content as clipPath template.
|
||||
@@ -42,4 +46,8 @@
|
||||
|
||||
- (CGRect)getContextBounds;
|
||||
|
||||
- (void)drawRect:(CGRect)rect;
|
||||
|
||||
- (void)drawToContext:(CGContextRef)context withRect:(CGRect)rect;
|
||||
|
||||
@end
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_clipPaths;
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *_templates;
|
||||
NSMutableDictionary<NSString *, RNSVGPainter *> *_painters;
|
||||
CGRect _boundingBox;
|
||||
CGAffineTransform _viewBoxTransform;
|
||||
}
|
||||
|
||||
@@ -83,6 +82,26 @@
|
||||
_vbHeight = vbHeight;
|
||||
}
|
||||
|
||||
- (void)setBBWidth:(NSString *)bbWidth
|
||||
{
|
||||
if ([bbWidth isEqualToString:_bbWidth]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_bbWidth = bbWidth;
|
||||
}
|
||||
|
||||
- (void)setBBHeight:(NSString *)bbHeight
|
||||
{
|
||||
if ([bbHeight isEqualToString:_bbHeight]) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidate];
|
||||
_bbHeight = bbHeight;
|
||||
}
|
||||
|
||||
- (void)setAlign:(NSString *)align
|
||||
{
|
||||
if ([align isEqualToString:_align]) {
|
||||
@@ -103,13 +122,7 @@
|
||||
_meetOrSlice = meetOrSlice;
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
_clipPaths = nil;
|
||||
_templates = nil;
|
||||
_painters = nil;
|
||||
_boundingBox = rect;
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
- (void)drawToContext:(CGContextRef)context withRect:(CGRect)rect {
|
||||
|
||||
if (self.align) {
|
||||
_viewBoxTransform = [RNSVGViewBox getTransform:CGRectMake(self.minX, self.minY, self.vbWidth, self.vbHeight)
|
||||
@@ -119,21 +132,36 @@
|
||||
CGContextConcatCTM(context, _viewBoxTransform);
|
||||
}
|
||||
|
||||
for (RNSVGNode *node in self.subviews) {
|
||||
for (UIView *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
if (node.responsible && !self.responsible) {
|
||||
RNSVGNode *svg = (RNSVGNode *)node;
|
||||
[svg renderTo:context];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
_clipPaths = nil;
|
||||
_templates = nil;
|
||||
_painters = nil;
|
||||
_boundingBox = rect;
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
for (UIView *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
RNSVGNode *svg = (RNSVGNode *)node;
|
||||
if (svg.responsible && !self.responsible) {
|
||||
self.responsible = YES;
|
||||
}
|
||||
|
||||
[node parseReference];
|
||||
[svg parseReference];
|
||||
} else {
|
||||
RCTLogWarn(@"Not a RNSVGNode: %@", node.class);
|
||||
}
|
||||
}
|
||||
|
||||
for (RNSVGNode *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
[node renderTo:context];
|
||||
}
|
||||
}
|
||||
[self drawToContext:context withRect:rect];
|
||||
}
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
|
||||
@@ -103,6 +103,6 @@ extern CGFloat const RNSVG_DEFAULT_FONT_SIZE;
|
||||
|
||||
- (void)endTransparencyLayer:(CGContextRef)context;
|
||||
|
||||
- (void)traverseSubviews:(BOOL (^)(__kindof RNSVGNode *node))block;
|
||||
- (void)traverseSubviews:(BOOL (^)(__kindof UIView *node))block;
|
||||
|
||||
@end
|
||||
|
||||
@@ -310,13 +310,19 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)traverseSubviews:(BOOL (^)(__kindof RNSVGNode *node))block
|
||||
- (void)traverseSubviews:(BOOL (^)(__kindof UIView *node))block
|
||||
{
|
||||
for (RNSVGNode *node in self.subviews) {
|
||||
for (UIView *node in self.subviews) {
|
||||
if ([node isKindOfClass:[RNSVGNode class]]) {
|
||||
if (!block(node)) {
|
||||
break;
|
||||
}
|
||||
} else if ([node isKindOfClass:[RNSVGSvgView class]]) {
|
||||
if (!block(node)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
RCTLogWarn(@"Not a RNSVGNode: %@", node.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ RCT_EXPORT_MODULE()
|
||||
return [RNSVGSvgView new];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(bbWidth, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(bbHeight, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(minX, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(minY, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(vbWidth, CGFloat)
|
||||
|
||||
Reference in New Issue
Block a user