From dd6cb80e84b88116e432e3ab483d5a40984caa85 Mon Sep 17 00:00:00 2001 From: Horcrux Date: Tue, 19 Jul 2016 23:09:51 +0800 Subject: [PATCH] refactor Use and Defs element with native code support(iOS) --- Example/examples.js | 19 +--- Example/examples/Definations.js | 51 +++++++++ Example/main.js | 3 +- elements/Circle.js | 4 +- elements/Defs.js | 105 ++---------------- elements/Ellipse.js | 4 +- elements/G.js | 44 ++------ elements/Line.js | 4 +- elements/Path.js | 27 +---- elements/Rect.js | 4 +- elements/Shape.js | 4 +- elements/Text.js | 24 +--- elements/Use.js | 54 +++++++-- ios/Elements/RNSVGClipPath.h | 1 - ios/Elements/RNSVGClipPath.m | 13 ++- ios/Elements/RNSVGDefination.h | 21 ++++ ios/Elements/RNSVGDefination.m | 27 +++++ ios/Elements/RNSVGGroup.m | 27 ++++- ios/Elements/RNSVGSvgView.h | 10 +- ios/Elements/RNSVGSvgView.m | 32 +++++- ios/Elements/RNSVGUse.h | 19 ++++ ios/Elements/RNSVGUse.m | 26 +++++ ios/RNSVG.xcodeproj/project.pbxproj | 24 ++++ ios/RNSVGNode.h | 34 +++++- ios/RNSVGNode.m | 86 ++++++++++---- ios/RNSVGRenderable.m | 38 ++++++- ios/Text/RNSVGText.m | 6 +- ios/Text/RnSVGGlyphCache.m | 8 +- ios/Utils/RNSVGPercentageConverter.m | 2 +- ios/ViewManagers/RNSVGDefinationManager.h | 15 +++ ios/ViewManagers/RNSVGDefinationManager.m | 31 ++++++ ios/ViewManagers/RNSVGNodeManager.m | 3 +- ...{RNSVGShapeManager.h => RNSVGUseManager.h} | 2 +- ...{RNSVGShapeManager.m => RNSVGUseManager.m} | 15 ++- lib/attributes.js | 9 +- lib/extract/extractProps.js | 14 ++- lib/props.js | 7 +- 37 files changed, 535 insertions(+), 282 deletions(-) create mode 100644 Example/examples/Definations.js create mode 100644 ios/Elements/RNSVGDefination.h create mode 100644 ios/Elements/RNSVGDefination.m create mode 100644 ios/Elements/RNSVGUse.h create mode 100644 ios/Elements/RNSVGUse.m create mode 100644 ios/ViewManagers/RNSVGDefinationManager.h create mode 100644 ios/ViewManagers/RNSVGDefinationManager.m rename ios/ViewManagers/{RNSVGShapeManager.h => RNSVGUseManager.h} (82%) rename ios/ViewManagers/{RNSVGShapeManager.m => RNSVGUseManager.m} (52%) diff --git a/Example/examples.js b/Example/examples.js index da57257a..a0108eea 100644 --- a/Example/examples.js +++ b/Example/examples.js @@ -14,24 +14,9 @@ import * as Symbol from './examples/Symbol'; import * as Gradients from './examples/Gradients'; import * as Clipping from './examples/Clipping'; import * as Image from './examples/Image'; +import * as Definations from './examples/Definations'; import * as TouchEvents from './examples/TouchEvents'; export { - Svg, - Rect, - Circle, - Ellipse, - Line, - Polygon, - Polyline, - Path, - Text, - Stroking, - G, - Use, - Symbol, - Gradients, - Clipping, - Image, - TouchEvents + Definations }; diff --git a/Example/examples/Definations.js b/Example/examples/Definations.js new file mode 100644 index 00000000..18dfea41 --- /dev/null +++ b/Example/examples/Definations.js @@ -0,0 +1,51 @@ +import React, { + Component +} from 'react'; + +import Svg, { + Defs, + G, + Path, + Use, + Rect +} from 'react-native-svg'; + +class DefsExample extends Component{ + static title = 'basic Defs usage'; + + render() { + return + + + + + + + + ; + } +} + +const icon = + + + + + + +; + + + +const samples = [DefsExample]; + +export { + icon, + samples +}; diff --git a/Example/main.js b/Example/main.js index 5db80fd7..d1ea1a18 100644 --- a/Example/main.js +++ b/Example/main.js @@ -111,7 +111,8 @@ const styles = StyleSheet.create({ } }); -const names = ['Svg', 'Stroking', 'Path', 'Line', 'Rect', 'Polygon', 'Polyline', 'Circle', 'Ellipse', 'G', 'Text', 'Use', 'Symbol', 'Gradients', 'Clipping', 'Image', 'TouchEvents']; +//const names = ['Svg', 'Stroking', 'Path', 'Line', 'Rect', 'Polygon', 'Polyline', 'Circle', 'Ellipse', 'G', 'Text', 'Use', 'Symbol', 'Gradients', 'Clipping', 'Image', 'TouchEvents']; +const names = ['Definations']; class SvgExample extends Component { constructor() { diff --git a/elements/Circle.js b/elements/Circle.js index 4d0b125f..d8fd363e 100644 --- a/elements/Circle.js +++ b/elements/Circle.js @@ -15,9 +15,7 @@ class Circle extends Shape { static contextTypes = { ...fillProps, ...strokeProps, - ...circleProps, - isInGroup: PropTypes.bool, - svgId: numberProp + ...circleProps }; setNativeProps = (...args) => { diff --git a/elements/Defs.js b/elements/Defs.js index 5fc7da09..d18bb699 100644 --- a/elements/Defs.js +++ b/elements/Defs.js @@ -1,102 +1,19 @@ -import React, {Children, Component, cloneElement, PropTypes} from 'react'; -import {NativeGroup} from './G'; -let map = {}; +import React, { + Component, +} from 'react' +import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass'; -import LinearGradient from './LinearGradient'; -import RadialGradient from './RadialGradient'; -import ClipPath from './ClipPath'; -let onlyChild = Children.only; - -class DefsItem extends Component{ - static displayName = 'DefsItem'; - static propType = { - visible: PropTypes.bool - }; - - static defaultProps = { - visible: false - }; - - constructor() { - super(...arguments); - this.id = this.props.id + ':' + this.props.svgId; - map[this.id] = cloneElement(onlyChild(this.props.children), { - id: null - }); - } - - componentWillReceiveProps = nextProps => { - let id = nextProps.id + ':' + nextProps.svgId; - if (id !== this.id) { - delete map[this.id]; - } - map[id] = cloneElement(onlyChild(nextProps.children), { - id: null - }); - }; - - componentWillUnmount = () => { - delete map[this.id]; - }; - - render() { - return this.props.visible ? onlyChild(this.props.children) : ; - } -} - -let idReg = /^#(.+)/; -class DefsUse extends Component{ - static displayName = 'DefsUse'; - static propType = { - href: PropTypes.string - }; - render() { - let href = this.props.href; - if (href) { - let matched = href.match(idReg); - if (matched) { - let template = map[matched[1] + ':' + this.props.svgId]; - if (template) { - return cloneElement(template, { - ...this.props, - href: null - }); - } - } - } - - console.warn(`Invalid href: '${href}' for Use element.\n Please check if '${href}' if defined`); - return ; - } -} - -// TODO: defination scope, global or local? -class Defs extends Component{ +class Defs extends Component { static displayName = 'Defs'; - static Item = DefsItem; - static Use = DefsUse; - - getChildren = () => { - return Children.map(this.props.children, child => { - let {type} = child; - - if (type === LinearGradient || type === RadialGradient || type === ClipPath) { - return cloneElement(child, { - svgId: this.props.svgId - }); - } - if (child.props.id) { - return {child}; - } - }); - }; render() { - return {this.getChildren()}; + return {this.props.children}; } } +const RNSVGDefination = createReactNativeComponentClass({ + validAttributes: {}, + uiViewClassName: 'RNSVGDefination' +}); + export default Defs; diff --git a/elements/Ellipse.js b/elements/Ellipse.js index 31eb41b4..7beece7f 100644 --- a/elements/Ellipse.js +++ b/elements/Ellipse.js @@ -15,9 +15,7 @@ class Ellipse extends Shape{ static contextTypes = { ...fillProps, ...strokeProps, - ...ellipseProps, - isInGroup: PropTypes.bool, - svgId: numberProp + ...ellipseProps }; setNativeProps = (...args) => { diff --git a/elements/G.js b/elements/G.js index 9c7fdcd2..94d34dbe 100644 --- a/elements/G.js +++ b/elements/G.js @@ -1,5 +1,4 @@ import React, {Component, PropTypes} from 'react'; -import Defs from './Defs'; import _ from 'lodash'; import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass'; import {numberProp, contextProps} from '../lib/props'; @@ -10,13 +9,10 @@ class G extends Component{ static displayName = 'G'; static contextTypes = { - svgId: numberProp, ...contextProps }; static childContextTypes = { - svgId: numberProp, - isInGroup: PropTypes.bool, ...contextProps }; @@ -26,43 +22,23 @@ class G extends Component{ props[key] = this.props[key]; } return props; - }, { - svgId: this.props.svgId || this.context.svgId, - isInGroup: true - }); + }, {}); + return _.defaults({}, this.context, context); }; setNativeProps = (...args) => { - this.getNativeElement().setNativeProps(...args); - }; - - getNativeElement = () => { - return this.refs.root || this.root; + this.root.setNativeProps(...args); }; render() { - if (this.props.id) { - return - this.root = ele.refs.root} - id={null} - /> - ; - } else { - return - {this.props.children} - ; - } + return this.root = ele} + asClipPath={this.props.asClipPath} + > + {this.props.children} + ; } } diff --git a/elements/Line.js b/elements/Line.js index dd76166b..19a2dafc 100644 --- a/elements/Line.js +++ b/elements/Line.js @@ -15,9 +15,7 @@ class Line extends Shape { static contextTypes = { ...fillProps, ...strokeProps, - ...lineProps, - isInGroup: PropTypes.bool, - svgId: numberProp + ...lineProps }; setNativeProps = (...args) => { diff --git a/elements/Path.js b/elements/Path.js index a91776d7..a78f9fe4 100644 --- a/elements/Path.js +++ b/elements/Path.js @@ -1,5 +1,4 @@ import React, {PropTypes} from 'react'; -import Defs from './Defs'; import SerializablePath from '../lib/SerializablePath'; import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass'; import {PathAttributes} from '../lib/attributes'; @@ -16,9 +15,7 @@ class Path extends Shape { }; static contextTypes = { - ...pathProps, - isInGroup: PropTypes.bool, - svgId: numberProp + ...pathProps }; @@ -31,34 +28,16 @@ class Path extends Shape { }; setNativeProps = (...args) => { - this.getNativeElement().setNativeProps(...args); - }; - - getNativeElement = () => { - return this.refs.root || this.root; + this.root.setNativeProps(...args); }; render() { let props = mergeContext(this.props, this.context); - if (props.id) { - return - this.root = ele.refs.root} - {...props} - id={null} - /> - ; - } - let d = new SerializablePath(props.d).toJSON(); return ( this.root = ele} {...this.extractProps(props)} d={d} /> diff --git a/elements/Rect.js b/elements/Rect.js index bc315b0d..11879a61 100644 --- a/elements/Rect.js +++ b/elements/Rect.js @@ -16,9 +16,7 @@ class Rect extends Shape { static contextTypes = { ...fillProps, ...strokeProps, - ...rectProps, - isInGroup: PropTypes.bool, - svgId: numberProp + ...rectProps }; setNativeProps = (...args) => { diff --git a/elements/Shape.js b/elements/Shape.js index 9d859e53..8e8120c4 100644 --- a/elements/Shape.js +++ b/elements/Shape.js @@ -12,8 +12,8 @@ class Shape extends Component { this.state = this.touchableGetInitialState(); } - extractProps = (props) => { - let extractedProps = extractProps(props); + extractProps = (props, options = {stroke: true, fill: true, responder: true}) => { + let extractedProps = extractProps(props, options); if (extractedProps.touchable && !extractedProps.disabled) { _.assign(extractedProps, { onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder, diff --git a/elements/Text.js b/elements/Text.js index c4a8b62f..e3b8f8f7 100644 --- a/elements/Text.js +++ b/elements/Text.js @@ -20,17 +20,11 @@ class Text extends Shape { static contextTypes = { ...textProps, ...fillProps, - ...strokeProps, - isInGroup: PropTypes.bool, - svgId: numberProp + ...strokeProps }; setNativeProps = (...args) => { - this.getNativeElement().setNativeProps(...args); - }; - - getNativeElement = () => { - return this.refs.root || this.root; + this.root.setNativeProps(...args); }; render() { @@ -45,21 +39,9 @@ class Text extends Shape { y = props.dy ? +props.y + (+props.dy) : +props.y; } - if (this.props.id) { - return this.root = ele.refs.root} - id={this.props.id} - svgId={this.props.svgId} - visible={true} - text={true} - > - - ; - } - return ( this.root = ele} {...extractProps({...props, x, y})} {...extractText(props)} /> diff --git a/elements/Use.js b/elements/Use.js index 7f42dee0..7e86a4dd 100644 --- a/elements/Use.js +++ b/elements/Use.js @@ -1,14 +1,54 @@ -import React, {Component, PropTypes} from 'react'; -import Defs from './Defs'; -class Use extends Component{ +import {PropTypes} from 'react'; +import {pathProps} from '../lib/props'; +import {UseAttributes} from '../lib/attributes'; +import Shape from './Shape'; +import React from 'react'; +import patternReg from '../lib/extract/patternReg'; +import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass'; + +class Defs extends Shape { static displayName = 'Use'; - static propType = { - href: PropTypes.string + + static propTypes = { + href: PropTypes.string.isRequired, + ...pathProps + }; + + setNativeProps = (...args) => { + this.root.setNativeProps(...args); }; render() { - return ; + let {props} = this; + // 尝试匹配 "url(#pattern)" + let matched = props.href.match(patternReg); + let href; + + if (matched) { + href = matched[1]; + } + + if (!href) { + console.warn('Invalid `href` prop for `Use` element, expected a href like `"url(#id)"`, but got: "' + props.href + '"'); + } + + return this.root = ele} + {...this.extractProps(props, { + stroke: true, + fill: true, + responder: true, + transform: true + })} + href={href} + >{props.children}; } } -export default Use; +const RNSVGUse = createReactNativeComponentClass({ + validAttributes: UseAttributes, + uiViewClassName: 'RNSVGUse' +}); + +export default Defs; + diff --git a/ios/Elements/RNSVGClipPath.h b/ios/Elements/RNSVGClipPath.h index 84fd6eac..d99db001 100644 --- a/ios/Elements/RNSVGClipPath.h +++ b/ios/Elements/RNSVGClipPath.h @@ -13,7 +13,6 @@ #import "RNSVGSvgView.h" @interface RNSVGClipPath : RNSVGGroup -@property (nonatomic, strong) NSString *name; - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; diff --git a/ios/Elements/RNSVGClipPath.m b/ios/Elements/RNSVGClipPath.m index b4041d75..a1d0764b 100644 --- a/ios/Elements/RNSVGClipPath.m +++ b/ios/Elements/RNSVGClipPath.m @@ -10,16 +10,23 @@ @implementation RNSVGClipPath -- (void)renderLayerTo:(CGContextRef)context +- (void)saveDefination:(CGContextRef)context { [[self getSvgView] defineClipPath:[self getPath:context] clipPathRef:self.name]; } -// hitTest delagate - - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { return nil; } +- (void)removeDefination +{ + if (self.name) { + [[self getSvgView] removeClipPath: self.name]; + } +} + + + @end diff --git a/ios/Elements/RNSVGDefination.h b/ios/Elements/RNSVGDefination.h new file mode 100644 index 00000000..dee64827 --- /dev/null +++ b/ios/Elements/RNSVGDefination.h @@ -0,0 +1,21 @@ +/** + * 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. + */ + +#import "RNSVGNode.h" + +/** + * RNSVG defination are implemented as abstract UIViews for all elements inside Defs. + */ + +@interface RNSVGDefination : RNSVGNode + +- (void)renderTo:(CGContextRef)context; + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; + +@end diff --git a/ios/Elements/RNSVGDefination.m b/ios/Elements/RNSVGDefination.m new file mode 100644 index 00000000..6ef998c7 --- /dev/null +++ b/ios/Elements/RNSVGDefination.m @@ -0,0 +1,27 @@ +/** + * 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. + */ +#import "RNSVGDefination.h" + +@class RNSVGNode; + +@implementation RNSVGDefination + +- (void)renderTo:(CGContextRef)context +{ + for (RNSVGNode *node in self.subviews) { + [node saveDefination: context]; + } +} + +- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event +{ + return nil; +} + +@end + diff --git a/ios/Elements/RNSVGGroup.m b/ios/Elements/RNSVGGroup.m index 39b14b19..7abb2c76 100644 --- a/ios/Elements/RNSVGGroup.m +++ b/ios/Elements/RNSVGGroup.m @@ -7,6 +7,7 @@ */ #import "RNSVGGroup.h" +#import @implementation RNSVGGroup @@ -35,17 +36,39 @@ } // hitTest delagate - - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) { UIView *view = [node hitTest: point withEvent:event]; - if (view != NULL) { + if (view) { return view; } } return nil; } +- (void)saveDefination:(CGContextRef)context +{ + if (self.name) { + RNSVGSvgView* svg = [self getSvgView]; + [svg defineTemplate:self templateRef:self.name]; + } + + for (RNSVGNode *node in self.subviews) { + [node saveDefination:context]; + } +} + +- (void)willRemoveSubview:(UIView *)subview +{ + [super willRemoveSubview:subview]; +} + +- (void)mergeProperties:(__kindof RNSVGNode *)target +{ + for (RNSVGNode *node in self.subviews) { + [node mergeProperties:target]; + } +} @end diff --git a/ios/Elements/RNSVGSvgView.h b/ios/Elements/RNSVGSvgView.h index da0cf80a..3caccff6 100644 --- a/ios/Elements/RNSVGSvgView.h +++ b/ios/Elements/RNSVGSvgView.h @@ -10,6 +10,8 @@ #import "RNSVGContainer.h" +@class RNSVGNode; + @interface RNSVGSvgView : UIView @property (nonatomic, assign) BOOL responsible; @@ -17,10 +19,16 @@ /** * define content as clipPath template. */ -- (void)defineClipPath:(CGPathRef)clipPath clipPathRef:(NSString *)clipPathId; +- (void)defineClipPath:(CGPathRef)clipPath clipPathRef:(NSString *)clipPathRef; - (void)removeClipPath:(NSString *)clipPathRef; - (CGPathRef)getDefinedClipPath:(NSString *)clipPathRef; +- (void)defineTemplate:(RNSVGNode *)template templateRef:(NSString *)templateRef; + +- (void)removeTemplate:(NSString *)tempalteRef; + +- (RNSVGNode *)getDefinedTemplate:(NSString *)tempalteRef; + @end diff --git a/ios/Elements/RNSVGSvgView.m b/ios/Elements/RNSVGSvgView.m index 6a0c0825..bb63d89d 100644 --- a/ios/Elements/RNSVGSvgView.m +++ b/ios/Elements/RNSVGSvgView.m @@ -13,7 +13,8 @@ @implementation RNSVGSvgView { - NSMutableDictionary *clipPaths; + NSMutableDictionary *clipPaths; + NSMutableDictionary *templates; } - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex @@ -42,8 +43,9 @@ - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); - + for (RNSVGNode *node in self.subviews) { + [node saveDefination:context]; [node renderTo:context]; if (node.responsible && !self.responsible) { @@ -64,7 +66,7 @@ - (void)defineClipPath:(CGPathRef)clipPath clipPathRef:(NSString *)clipPathRef { - if (clipPaths == NULL) { + if (!clipPaths) { clipPaths = [[NSMutableDictionary alloc] init]; } [clipPaths setValue:[NSValue valueWithPointer:clipPath] forKey:clipPathRef]; @@ -72,14 +74,34 @@ - (void)removeClipPath:(NSString *)clipPathRef { - if (clipPaths != NULL) { + if (clipPaths) { [clipPaths removeObjectForKey:clipPathRef]; } } - (CGPathRef)getDefinedClipPath:(NSString *)clipPathRef { - return [[clipPaths valueForKey:clipPathRef] pointerValue]; + return clipPaths ? [[clipPaths valueForKey:clipPathRef] pointerValue] : nil; +} + +- (void)defineTemplate:(RNSVGNode *)template templateRef:(NSString *)templateRef +{ + if (!templates) { + templates = [[NSMutableDictionary alloc] init]; + } + [templates setObject:template forKey:templateRef]; +} + +- (void)removeTemplate:(NSString *)tempalteRef +{ + if (templates) { + [templates removeObjectForKey:tempalteRef]; + } +} + +- (RNSVGNode *)getDefinedTemplate:(NSString *)tempalteRef +{ + return templates ? [templates objectForKey:tempalteRef] : nil; } @end diff --git a/ios/Elements/RNSVGUse.h b/ios/Elements/RNSVGUse.h new file mode 100644 index 00000000..54a4578f --- /dev/null +++ b/ios/Elements/RNSVGUse.h @@ -0,0 +1,19 @@ +/** + * 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. + */ + +#import "RNSVGRenderable.h" + +/** + * RNSVG defination are implemented as abstract UIViews for all elements inside Defs. + */ + +@interface RNSVGUse : RNSVGRenderable + +@property (nonatomic, strong) NSString *href; + +@end diff --git a/ios/Elements/RNSVGUse.m b/ios/Elements/RNSVGUse.m new file mode 100644 index 00000000..c5528677 --- /dev/null +++ b/ios/Elements/RNSVGUse.m @@ -0,0 +1,26 @@ +/** + * 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. + */ +#import "RNSVGUse.h" +#import "RCTLog.h" + +@implementation RNSVGUse + +- (void)renderLayerTo:(CGContextRef)context +{ + RNSVGNode* template = [[self getSvgView] getDefinedTemplate:self.href]; + if (template) { + [template mergeProperties:self]; + [template renderTo:context]; + } else if (self.href) { + // TODO: calling yellow box here + RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href); + } +} + +@end + diff --git a/ios/RNSVG.xcodeproj/project.pbxproj b/ios/RNSVG.xcodeproj/project.pbxproj index f56650f3..a6151799 100644 --- a/ios/RNSVG.xcodeproj/project.pbxproj +++ b/ios/RNSVG.xcodeproj/project.pbxproj @@ -13,6 +13,10 @@ 0CF68B0D1AF0549300FF9E5C /* RNSVGPattern.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF01AF0549300FF9E5C /* RNSVGPattern.m */; }; 0CF68B0E1AF0549300FF9E5C /* RNSVGRadialGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF21AF0549300FF9E5C /* RNSVGRadialGradient.m */; }; 0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF41AF0549300FF9E5C /* RNSVGSolidColor.m */; }; + 1023B48D1D3DDCCE0051496D /* RNSVGDefinationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B48C1D3DDCCE0051496D /* RNSVGDefinationManager.m */; }; + 1023B4901D3DF4C40051496D /* RNSVGDefination.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B48F1D3DF4C40051496D /* RNSVGDefination.m */; }; + 1023B4931D3DF5060051496D /* RNSVGUse.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B4921D3DF5060051496D /* RNSVGUse.m */; }; + 1023B4961D3DF57D0051496D /* RNSVGUseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B4951D3DF57D0051496D /* RNSVGUseManager.m */; }; 1039D2891CE71EB7001E90A8 /* RNSVGGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2821CE71EB7001E90A8 /* RNSVGGroup.m */; }; 1039D28A1CE71EB7001E90A8 /* RNSVGImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2841CE71EB7001E90A8 /* RNSVGImage.m */; }; 1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2861CE71EB7001E90A8 /* RNSVGPath.m */; }; @@ -68,6 +72,14 @@ 0CF68AF21AF0549300FF9E5C /* RNSVGRadialGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGRadialGradient.m; sourceTree = ""; }; 0CF68AF31AF0549300FF9E5C /* RNSVGSolidColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGSolidColor.h; sourceTree = ""; }; 0CF68AF41AF0549300FF9E5C /* RNSVGSolidColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSolidColor.m; sourceTree = ""; }; + 1023B48B1D3DDCCE0051496D /* RNSVGDefinationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGDefinationManager.h; sourceTree = ""; }; + 1023B48C1D3DDCCE0051496D /* RNSVGDefinationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGDefinationManager.m; sourceTree = ""; }; + 1023B48E1D3DF4C40051496D /* RNSVGDefination.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGDefination.h; path = Elements/RNSVGDefination.h; sourceTree = ""; }; + 1023B48F1D3DF4C40051496D /* RNSVGDefination.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGDefination.m; path = Elements/RNSVGDefination.m; sourceTree = ""; }; + 1023B4911D3DF5060051496D /* RNSVGUse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGUse.h; path = Elements/RNSVGUse.h; sourceTree = ""; }; + 1023B4921D3DF5060051496D /* RNSVGUse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGUse.m; path = Elements/RNSVGUse.m; sourceTree = ""; }; + 1023B4941D3DF57D0051496D /* RNSVGUseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGUseManager.h; sourceTree = ""; }; + 1023B4951D3DF57D0051496D /* RNSVGUseManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGUseManager.m; sourceTree = ""; }; 1039D2811CE71EB7001E90A8 /* RNSVGGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGGroup.h; path = Elements/RNSVGGroup.h; sourceTree = ""; }; 1039D2821CE71EB7001E90A8 /* RNSVGGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGGroup.m; path = Elements/RNSVGGroup.m; sourceTree = ""; }; 1039D2831CE71EB7001E90A8 /* RNSVGImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGImage.h; path = Elements/RNSVGImage.h; sourceTree = ""; }; @@ -185,6 +197,10 @@ 0CF68AF81AF0549300FF9E5C /* ViewManagers */ = { isa = PBXGroup; children = ( + 1023B4941D3DF57D0051496D /* RNSVGUseManager.h */, + 1023B4951D3DF57D0051496D /* RNSVGUseManager.m */, + 1023B48B1D3DDCCE0051496D /* RNSVGDefinationManager.h */, + 1023B48C1D3DDCCE0051496D /* RNSVGDefinationManager.m */, 10ED4A9C1CF0656A0078BC02 /* RNSVGClipPathManager.h */, 10ED4A9D1CF0656A0078BC02 /* RNSVGClipPathManager.m */, 10BA0D1C1CE74E3100887C2B /* RNSVGCircleManager.h */, @@ -245,6 +261,10 @@ 1039D2801CE71DCF001E90A8 /* Elements */ = { isa = PBXGroup; children = ( + 1023B4911D3DF5060051496D /* RNSVGUse.h */, + 1023B4921D3DF5060051496D /* RNSVGUse.m */, + 1023B48E1D3DF4C40051496D /* RNSVGDefination.h */, + 1023B48F1D3DF4C40051496D /* RNSVGDefination.m */, 10ED4A991CF065260078BC02 /* RNSVGClipPath.h */, 10ED4A9A1CF065260078BC02 /* RNSVGClipPath.m */, 1039D2811CE71EB7001E90A8 /* RNSVGGroup.h */, @@ -336,6 +356,7 @@ 10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */, 1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */, 0CF68B0D1AF0549300FF9E5C /* RNSVGPattern.m in Sources */, + 1023B4931D3DF5060051496D /* RNSVGUse.m in Sources */, 0CF68B0E1AF0549300FF9E5C /* RNSVGRadialGradient.m in Sources */, 1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */, 10BA0D3B1CE74E3100887C2B /* RNSVGRectManager.m in Sources */, @@ -352,6 +373,7 @@ 0CF68B0C1AF0549300FF9E5C /* RNSVGLinearGradient.m in Sources */, 10BA0D371CE74E3100887C2B /* RNSVGImageManager.m in Sources */, 10BA0D391CE74E3100887C2B /* RNSVGNodeManager.m in Sources */, + 1023B4901D3DF4C40051496D /* RNSVGDefination.m in Sources */, 10BA0D381CE74E3100887C2B /* RNSVGLineManager.m in Sources */, 10BA0D481CE74E3D00887C2B /* RNSVGCircle.m in Sources */, 10BA0D351CE74E3100887C2B /* RNSVGEllipseManager.m in Sources */, @@ -360,6 +382,8 @@ 10BA0D361CE74E3100887C2B /* RNSVGGroupManager.m in Sources */, 10BA0D4A1CE74E3D00887C2B /* RNSVGLine.m in Sources */, 1039D28C1CE71EB7001E90A8 /* RNSVGSvgView.m in Sources */, + 1023B4961D3DF57D0051496D /* RNSVGUseManager.m in Sources */, + 1023B48D1D3DDCCE0051496D /* RNSVGDefinationManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/RNSVGNode.h b/ios/RNSVGNode.h index 072d720a..57004656 100644 --- a/ios/RNSVGNode.h +++ b/ios/RNSVGNode.h @@ -11,21 +11,22 @@ #import "RNSVGSvgView.h" /** - * RNSVG nodes are implemented as empty UIViews but this is just an implementation detail to fit - * into the existing view management. They should also be shadow views and painted on a background - * thread. + * RNSVG nodes are implemented as base UIViews. They should be implementation for all basic + *interfaces for all non-defination nodes. */ @interface RNSVGNode : UIView -@property (nonatomic, assign) CGRect rect; +@property (nonatomic, strong) NSString *name; @property (nonatomic, assign) CGFloat opacity; @property (nonatomic, assign) RNSVGCGFCRule clipRule; @property (nonatomic, assign) CGPathRef clipPath; // convert clipPath="M0,0 L0,10 L10,10z" into path @property (nonatomic, strong) NSString *clipPathRef; // use clipPath="url(#clip)" as ClipPath @property (nonatomic, assign) BOOL responsible; + - (void)invalidate; + - (void)renderTo:(CGContextRef)context; /** @@ -36,20 +37,41 @@ - (void)renderLayerTo:(CGContextRef)context; /** - * clip node by clipPath or clipPathId. + * clip node by clipPath or clipPathRef. */ - (void)clip:(CGContextRef)context; +/** + * get clip path for current node. + */ - (CGPathRef)getClipPath; /** * getPath will return the path inside node as a ClipPath. */ -- (CGPathRef)getPath: (CGContextRef) context; +- (CGPathRef)getPath:(CGContextRef) context; +/** + * run hitTest + */ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; - (RNSVGSvgView *)getSvgView; +/** + * save element`s defination into svg element. + */ +- (void)saveDefination:(CGContextRef)context; + +/** + * remove element`s defination from svg element. + */ +- (void)removeDefination; + +/** + * merge owned properties into target element`s properties + */ +- (void)mergeProperties:(__kindof RNSVGNode *)target; + @end diff --git a/ios/RNSVGNode.m b/ios/RNSVGNode.m index 0ffe2a09..e83f7ab4 100644 --- a/ios/RNSVGNode.m +++ b/ios/RNSVGNode.m @@ -30,6 +30,17 @@ // Do nothing, as subviews are inserted by insertReactSubview: } +- (void)invalidate +{ + id container = (id)self.superview; + [container invalidate]; +} + +- (void)reactSetInheritedBackgroundColor:(UIColor *)inheritedBackgroundColor +{ + self.backgroundColor = inheritedBackgroundColor; +} + - (void)setOpacity:(CGFloat)opacity { if (opacity == _opacity) { @@ -52,12 +63,6 @@ super.transform = transform; } -- (void)invalidate -{ - id container = (id)self.superview; - [container invalidate]; -} - - (void)renderTo:(CGContextRef)context { float opacity = self.opacity; @@ -111,7 +116,7 @@ { CGPathRef clipPath = [self getClipPath]; - if (clipPath != NULL) { + if (clipPath) { CGContextAddPath(context, [self getClipPath]); if (self.clipRule == kRNSVGCGFCRuleEvenodd) { CGContextEOClip(context); @@ -121,33 +126,68 @@ } } - -- (void)reactSetInheritedBackgroundColor:(UIColor *)inheritedBackgroundColor -{ - self.backgroundColor = inheritedBackgroundColor; -} - - (void)renderLayerTo:(CGContextRef)context { // abstract } -- (RNSVGSvgView *)getSvgView -{ - UIView *parent = self.superview; - while ([parent class] != [RNSVGSvgView class]) { - parent = parent.superview; - } - - return (RNSVGSvgView *)parent; -} - - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; { // abstract return nil; } +- (RNSVGSvgView *)getSvgView +{ + UIView *parent = self.superview; + while (parent && [parent class] != [RNSVGSvgView class]) { + parent = parent.superview; + } + + return (RNSVGSvgView *)parent; +} + +- (void)willRemoveFromSuperView +{ + if (self.subviews) { + for (RNSVGNode *node in self.subviews) { + [node willRemoveFromSuperView]; + } + } + [self removeDefination]; + [super removeFromSuperview]; +} + +/** + * reverse removeFromSuperview calling order. + * calling it from subviews to superview. + */ +- (void)removeFromSuperview +{ + [self willRemoveFromSuperView]; +} + +- (void)saveDefination:(CGContextRef)context +{ + if (self.name) { + RNSVGSvgView* svg = [self getSvgView]; + [svg defineTemplate:self templateRef:self.name]; + } +} + +- (void)removeDefination +{ + if (self.name) { + [[self getSvgView] removeTemplate:self.name]; + } +} + + +- (void)mergeProperties:(__kindof RNSVGNode *)target +{ + self.opacity = target.opacity * self.opacity; +} + - (void)dealloc { CGPathRelease(_clipPath); diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m index edcc7eb7..ac9abb6e 100644 --- a/ios/RNSVGRenderable.m +++ b/ios/RNSVGRenderable.m @@ -101,8 +101,8 @@ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { CGPathRef clipPath = [self getClipPath]; - if (self.nodeArea != NULL && CGPathContainsPoint(self.nodeArea, nil, point, NO)) { - if (clipPath == NULL) { + if (self.nodeArea && CGPathContainsPoint(self.nodeArea, nil, point, NO)) { + if (!clipPath) { return self; } else { return CGPathContainsPoint(clipPath, nil, point, NO) ? self : nil; @@ -112,6 +112,40 @@ } } +- (void)mergeProperties:(__kindof RNSVGNode *)target +{ + RNSVGRenderable* renderableTarget = target; + + if (renderableTarget.fill) { + self.fill = renderableTarget.fill; + } + if (renderableTarget.fillRule) { + self.fillRule = renderableTarget.fillRule; + } + if (renderableTarget.stroke) { + self.stroke = renderableTarget.stroke; + } + if (renderableTarget.strokeWidth) { + self.strokeWidth = renderableTarget.strokeWidth; + } + if (renderableTarget.strokeLinecap) { + self.strokeLinecap = renderableTarget.strokeLinecap; + } + if (renderableTarget.strokeLinejoin) { + self.strokeLinejoin = renderableTarget.strokeLinejoin; + } + if (renderableTarget.strokeMiterlimit) { + self.strokeMiterlimit = renderableTarget.strokeMiterlimit; + } + if (renderableTarget.strokeDasharray.count != 0) { + self.strokeDasharray = renderableTarget.strokeDasharray; + } + if (renderableTarget.strokeDashoffset) { + self.strokeDashoffset = renderableTarget.strokeDashoffset; + } + [super mergeProperties:target]; +} + - (void)renderLayerTo:(CGContextRef)context { // abstract diff --git a/ios/Text/RNSVGText.m b/ios/Text/RNSVGText.m index 4940d1ac..ff39d98c 100644 --- a/ios/Text/RNSVGText.m +++ b/ios/Text/RNSVGText.m @@ -81,7 +81,7 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame) } // We should consider snapping this shift to device pixels to improve rendering quality // when a line has subpixel width. - CGAffineTransform offset = CGAffineTransformMakeTranslation(-shift, frame.baseLine + frame.lineHeight * i + (self.path == NULL ? 0 : -frame.lineHeight)); + CGAffineTransform offset = CGAffineTransformMakeTranslation(-shift, frame.baseLine + frame.lineHeight * i + (self.path ? -frame.lineHeight : 0)); CGPathAddPath(path, &offset, [self setLinePath:frame.lines[i]]); } @@ -115,11 +115,11 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame) for(CFIndex j = 0; j < runGlyphCount; ++j, ++glyphIndex) { CGPathRef letter = [cache pathForGlyph:glyphs[j] fromFont:runFont]; CGPoint point = positions[j]; - if (letter != NULL) { + if (letter) { CGAffineTransform transform; // draw glyphs along path - if (self.path != NULL) { + if (self.path) { CGPoint slope; CGRect bounding = CGPathGetBoundingBox(letter); UIBezierPath* path = [UIBezierPath bezierPathWithCGPath:self.path]; diff --git a/ios/Text/RnSVGGlyphCache.m b/ios/Text/RnSVGGlyphCache.m index 155e5e03..9b80ba72 100644 --- a/ios/Text/RnSVGGlyphCache.m +++ b/ios/Text/RnSVGGlyphCache.m @@ -13,7 +13,7 @@ -(id)init { self = [super init]; - if(self != nil) + if(self) { cache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); } @@ -29,7 +29,7 @@ { // First we lookup the font to get to its glyph dictionary CFMutableDictionaryRef glyphDict = (CFMutableDictionaryRef)CFDictionaryGetValue(cache, font); - if(glyphDict == NULL) + if(!glyphDict) { // And if this font hasn't been seen before, we'll create and set the dictionary for it glyphDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); @@ -38,11 +38,11 @@ } // Next we try to get a path for the given glyph from the glyph dictionary CGPathRef path = (CGPathRef)CFDictionaryGetValue(glyphDict, (const void *)(uintptr_t)glyph); - if(path == NULL) + if(!path) { // If the path hasn't been seen before, then we'll create the path from the font & glyph and cache it. path = CTFontCreatePathForGlyph(font, glyph, NULL); - if(path == NULL) + if(!path) { // If a glyph does not have a path, then we need a placeholder to set in the dictionary path = (CGPathRef)kCFNull; diff --git a/ios/Utils/RNSVGPercentageConverter.m b/ios/Utils/RNSVGPercentageConverter.m index 4e8da6b4..3ea777ae 100644 --- a/ios/Utils/RNSVGPercentageConverter.m +++ b/ios/Utils/RNSVGPercentageConverter.m @@ -56,7 +56,7 @@ - (BOOL) isPercentage:(NSString *) string { - return [percentageRegularExpression firstMatchInString:string options:0 range:NSMakeRange(0, [string length])] != NULL; + return [percentageRegularExpression firstMatchInString:string options:0 range:NSMakeRange(0, [string length])] != nil; } @end diff --git a/ios/ViewManagers/RNSVGDefinationManager.h b/ios/ViewManagers/RNSVGDefinationManager.h new file mode 100644 index 00000000..146439ab --- /dev/null +++ b/ios/ViewManagers/RNSVGDefinationManager.h @@ -0,0 +1,15 @@ +/** + * 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. + */ + +#import "RCTViewManager.h" + +@interface RNSVGDefinationManager : RCTViewManager + +@end + +#import "RNSVGNode.h" diff --git a/ios/ViewManagers/RNSVGDefinationManager.m b/ios/ViewManagers/RNSVGDefinationManager.m new file mode 100644 index 00000000..2787ec0d --- /dev/null +++ b/ios/ViewManagers/RNSVGDefinationManager.m @@ -0,0 +1,31 @@ +/** + * 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. + */ + +#import "RNSVGDefination.h" +#import "RNSVGDefinationManager.h" + +@implementation RNSVGDefinationManager + +RCT_EXPORT_MODULE() + +- (RNSVGDefination *)node +{ + return [RNSVGDefination new]; +} + +- (UIView *)view +{ + return [self node]; +} + +- (RCTShadowView *)shadowView +{ + return nil; +} + +@end diff --git a/ios/ViewManagers/RNSVGNodeManager.m b/ios/ViewManagers/RNSVGNodeManager.m index 2514fd0d..e465f224 100644 --- a/ios/ViewManagers/RNSVGNodeManager.m +++ b/ios/ViewManagers/RNSVGNodeManager.m @@ -29,8 +29,9 @@ RCT_EXPORT_MODULE() return nil; } +RCT_EXPORT_VIEW_PROPERTY(name, NSString) RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat) -RCT_EXPORT_VIEW_PROPERTY(trans, CGAffineTransform) +RCT_EXPORT_VIEW_PROPERTY(transform, CGAffineTransform) RCT_EXPORT_VIEW_PROPERTY(clipPathRef, NSString) RCT_EXPORT_VIEW_PROPERTY(responsible, BOOL) diff --git a/ios/ViewManagers/RNSVGShapeManager.h b/ios/ViewManagers/RNSVGUseManager.h similarity index 82% rename from ios/ViewManagers/RNSVGShapeManager.h rename to ios/ViewManagers/RNSVGUseManager.h index 70455e82..aa6bc346 100644 --- a/ios/ViewManagers/RNSVGShapeManager.h +++ b/ios/ViewManagers/RNSVGUseManager.h @@ -8,6 +8,6 @@ #import "RNSVGRenderableManager.h" -@interface RNSVGShapeManager : RNSVGRenderableManager +@interface RNSVGUseManager : RNSVGRenderableManager @end diff --git a/ios/ViewManagers/RNSVGShapeManager.m b/ios/ViewManagers/RNSVGUseManager.m similarity index 52% rename from ios/ViewManagers/RNSVGShapeManager.m rename to ios/ViewManagers/RNSVGUseManager.m index 2787b9ad..92b2d6a0 100644 --- a/ios/ViewManagers/RNSVGShapeManager.m +++ b/ios/ViewManagers/RNSVGUseManager.m @@ -6,20 +6,19 @@ * LICENSE file in the root directory of this source tree. */ -#import "RNSVGShapeManager.h" +#import "RNSVGUseManager.h" +#import "RNSVGUse.h" -#import "RNSVGShape.h" -#import "RCTConvert+RNSVG.h" - -@implementation RNSVGShapeManager +@implementation RNSVGUseManager RCT_EXPORT_MODULE() -- (RNSVGRenderable *)node +- (RNSVGNode *)node { - return [RNSVGShape new]; + return [RNSVGUse new]; } -RCT_EXPORT_VIEW_PROPERTY(shape, NSDictionary) + +RCT_EXPORT_VIEW_PROPERTY(href, NSString) @end diff --git a/lib/attributes.js b/lib/attributes.js index b58eb9e3..5970b5f8 100644 --- a/lib/attributes.js +++ b/lib/attributes.js @@ -3,7 +3,7 @@ import _ from 'lodash'; const merge = _.assign; function arrayDiffer(a, b) { - if (a == null || b == null) { + if (_.isNil(a) || _.isNil(b) ) { return true; } if (a.length !== b.length) { @@ -79,6 +79,10 @@ const GroupAttributes = merge(NodeAttributes, { clipRule: true }); +const UseAttributes = merge(RenderableAttributes, { + href: true +}); + const PathAttributes = merge(RenderableAttributes, { d: { diff: arrayDiffer @@ -146,5 +150,6 @@ export { EllipseAttributes, ImageAttributes, LineAttributes, - RectAttributes + RectAttributes, + UseAttributes }; diff --git a/lib/extract/extractProps.js b/lib/extract/extractProps.js index 6870a755..2353804c 100644 --- a/lib/extract/extractProps.js +++ b/lib/extract/extractProps.js @@ -6,16 +6,14 @@ import extractResponder from './extractResponder'; import _ from 'lodash'; export default function(props, options = {stroke: true, transform: true, fill: true, responder: true}) { - if (props.visible === false) { - return { - opacity: 0 - }; - } - let extractedProps = { opacity: +props.opacity || 1 }; + if (props.id) { + extractedProps.name = props.id; + } + if (props.clipPath) { _.assign(extractedProps, extractClipping(props)); } @@ -30,6 +28,10 @@ export default function(props, options = {stroke: true, transform: true, fill: t if (options.transform) { extractedProps.transform = extractTransform(props); + } else if (props.transform) { + // todo: add support for transform prop like this: + // {scale: 1.5, translate: '10 10'} + extractedProps.transform = props.transform; } if (options.responder) { diff --git a/lib/props.js b/lib/props.js index a2bd71c3..370f7829 100644 --- a/lib/props.js +++ b/lib/props.js @@ -37,6 +37,10 @@ const clipProps = { clipPath: PropTypes.string }; +const definationProps = { + name: PropTypes.string +}; + const strokeProps = { stroke: PropTypes.string, strokeWidth: numberProp, @@ -77,7 +81,8 @@ const pathProps = { ...clipProps, ...transformProps, ...responderProps, - ...touchableProps + ...touchableProps, + ...definationProps }; const circleProps = {