complete ClipPath element on iOS

This commit is contained in:
Horcrux
2016-04-27 23:14:15 +08:00
parent efd56cdb6a
commit fa751e31b0
18 changed files with 112 additions and 60 deletions

View File

@@ -213,7 +213,7 @@ const icon = <Svg
</G> </G>
</Svg>; </Svg>;
const samples = [ClipPathAttr, ClipRule];//, ClipPathElement, TextClipping const samples = [ClipPathAttr, ClipRule, ClipPathElement, TextClipping];
export { export {
icon, icon,

View File

@@ -564,7 +564,7 @@ npm install
6. more Text features support 6. more Text features support
7. Pattern element 7. Pattern element
8. Image element 8. Image element
9. calculate bounding box only if necessary.
#### Thanks: #### Thanks:

View File

@@ -28,26 +28,13 @@ class ClipPath extends Component{
remove(this.id); 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() { render() {
this._combinePaths(this.props.children); set(this.id, this.id);
set(this.id, this._path);
return <NativeGroup />; return <NativeGroup
asClipPath={this.id}
opacity={1}
>{this.props.children}</NativeGroup>;
} }
} }

View File

@@ -80,11 +80,16 @@ class Defs extends Component{
static Item = DefsItem; static Item = DefsItem;
static Use = DefsUse; static Use = DefsUse;
shouldRender = false;
getChildren = () => { getChildren = () => {
return Children.map(this.props.children, child => { return Children.map(this.props.children, child => {
let {type} = child; let {type} = child;
if (type === LinearGradient || type === RadialGradient || type === ClipPath) { if (type === LinearGradient || type === RadialGradient || type === ClipPath) {
if (type === ClipPath) {
this.shouldRender = true;
}
return cloneElement(child, { return cloneElement(child, {
svgId: this.props.svgId svgId: this.props.svgId
}); });
@@ -99,8 +104,9 @@ class Defs extends Component{
}; };
render() { render() {
return <NativeGroup> let children = this.getChildren();
{this.getChildren()} return <NativeGroup opacity={this.shouldRender ? 1 : 0}>
{children}
</NativeGroup>; </NativeGroup>;
} }
} }

View File

@@ -47,6 +47,7 @@ class G extends Component{
} else { } else {
return <NativeGroup return <NativeGroup
{...extractProps(this.props, {transform: true})} {...extractProps(this.props, {transform: true})}
asClipPath={this.props.asClipPath}
> >
{this.props.children} {this.props.children}
</NativeGroup>; </NativeGroup>;

View File

@@ -58,7 +58,7 @@ class Shape extends Component{
let shape = new SerializableShape(props, COORD_PROPS[this.type]).toJSON(); let shape = new SerializableShape(props, COORD_PROPS[this.type]).toJSON();
return <NativePath return <NativeShape
{...extractProps(this.type === 3 ? { {...extractProps(this.type === 3 ? {
...props, ...props,
x: null, x: null,
@@ -72,7 +72,7 @@ class Shape extends Component{
}; };
} }
let NativePath = createReactNativeComponentClass({ let NativeShape = createReactNativeComponentClass({
validAttributes: ShapeAttributes, validAttributes: ShapeAttributes,
uiViewClassName: 'RNSVGShape' uiViewClassName: 'RNSVGShape'
}); });

View File

@@ -20,7 +20,6 @@ class SymbolElement extends Component{
{...props} {...props}
viewbox={props.viewbox} viewbox={props.viewbox}
preserveAspectRatio={props.preserveAspectRatio} preserveAspectRatio={props.preserveAspectRatio}
shouldTransform={true}
> >
{props.children} {props.children}
</ViewBox> </ViewBox>

View File

@@ -7,12 +7,7 @@ import G from './G';
import extractViewbox from '../lib/extract/extractViewbox'; import extractViewbox from '../lib/extract/extractViewbox';
class ViewBox extends Component{ class ViewBox extends Component{
static displayName = 'ViewBox'; static displayName = 'ViewBox';
static propType = {
shouldTransform: PropTypes.bool
};
static defaultProps = {
shouldTransform: false
};
render() { render() {
let viewbox = extractViewbox(this.props); let viewbox = extractViewbox(this.props);
let scaleX = 1; let scaleX = 1;

View File

@@ -13,8 +13,7 @@
#import "RNSVGCGFCRule.h" #import "RNSVGCGFCRule.h"
@interface RNSVGGroup : RNSVGNode <RNSVGContainer> @interface RNSVGGroup : RNSVGNode <RNSVGContainer>
@property (nonatomic, strong) NSString *asClipPath; // Current group is a <ClipPath /> element and asClipPath is its id.
@property (nonatomic, assign) CGPathRef clipPath;
@property (nonatomic, assign) RNSVGCGFCRule clipRule;
@end @end

View File

@@ -12,10 +12,24 @@
- (void)renderLayerTo:(CGContextRef)context - (void)renderLayerTo:(CGContextRef)context
{ {
[self clip:context]; if (self.asClipPath == NULL) {
for (RNSVGNode *node in self.subviews) { [self clip:context];
[node renderTo: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 @end

View File

@@ -20,8 +20,9 @@
@property (nonatomic, assign) CGRect rect; @property (nonatomic, assign) CGRect rect;
@property (nonatomic, assign) CGFloat opacity; @property (nonatomic, assign) CGFloat opacity;
@property (nonatomic, assign) CGPathRef clipPath;
@property (nonatomic, assign) RNSVGCGFCRule clipRule; @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)invalidate;
- (void)renderTo:(CGContextRef)context; - (void)renderTo:(CGContextRef)context;
@@ -33,6 +34,19 @@
*/ */
- (void)renderLayerTo:(CGContextRef)context; - (void)renderLayerTo:(CGContextRef)context;
/**
* clip node by clipPath or clipPathId.
*/
- (void)clip:(CGContextRef)context; - (void)clip:(CGContextRef)context;
/**
* define <ClipPath></ClipPath> 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 @end

View File

@@ -10,6 +10,8 @@
#import "RNSVGContainer.h" #import "RNSVGContainer.h"
static NSMutableDictionary *ClipPaths;
@implementation RNSVGNode @implementation RNSVGNode
- (void)insertSubview:(UIView *)subview atIndex:(NSInteger)index - (void)insertSubview:(UIView *)subview atIndex:(NSInteger)index
@@ -71,6 +73,17 @@
} }
- (void)setClipPath:(CGPathRef)clipPath - (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) { if (clipPath == _clipPath) {
return; return;
@@ -78,6 +91,11 @@
[self invalidate]; [self invalidate];
CGPathRelease(_clipPath); CGPathRelease(_clipPath);
_clipPath = CGPathRetain(clipPath); _clipPath = CGPathRetain(clipPath);
if (ClipPaths == NULL) {
ClipPaths = [[NSMutableDictionary alloc] init];
}
[ClipPaths setValue:[NSValue valueWithPointer:_clipPath] forKey:clipPathId];
} }
- (void)dealloc - (void)dealloc
@@ -91,15 +109,29 @@
// abstract // abstract
} }
- (CGPathRef)getPath: (CGContextRef) context
{
// abstract
return CGPathCreateMutable();
}
- (void)clip:(CGContextRef)context - (void)clip:(CGContextRef)context
{ {
CGPathRef clipPath = nil;
if (self.clipPath) { if (self.clipPath) {
CGContextAddPath(context, self.clipPath); clipPath = self.clipPath;
if (self.clipRule == kRNSVGCGFCRuleEvenodd) { } else if (self.clipPathId) {
CGContextEOClip(context); clipPath = [[ClipPaths valueForKey:self.clipPathId] pointerValue];
} else { } else {
CGContextClip(context); return;
} }
CGContextAddPath(context, clipPath);
if (self.clipRule == kRNSVGCGFCRuleEvenodd) {
CGContextEOClip(context);
} else {
CGContextClip(context);
} }
} }

View File

@@ -39,9 +39,7 @@
fillColor = [self.fill applyFillColor:context]; fillColor = [self.fill applyFillColor:context];
if (!fillColor) { if (!fillColor) {
if (self.clipPath) { [self clip:context];
[self clip:context];
}
CGContextSaveGState(context); CGContextSaveGState(context);
CGContextAddPath(context, self.d); CGContextAddPath(context, self.d);
@@ -97,4 +95,9 @@
CGContextDrawPath(context, mode); CGContextDrawPath(context, mode);
} }
- (CGPathRef)getPath:(CGContextRef)context
{
return self.d;
}
@end @end

View File

@@ -11,13 +11,15 @@
@implementation RNSVGShape @implementation RNSVGShape
- (void)dealloc
{
}
- (void)renderLayerTo:(CGContextRef)context - (void)renderLayerTo:(CGContextRef)context
{ {
self.d = [self getPath: context];
[super renderLayerTo:context];
}
- (CGPathRef)getPath:(CGContextRef)context
{
int type = [[self.shape objectForKey:@"type"] intValue]; int type = [[self.shape objectForKey:@"type"] intValue];
CGRect box = CGContextGetClipBoundingBox(context); CGRect box = CGContextGetClipBoundingBox(context);
CGMutablePathRef path = CGPathCreateMutable(); CGMutablePathRef path = CGPathCreateMutable();
@@ -39,7 +41,7 @@
CGFloat value = [[prop objectForKey:@"value"] floatValue]; CGFloat value = [[prop objectForKey:@"value"] floatValue];
if ([[prop objectForKey:@"percentage"] integerValue] == 1) { if ([[prop objectForKey:@"percentage"] integerValue] == 1) {
r = sqrt(pow((width * value), 2) + pow((height * value), 2)) / sqrt(2); r = sqrt(pow((width * value), 2) + pow((height * value), 2)) / sqrt(2);
} else { } else {
r = value; r = value;
} }
@@ -104,13 +106,9 @@
RCTLogError(@"Invalid Shape type %d at %@", type, self.shape); RCTLogError(@"Invalid Shape type %d at %@", type, self.shape);
//CGPathRelease(path); //CGPathRelease(path);
} }
self.d = path; return path;
[super renderLayerTo:context];
//NSLog(@"%@", NSStringFromCGRect(box));
//NSLog(@"%@", self.shape);
} }
- (CGFloat)getActualProp:(NSString *)name relative:(float)relative - (CGFloat)getActualProp:(NSString *)name relative:(float)relative

View File

@@ -22,5 +22,6 @@ RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath) RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath)
RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule) RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule)
RCT_EXPORT_VIEW_PROPERTY(asClipPath, NSString)
@end @end

View File

@@ -31,5 +31,6 @@ RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat) RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(transform, CGAffineTransform) RCT_EXPORT_VIEW_PROPERTY(transform, CGAffineTransform)
RCT_EXPORT_VIEW_PROPERTY(clipPathId, NSString)
@end @end

View File

@@ -63,10 +63,12 @@ const NodeAttributes = {
clipPath: { clipPath: {
diff: arrayDiffer diff: arrayDiffer
}, },
clipPathId: true,
clipRule: true clipRule: true
}; };
const GroupAttributes = { const GroupAttributes = {
asClipPath: true,
...NodeAttributes ...NodeAttributes
}; };

View File

@@ -29,9 +29,9 @@ export default function (props) {
let patternName = `${matched[1]}:${props.svgId}`; let patternName = `${matched[1]}:${props.svgId}`;
let pattern = clipPatterns[patternName]; let pattern = clipPatterns[patternName];
if (pattern) { if (pattern) {
clippingProps.clipPath = new SerializablePath(pattern).toJSON(); clippingProps.clipPathId = pattern;
} else { } else {
clippingProps = {}; clippingProps = null;
// TODO: warn // TODO: warn
} }
} else { } else {