diff --git a/Example/examples/Clipping.js b/Example/examples/Clipping.js index 23f41579..47eebae2 100644 --- a/Example/examples/Clipping.js +++ b/Example/examples/Clipping.js @@ -213,7 +213,7 @@ const icon = ; -const samples = [ClipPathAttr, ClipRule];//, ClipPathElement, TextClipping +const samples = [ClipPathAttr, ClipRule, ClipPathElement, TextClipping]; export { icon, diff --git a/README.md b/README.md index b057d450..8bf8dfde 100644 --- a/README.md +++ b/README.md @@ -564,7 +564,7 @@ npm install 6. more Text features support 7. Pattern element 8. Image element - +9. calculate bounding box only if necessary. #### Thanks: diff --git a/elements/ClipPath.js b/elements/ClipPath.js index cb9644e7..240afd6f 100644 --- a/elements/ClipPath.js +++ b/elements/ClipPath.js @@ -28,26 +28,13 @@ class ClipPath extends Component{ remove(this.id); }; - _combinePaths = children => { - // TODO: combine g elements and their children - // TODO: combine text elements - Children.forEach(children, child => { - let {props, type: {getPath}} = child; - - if (getPath) { - this._path += getPath(props); - } - - this._combinePaths(props.children); - }); - }; - - _path = ''; - render() { - this._combinePaths(this.props.children); - set(this.id, this._path); - return ; + set(this.id, this.id); + + return {this.props.children}; } } diff --git a/elements/Defs.js b/elements/Defs.js index e8699205..15e45afb 100644 --- a/elements/Defs.js +++ b/elements/Defs.js @@ -80,11 +80,16 @@ class Defs extends Component{ static Item = DefsItem; static Use = DefsUse; + shouldRender = false; getChildren = () => { return Children.map(this.props.children, child => { let {type} = child; if (type === LinearGradient || type === RadialGradient || type === ClipPath) { + if (type === ClipPath) { + this.shouldRender = true; + } + return cloneElement(child, { svgId: this.props.svgId }); @@ -99,8 +104,9 @@ class Defs extends Component{ }; render() { - return - {this.getChildren()} + let children = this.getChildren(); + return + {children} ; } } diff --git a/elements/G.js b/elements/G.js index b08eeb91..7cdcd7ff 100644 --- a/elements/G.js +++ b/elements/G.js @@ -47,6 +47,7 @@ class G extends Component{ } else { return {this.props.children} ; diff --git a/elements/Shape.js b/elements/Shape.js index 741bcbf9..15b77e4f 100644 --- a/elements/Shape.js +++ b/elements/Shape.js @@ -58,7 +58,7 @@ class Shape extends Component{ let shape = new SerializableShape(props, COORD_PROPS[this.type]).toJSON(); - return {props.children} diff --git a/elements/ViewBox.js b/elements/ViewBox.js index 9ebc55d2..8fb69a75 100644 --- a/elements/ViewBox.js +++ b/elements/ViewBox.js @@ -7,12 +7,7 @@ import G from './G'; import extractViewbox from '../lib/extract/extractViewbox'; class ViewBox extends Component{ static displayName = 'ViewBox'; - static propType = { - shouldTransform: PropTypes.bool - }; - static defaultProps = { - shouldTransform: false - }; + render() { let viewbox = extractViewbox(this.props); let scaleX = 1; diff --git a/ios/RNSVGGroup.h b/ios/RNSVGGroup.h index f55512e7..3a0828e2 100644 --- a/ios/RNSVGGroup.h +++ b/ios/RNSVGGroup.h @@ -13,8 +13,7 @@ #import "RNSVGCGFCRule.h" @interface RNSVGGroup : RNSVGNode +@property (nonatomic, strong) NSString *asClipPath; // Current group is a element and asClipPath is its id. -@property (nonatomic, assign) CGPathRef clipPath; -@property (nonatomic, assign) RNSVGCGFCRule clipRule; @end diff --git a/ios/RNSVGGroup.m b/ios/RNSVGGroup.m index 41190747..1c7701aa 100644 --- a/ios/RNSVGGroup.m +++ b/ios/RNSVGGroup.m @@ -12,10 +12,24 @@ - (void)renderLayerTo:(CGContextRef)context { - [self clip:context]; - for (RNSVGNode *node in self.subviews) { - [node renderTo:context]; + if (self.asClipPath == NULL) { + [self clip:context]; + for (RNSVGNode *node in self.subviews) { + [node renderTo:context]; + } + } else { + [self defineClipPath:[self getPath:context] clipPathId:self.asClipPath]; } } +- (CGPathRef)getPath:(CGContextRef)context +{ + CGMutablePathRef path = CGPathCreateMutable(); + for (RNSVGNode *node in self.subviews) { + CGPathAddPath(path, nil, [node getPath:context]); + } + + return path; +} + @end diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h index 40c64830..db6e0572 100644 --- a/ios/RNSVGNode.h +++ b/ios/RNSVGNode.h @@ -20,8 +20,9 @@ @property (nonatomic, assign) CGRect rect; @property (nonatomic, assign) CGFloat opacity; -@property (nonatomic, assign) CGPathRef clipPath; @property (nonatomic, assign) RNSVGCGFCRule clipRule; +@property (nonatomic, assign) CGPathRef clipPath; // convert clipPath="M0,0 L0,10 L10,10z" into path +@property (nonatomic, strong) NSString *clipPathId; // use clipPath="url(#clip)" as ClipPath - (void)invalidate; - (void)renderTo:(CGContextRef)context; @@ -33,6 +34,19 @@ */ - (void)renderLayerTo:(CGContextRef)context; +/** + * clip node by clipPath or clipPathId. + */ - (void)clip:(CGContextRef)context; +/** + * define content as clipPath template. + */ +- (void)defineClipPath:(CGPathRef)clipPath clipPathId:(NSString *)clipPathId; + +/** + * getPath will return the path inside node as a ClipPath. + */ +- (CGPathRef)getPath: (CGContextRef) context; + @end diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m index 533c601c..9a88d042 100644 --- a/ios/RNSVGNode.m +++ b/ios/RNSVGNode.m @@ -10,6 +10,8 @@ #import "RNSVGContainer.h" +static NSMutableDictionary *ClipPaths; + @implementation RNSVGNode - (void)insertSubview:(UIView *)subview atIndex:(NSInteger)index @@ -71,6 +73,17 @@ } - (void)setClipPath:(CGPathRef)clipPath +{ + if (_clipPath == clipPath) { + return; + } + [self invalidate]; + CGPathRelease(_clipPath); + _clipPath = CGPathRetain(clipPath); +} + + +-(void)defineClipPath:(CGPathRef)clipPath clipPathId:(NSString *)clipPathId { if (clipPath == _clipPath) { return; @@ -78,6 +91,11 @@ [self invalidate]; CGPathRelease(_clipPath); _clipPath = CGPathRetain(clipPath); + if (ClipPaths == NULL) { + ClipPaths = [[NSMutableDictionary alloc] init]; + } + + [ClipPaths setValue:[NSValue valueWithPointer:_clipPath] forKey:clipPathId]; } - (void)dealloc @@ -91,15 +109,29 @@ // abstract } +- (CGPathRef)getPath: (CGContextRef) context +{ + // abstract + return CGPathCreateMutable(); +} + - (void)clip:(CGContextRef)context { + CGPathRef clipPath = nil; + if (self.clipPath) { - CGContextAddPath(context, self.clipPath); - if (self.clipRule == kRNSVGCGFCRuleEvenodd) { - CGContextEOClip(context); - } else { - CGContextClip(context); - } + clipPath = self.clipPath; + } else if (self.clipPathId) { + clipPath = [[ClipPaths valueForKey:self.clipPathId] pointerValue]; + } else { + return; + } + + CGContextAddPath(context, clipPath); + if (self.clipRule == kRNSVGCGFCRuleEvenodd) { + CGContextEOClip(context); + } else { + CGContextClip(context); } } diff --git a/ios/RNSVGPath.m b/ios/RNSVGPath.m index e928f639..dd88a55c 100644 --- a/ios/RNSVGPath.m +++ b/ios/RNSVGPath.m @@ -39,9 +39,7 @@ fillColor = [self.fill applyFillColor:context]; if (!fillColor) { - if (self.clipPath) { - [self clip:context]; - } + [self clip:context]; CGContextSaveGState(context); CGContextAddPath(context, self.d); @@ -97,4 +95,9 @@ CGContextDrawPath(context, mode); } +- (CGPathRef)getPath:(CGContextRef)context +{ + return self.d; +} + @end diff --git a/ios/RNSVGShape.m b/ios/RNSVGShape.m index 6137d2e6..25f673e4 100644 --- a/ios/RNSVGShape.m +++ b/ios/RNSVGShape.m @@ -11,13 +11,15 @@ @implementation RNSVGShape -- (void)dealloc -{ - -} - - (void)renderLayerTo:(CGContextRef)context { + self.d = [self getPath: context]; + [super renderLayerTo:context]; +} + +- (CGPathRef)getPath:(CGContextRef)context +{ + int type = [[self.shape objectForKey:@"type"] intValue]; CGRect box = CGContextGetClipBoundingBox(context); CGMutablePathRef path = CGPathCreateMutable(); @@ -39,7 +41,7 @@ CGFloat value = [[prop objectForKey:@"value"] floatValue]; if ([[prop objectForKey:@"percentage"] integerValue] == 1) { r = sqrt(pow((width * value), 2) + pow((height * value), 2)) / sqrt(2); - + } else { r = value; } @@ -104,13 +106,9 @@ RCTLogError(@"Invalid Shape type %d at %@", type, self.shape); //CGPathRelease(path); - } - self.d = path; - [super renderLayerTo:context]; - //NSLog(@"%@", NSStringFromCGRect(box)); - //NSLog(@"%@", self.shape); + return path; } - (CGFloat)getActualProp:(NSString *)name relative:(float)relative diff --git a/ios/ViewManagers/RNSVGGroupManager.m b/ios/ViewManagers/RNSVGGroupManager.m index fa2daa31..a2744c8a 100644 --- a/ios/ViewManagers/RNSVGGroupManager.m +++ b/ios/ViewManagers/RNSVGGroupManager.m @@ -22,5 +22,6 @@ RCT_EXPORT_MODULE() RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath) RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule) +RCT_EXPORT_VIEW_PROPERTY(asClipPath, NSString) @end diff --git a/ios/ViewManagers/RNSVGNodeManager.m b/ios/ViewManagers/RNSVGNodeManager.m index 0333bf6f..08ec88a8 100644 --- a/ios/ViewManagers/RNSVGNodeManager.m +++ b/ios/ViewManagers/RNSVGNodeManager.m @@ -31,5 +31,6 @@ RCT_EXPORT_MODULE() RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat) RCT_EXPORT_VIEW_PROPERTY(transform, CGAffineTransform) +RCT_EXPORT_VIEW_PROPERTY(clipPathId, NSString) @end diff --git a/lib/attributes.js b/lib/attributes.js index cc4623e2..dfcde8a2 100644 --- a/lib/attributes.js +++ b/lib/attributes.js @@ -63,10 +63,12 @@ const NodeAttributes = { clipPath: { diff: arrayDiffer }, + clipPathId: true, clipRule: true }; const GroupAttributes = { + asClipPath: true, ...NodeAttributes }; diff --git a/lib/extract/extractClipping.js b/lib/extract/extractClipping.js index db0dc3f8..45245a41 100644 --- a/lib/extract/extractClipping.js +++ b/lib/extract/extractClipping.js @@ -29,9 +29,9 @@ export default function (props) { let patternName = `${matched[1]}:${props.svgId}`; let pattern = clipPatterns[patternName]; if (pattern) { - clippingProps.clipPath = new SerializablePath(pattern).toJSON(); + clippingProps.clipPathId = pattern; } else { - clippingProps = {}; + clippingProps = null; // TODO: warn } } else {