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>
</Svg>;
const samples = [ClipPathAttr, ClipRule];//, ClipPathElement, TextClipping
const samples = [ClipPathAttr, ClipRule, ClipPathElement, TextClipping];
export {
icon,

View File

@@ -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:

View File

@@ -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 <NativeGroup />;
set(this.id, this.id);
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 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 <NativeGroup>
{this.getChildren()}
let children = this.getChildren();
return <NativeGroup opacity={this.shouldRender ? 1 : 0}>
{children}
</NativeGroup>;
}
}

View File

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

View File

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

View File

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

View File

@@ -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;

View File

@@ -13,8 +13,7 @@
#import "RNSVGCGFCRule.h"
@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

View File

@@ -12,10 +12,24 @@
- (void)renderLayerTo:(CGContextRef)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

View File

@@ -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 <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

View File

@@ -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,16 +109,30 @@
// abstract
}
- (CGPathRef)getPath: (CGContextRef) context
{
// abstract
return CGPathCreateMutable();
}
- (void)clip:(CGContextRef)context
{
CGPathRef clipPath = nil;
if (self.clipPath) {
CGContextAddPath(context, self.clipPath);
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);
}
}
}
@end

View File

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

View File

@@ -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();
@@ -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

View File

@@ -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

View File

@@ -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

View File

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

View File

@@ -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 {