mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-20 14:05:09 +00:00
complete ClipPath element on iOS
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|
||||||
|
|||||||
@@ -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>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user