complete ClipPath refactor

This commit is contained in:
Horcrux
2016-07-20 14:44:19 +08:00
parent dd6cb80e84
commit 18e1b60823
30 changed files with 254 additions and 251 deletions

View File

@@ -7,7 +7,9 @@ import Svg, {
G, G,
Path, Path,
Use, Use,
Rect Rect,
Circle,
ClipPath
} from 'react-native-svg'; } from 'react-native-svg';
class DefsExample extends Component{ class DefsExample extends Component{
@@ -19,12 +21,16 @@ class DefsExample extends Component{
width="100" width="100"
> >
<Defs> <Defs>
<G id="path" x="5" y="2"> <G id="path" x="5" y="2" opacity="0.9">
<Path id="test" fill='red' d="M38.459,1.66A0.884,0.884,0,0,1,39,2.5a0.7,0.7,0,0,1-.3.575L23.235,16.092,27.58,26.1a1.4,1.4,0,0,1,.148.3,1.3,1.3,0,0,1,0,.377,1.266,1.266,0,0,1-2.078.991L15.526,20.6l-7.58,4.35a1.255,1.255,0,0,1-.485,0,1.267,1.267,0,0,1-1.277-1.258q0-.01,0-0.02a1.429,1.429,0,0,1,0-.446C7.243,20.253,8.6,16.369,8.6,16.29L3.433,13.545A0.743,0.743,0,0,1,2.9,12.822a0.822,0.822,0,0,1,.623-0.773l8.164-2.972,3.018-8.5A0.822,0.822,0,0,1,15.427,0a0.752,0.752,0,0,1,.752.555l2.563,6.936S37.65,1.727,37.792,1.685A1.15,1.15,0,0,1,38.459,1.66Z"/> <Path id="test" fill='red' d="M38.459,1.66A0.884,0.884,0,0,1,39,2.5a0.7,0.7,0,0,1-.3.575L23.235,16.092,27.58,26.1a1.4,1.4,0,0,1,.148.3,1.3,1.3,0,0,1,0,.377,1.266,1.266,0,0,1-2.078.991L15.526,20.6l-7.58,4.35a1.255,1.255,0,0,1-.485,0,1.267,1.267,0,0,1-1.277-1.258q0-.01,0-0.02a1.429,1.429,0,0,1,0-.446C7.243,20.253,8.6,16.369,8.6,16.29L3.433,13.545A0.743,0.743,0,0,1,2.9,12.822a0.822,0.822,0,0,1,.623-0.773l8.164-2.972,3.018-8.5A0.822,0.822,0,0,1,15.427,0a0.752,0.752,0,0,1,.752.555l2.563,6.936S37.65,1.727,37.792,1.685A1.15,1.15,0,0,1,38.459,1.66Z"/>
</G> </G>
<ClipPath id="clip">
<Circle r="20%" cx="0" cy="0" />
</ClipPath>
</Defs> </Defs>
<Use href="url(#path)" x="0" fill="blue" /> <Use href="url(#path)" x="0" fill="blue" opacity="0.5" />
<Use href="url(#path)" x="10" fill="#3a8" /> <Use href="url(#path)" x="20" y="5" fill="#8a3" />
<Use href="url(#path)" x="30" clipPath="url(#clip)" />
</Svg>; </Svg>;
} }
} }
@@ -34,11 +40,11 @@ const icon = <Svg
width="20" width="20"
> >
<Defs> <Defs>
<G id="path"> <G id="path" scale="0.5">
<Path fill='red' d="M38.459,1.66A0.884,0.884,0,0,1,39,2.5a0.7,0.7,0,0,1-.3.575L23.235,16.092,27.58,26.1a1.4,1.4,0,0,1,.148.3,1.3,1.3,0,0,1,0,.377,1.266,1.266,0,0,1-2.078.991L15.526,20.6l-7.58,4.35a1.255,1.255,0,0,1-.485,0,1.267,1.267,0,0,1-1.277-1.258q0-.01,0-0.02a1.429,1.429,0,0,1,0-.446C7.243,20.253,8.6,16.369,8.6,16.29L3.433,13.545A0.743,0.743,0,0,1,2.9,12.822a0.822,0.822,0,0,1,.623-0.773l8.164-2.972,3.018-8.5A0.822,0.822,0,0,1,15.427,0a0.752,0.752,0,0,1,.752.555l2.563,6.936S37.65,1.727,37.792,1.685A1.15,1.15,0,0,1,38.459,1.66Z"/> <Path fill='red' d="M38.459,1.66A0.884,0.884,0,0,1,39,2.5a0.7,0.7,0,0,1-.3.575L23.235,16.092,27.58,26.1a1.4,1.4,0,0,1,.148.3,1.3,1.3,0,0,1,0,.377,1.266,1.266,0,0,1-2.078.991L15.526,20.6l-7.58,4.35a1.255,1.255,0,0,1-.485,0,1.267,1.267,0,0,1-1.277-1.258q0-.01,0-0.02a1.429,1.429,0,0,1,0-.446C7.243,20.253,8.6,16.369,8.6,16.29L3.433,13.545A0.743,0.743,0,0,1,2.9,12.822a0.822,0.822,0,0,1,.623-0.773l8.164-2.972,3.018-8.5A0.822,0.822,0,0,1,15.427,0a0.752,0.752,0,0,1,.752.555l2.563,6.936S37.65,1.727,37.792,1.685A1.15,1.15,0,0,1,38.459,1.66Z"/>
</G> </G>
</Defs> </Defs>
<Use href="url(#path)" x="10" fill="#3a8" /> <Use href="url(#path)" fill="#3a8" />
</Svg>; </Svg>;

View File

@@ -7,15 +7,16 @@ import {circleProps, pathProps, fillProps, strokeProps, numberProp} from '../lib
class Circle extends Shape { class Circle extends Shape {
static displayName = 'Circle'; static displayName = 'Circle';
static propTypes = { static propTypes = {
...pathProps, ...pathProps,
...circleProps ...circleProps
}; };
static contextTypes = { static defaultProps = {
...fillProps, cx: 0,
...strokeProps, cy: 0,
...circleProps r: 0
}; };
setNativeProps = (...args) => { setNativeProps = (...args) => {

View File

@@ -1,5 +1,4 @@
import React, {Component, PropTypes} from 'react'; import React, {Component, PropTypes} from 'react';
import {set, remove} from '../lib/extract/extractClipping';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass'; import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import {ClipPathAttributes} from '../lib/attributes'; import {ClipPathAttributes} from '../lib/attributes';
@@ -9,26 +8,9 @@ class ClipPath extends Component{
id: PropTypes.string.isRequired id: PropTypes.string.isRequired
}; };
constructor() {
super(...arguments);
this.id = this.props.id + ':' + this.props.svgId;
}
componentWillReceiveProps = nextProps => {
let id = nextProps.id + ':' + nextProps.svgId;
if (id !== this.id) {
remove(this.id);
}
};
componentWillUnmount = () => {
remove(this.id);
};
render() { render() {
set(this.id, this.id);
return <RNSVGClipPath return <RNSVGClipPath
name={this.id} name={this.props.id}
>{this.props.children}</RNSVGClipPath>; >{this.props.children}</RNSVGClipPath>;
} }
} }

View File

@@ -7,15 +7,17 @@ import {EllipseAttributes} from '../lib/attributes';
class Ellipse extends Shape{ class Ellipse extends Shape{
static displayName = 'Ellipse'; static displayName = 'Ellipse';
static propTypes = { static propTypes = {
...pathProps, ...pathProps,
...ellipseProps ...ellipseProps
}; };
static contextTypes = { static defaultProps = {
...fillProps, cx: 0,
...strokeProps, cy: 0,
...ellipseProps rx: 0,
ry: 0
}; };
setNativeProps = (...args) => { setNativeProps = (...args) => {

View File

@@ -1,31 +1,14 @@
import React, {Component, PropTypes} from 'react'; import React, {Component, PropTypes} from 'react';
import _ from 'lodash'; import _ from 'lodash';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass'; import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import {numberProp, contextProps} from '../lib/props'; import {transformProps} from '../lib/props';
import {GroupAttributes} from '../lib/attributes'; import {GroupAttributes} from '../lib/attributes';
import extractProps from '../lib/extract/extractProps'; import extractProps from '../lib/extract/extractProps';
class G extends Component{ class G extends Component{
static displayName = 'G'; static displayName = 'G';
static contextTypes = { static propTypes = transformProps;
...contextProps
};
static childContextTypes = {
...contextProps
};
getChildContext = () => {
let context = _.reduce(contextProps, (props, value, key) => {
if (!_.isNil(this.props[key])) {
props[key] = this.props[key];
}
return props;
}, {});
return _.defaults({}, this.context, context);
};
setNativeProps = (...args) => { setNativeProps = (...args) => {
this.root.setNativeProps(...args); this.root.setNativeProps(...args);

View File

@@ -11,14 +11,21 @@ class Image extends Shape {
static propTypes = { static propTypes = {
x: numberProp, x: numberProp,
y: numberProp, y: numberProp,
width: numberProp, width: numberProp.isRequired,
height: numberProp, height: numberProp.isRequired,
href: PropTypes.number.isRequired, href: PropTypes.number.isRequired,
...responderProps, ...responderProps,
...touchableProps ...touchableProps
//preserveAspectRatio: PropTypes.string //preserveAspectRatio: PropTypes.string
}; };
static defaultProps = {
x: 0,
y: 0,
width: 0,
height: 0
};
setNativeProps = (...args) => { setNativeProps = (...args) => {
this.root.setNativeProps(...args); this.root.setNativeProps(...args);
}; };

View File

@@ -7,15 +7,17 @@ import {lineProps, pathProps, fillProps, strokeProps, numberProp} from '../lib/p
class Line extends Shape { class Line extends Shape {
static displayName = 'Line'; static displayName = 'Line';
static propTypes = { static propTypes = {
...pathProps, ...pathProps,
...lineProps ...lineProps
}; };
static contextTypes = { static defaultProps = {
...fillProps, x1: 0,
...strokeProps, y1: 0,
...lineProps x2: 0,
y2: 0
}; };
setNativeProps = (...args) => { setNativeProps = (...args) => {

View File

@@ -10,23 +10,10 @@ class Path extends Shape {
static displayName = 'Path'; static displayName = 'Path';
static propTypes = { static propTypes = {
d: PropTypes.string, d: PropTypes.string.isRequired,
...pathProps ...pathProps
}; };
static contextTypes = {
...pathProps
};
_dimensions = null;
componentWillReceiveProps = nextProps => {
if (nextProps.d !== this.props.d) {
this._dimensions = null;
}
};
setNativeProps = (...args) => { setNativeProps = (...args) => {
this.root.setNativeProps(...args); this.root.setNativeProps(...args);
}; };

View File

@@ -1,12 +1,17 @@
import React, {Component, PropTypes} from 'react'; import React, {Component, PropTypes} from 'react';
import Path from './Path'; import Path from './Path';
import {pathProps} from '../lib/props'; import {pathProps} from '../lib/props';
import _ from 'lodash';
class Polygon extends Component{ class Polygon extends Component{
static displayName = 'Polygon'; static displayName = 'Polygon';
static propTypes = { static propTypes = {
...pathProps, ...pathProps,
points: PropTypes.oneOfType([PropTypes.string, PropTypes.array]) points: PropTypes.oneOfType([PropTypes.string, PropTypes.array]).isRequired
};
static defaultProps = {
points: ''
}; };
setNativeProps = (...args) => { setNativeProps = (...args) => {
@@ -14,10 +19,15 @@ class Polygon extends Component{
}; };
render() { render() {
let points = this.props.points;
if (_.isArray(points)) {
points = points.join(',');
}
return <Path return <Path
ref={ele => this.root = ele} ref={ele => this.root = ele}
{...this.props} {...this.props}
d={`M${this.props.points.trim().replace(/\s+/g, 'L')}z`} d={`M${points.trim().replace(/\s+/g, 'L')}z`}
/>; />;
} }
} }

View File

@@ -1,12 +1,17 @@
import React, {Component, PropTypes} from 'react'; import React, {Component, PropTypes} from 'react';
import Path from './Path'; import Path from './Path';
import {pathProps} from '../lib/props'; import {pathProps} from '../lib/props';
import _ from 'lodash';
class Polyline extends Component{ class Polyline extends Component{
static displayName = 'Polyline'; static displayName = 'Polyline';
static propTypes = { static propTypes = {
...pathProps, ...pathProps,
points: PropTypes.oneOfType([PropTypes.string, PropTypes.array]) points: PropTypes.oneOfType([PropTypes.string, PropTypes.array]).isRequired
};
static defaultProps = {
points: ''
}; };
setNativeProps = (...args) => { setNativeProps = (...args) => {
@@ -14,10 +19,15 @@ class Polyline extends Component{
}; };
render() { render() {
let points = this.props.points;
if (_.isArray(points)) {
points = points.join(',');
}
return <Path return <Path
ref={ele => this.root = ele} ref={ele => this.root = ele}
{...this.props} {...this.props}
d={`M${this.props.points.trim().replace(/\s+/g, 'L')}`} d={`M${points.trim().replace(/\s+/g, 'L')}`}
/>; />;
} }
} }

View File

@@ -8,15 +8,19 @@ import Shape from './Shape';
class Rect extends Shape { class Rect extends Shape {
static displayName = 'Rect'; static displayName = 'Rect';
static propTypes = { static propTypes = {
...pathProps, ...pathProps,
...rectProps ...rectProps
}; };
static contextTypes = { static defaultProps = {
...fillProps, x: 0,
...strokeProps, y: 0,
...rectProps width: 0,
height: 0,
rx: 0,
ry: 0
}; };
setNativeProps = (...args) => { setNativeProps = (...args) => {
@@ -37,8 +41,8 @@ class Rect extends Shape {
y={props.y.toString()} y={props.y.toString()}
width={props.width.toString()} width={props.width.toString()}
height={props.height.toString()} height={props.height.toString()}
rx={props.rx ? props.rx.toString() : '0'} rx={props.rx.toString()}
ry={props.ry ? props.ry.toString() : '0'} ry={props.ry.toString()}
/>; />;
} }
} }

View File

@@ -30,15 +30,6 @@ class Svg extends Component{
id++; id++;
this.id = id; this.id = id;
} }
getChildren = () => {
return Children.map(this.props.children, child => {
return cloneElement(child, {
svgId: this.id
});
});
};
measureInWindow = (...args) => { measureInWindow = (...args) => {
this.root.measureInWindow(...args); this.root.measureInWindow(...args);
}; };
@@ -67,9 +58,7 @@ class Svg extends Component{
preserveAspectRatio={props.preserveAspectRatio} preserveAspectRatio={props.preserveAspectRatio}
width={props.width} width={props.width}
height={props.height} height={props.height}
> >{props.children}</ViewBox> : props.children;
{this.getChildren()}
</ViewBox> : this.getChildren();
return ( return (
<NativeSvgView <NativeSvgView

View File

@@ -10,6 +10,7 @@ import Shape from './Shape';
class Text extends Shape { class Text extends Shape {
static displayName = 'Text'; static displayName = 'Text';
static propTypes = { static propTypes = {
dx: numberProp, dx: numberProp,
dy: numberProp, dy: numberProp,
@@ -17,10 +18,9 @@ class Text extends Shape {
...pathProps ...pathProps
}; };
static contextTypes = { static defaultProps = {
...textProps, dx: 0,
...fillProps, dy: 0
...strokeProps
}; };
setNativeProps = (...args) => { setNativeProps = (...args) => {

View File

@@ -1,10 +1,11 @@
import {PropTypes} from 'react'; import {PropTypes} from 'react';
import {pathProps} from '../lib/props'; import {pathProps} from '../lib/props';
import {UseAttributes} from '../lib/attributes'; import {UseAttributes, RenderableOnlyAttributes} from '../lib/attributes';
import Shape from './Shape'; import Shape from './Shape';
import React from 'react'; import React from 'react';
import patternReg from '../lib/extract/patternReg'; import patternReg from '../lib/extract/patternReg';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass'; import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import _ from 'lodash';
class Defs extends Shape { class Defs extends Shape {
static displayName = 'Use'; static displayName = 'Use';
@@ -32,14 +33,35 @@ class Defs extends Shape {
console.warn('Invalid `href` prop for `Use` element, expected a href like `"url(#id)"`, but got: "' + props.href + '"'); console.warn('Invalid `href` prop for `Use` element, expected a href like `"url(#id)"`, but got: "' + props.href + '"');
} }
let mergeList = [];
let extractedProps = this.extractProps(props, {
stroke: true,
fill: true,
responder: true,
transform: true
});
Object.keys(RenderableOnlyAttributes).forEach(name => {
if (!_.isNil(props[name])) {
// clipPath prop may provide `clipPathRef` as native prop
if (name === 'clipPath') {
if (extractedProps[name]) {
mergeList.push(name);
} else if (extractedProps.clipPathRef) {
mergeList.push('clipPathRef');
}
} else {
mergeList.push(name);
}
}
});
return <RNSVGUse return <RNSVGUse
ref={ele => this.root = ele} ref={ele => this.root = ele}
{...this.extractProps(props, { {...extractedProps}
stroke: true, mergeList={mergeList}
fill: true,
responder: true,
transform: true
})}
href={href} href={href}
>{props.children}</RNSVGUse>; >{props.children}</RNSVGUse>;
} }

View File

@@ -14,6 +14,4 @@
@interface RNSVGClipPath : RNSVGGroup @interface RNSVGClipPath : RNSVGGroup
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
@end @end

View File

@@ -10,16 +10,16 @@
@implementation RNSVGClipPath @implementation RNSVGClipPath
- (void)saveDefination:(CGContextRef)context
{
[[self getSvgView] defineClipPath:[self getPath:context] clipPathRef:self.name];
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{ {
return nil; return nil;
} }
- (void)saveDefination:(CGContextRef)context
{
[[self getSvgView] defineClipPath:self clipPathRef:self.name];
}
- (void)removeDefination - (void)removeDefination
{ {
if (self.name) { if (self.name) {

View File

@@ -64,10 +64,17 @@
[super willRemoveSubview:subview]; [super willRemoveSubview:subview];
} }
- (void)mergeProperties:(__kindof RNSVGNode *)target - (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList
{ {
for (RNSVGNode *node in self.subviews) { for (RNSVGNode *node in self.subviews) {
[node mergeProperties:target]; [node mergeProperties:target mergeList:mergeList];
}
}
- (void)resetProperties
{
for (RNSVGNode *node in self.subviews) {
[node resetProperties];
} }
} }

View File

@@ -19,13 +19,13 @@
/** /**
* define <ClipPath></ClipPath> content as clipPath template. * define <ClipPath></ClipPath> content as clipPath template.
*/ */
- (void)defineClipPath:(CGPathRef)clipPath clipPathRef:(NSString *)clipPathRef; - (void)defineClipPath:(__kindof RNSVGNode *)clipPath clipPathRef:(NSString *)clipPathRef;
- (void)removeClipPath:(NSString *)clipPathRef; - (void)removeClipPath:(NSString *)clipPathRef;
- (CGPathRef)getDefinedClipPath:(NSString *)clipPathRef; - (RNSVGNode *)getDefinedClipPath:(NSString *)clipPathRef;
- (void)defineTemplate:(RNSVGNode *)template templateRef:(NSString *)templateRef; - (void)defineTemplate:(__kindof RNSVGNode *)template templateRef:(NSString *)templateRef;
- (void)removeTemplate:(NSString *)tempalteRef; - (void)removeTemplate:(NSString *)tempalteRef;

View File

@@ -13,7 +13,7 @@
@implementation RNSVGSvgView @implementation RNSVGSvgView
{ {
NSMutableDictionary<NSString *, NSValue *> *clipPaths; NSMutableDictionary<NSString *, RNSVGNode *> *clipPaths;
NSMutableDictionary<NSString *, RNSVGNode *> *templates; NSMutableDictionary<NSString *, RNSVGNode *> *templates;
} }
@@ -64,12 +64,12 @@
return self.responsible ? [super hitTest:point withEvent:event] : nil; return self.responsible ? [super hitTest:point withEvent:event] : nil;
} }
- (void)defineClipPath:(CGPathRef)clipPath clipPathRef:(NSString *)clipPathRef - (void)defineClipPath:(__kindof RNSVGNode *)clipPath clipPathRef:(NSString *)clipPathRef
{ {
if (!clipPaths) { if (!clipPaths) {
clipPaths = [[NSMutableDictionary alloc] init]; clipPaths = [[NSMutableDictionary alloc] init];
} }
[clipPaths setValue:[NSValue valueWithPointer:clipPath] forKey:clipPathRef]; [clipPaths setObject:clipPath forKey:clipPathRef];
} }
- (void)removeClipPath:(NSString *)clipPathRef - (void)removeClipPath:(NSString *)clipPathRef
@@ -79,9 +79,9 @@
} }
} }
- (CGPathRef)getDefinedClipPath:(NSString *)clipPathRef - (RNSVGNode *)getDefinedClipPath:(NSString *)clipPathRef
{ {
return clipPaths ? [[clipPaths valueForKey:clipPathRef] pointerValue] : nil; return clipPaths ? [clipPaths objectForKey:clipPathRef] : nil;
} }
- (void)defineTemplate:(RNSVGNode *)template templateRef:(NSString *)templateRef - (void)defineTemplate:(RNSVGNode *)template templateRef:(NSString *)templateRef

View File

@@ -15,5 +15,7 @@
@interface RNSVGUse : RNSVGRenderable @interface RNSVGUse : RNSVGRenderable
@property (nonatomic, strong) NSString *href; @property (nonatomic, strong) NSString *href;
@property (nonatomic, copy) NSArray<NSString *> *mergeList;
@end @end

View File

@@ -10,12 +10,22 @@
@implementation RNSVGUse @implementation RNSVGUse
- (void)setMergeList:(NSArray<NSString *> *)mergeList
{
if (mergeList == _mergeList) {
return;
}
_mergeList = mergeList;
[self invalidate];
}
- (void)renderLayerTo:(CGContextRef)context - (void)renderLayerTo:(CGContextRef)context
{ {
RNSVGNode* template = [[self getSvgView] getDefinedTemplate:self.href]; RNSVGNode* template = [[self getSvgView] getDefinedTemplate:self.href];
if (template) { if (template) {
[template mergeProperties:self]; [template mergeProperties:self mergeList:self.mergeList];
[template renderTo:context]; [template renderTo:context];
[template resetProperties];
} else if (self.href) { } else if (self.href) {
// TODO: calling yellow box here // TODO: calling yellow box here
RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href); RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href);

View File

@@ -36,16 +36,13 @@
*/ */
- (void)renderLayerTo:(CGContextRef)context; - (void)renderLayerTo:(CGContextRef)context;
- (void)renderClip:(CGContextRef)context;
/** /**
* clip node by clipPath or clipPathRef. * clip node by clipPath or clipPathRef.
*/ */
- (void)clip:(CGContextRef)context; - (void)clip:(CGContextRef)context;
/**
* get clip path for current node.
*/
- (CGPathRef)getClipPath;
/** /**
* getPath will return the path inside node as a ClipPath. * getPath will return the path inside node as a ClipPath.
*/ */
@@ -70,8 +67,13 @@
- (void)removeDefination; - (void)removeDefination;
/** /**
* merge owned properties into target element`s properties * Just for template node to merge target node`s properties into owned properties
*/ */
- (void)mergeProperties:(__kindof RNSVGNode *)target; - (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList;
/**
* Just for template node to reset all owned properties once after rendered.
*/
- (void)resetProperties;
@end @end

View File

@@ -11,6 +11,9 @@
#import "RNSVGClipPath.h" #import "RNSVGClipPath.h"
@implementation RNSVGNode @implementation RNSVGNode
{
CGFloat originOpacity;
}
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex - (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{ {
@@ -76,13 +79,22 @@
if (transparent) { if (transparent) {
CGContextBeginTransparencyLayer(context, NULL); CGContextBeginTransparencyLayer(context, NULL);
} }
[self renderClip:context];
[self renderLayerTo:context]; [self renderLayerTo:context];
if (transparent) { if (transparent) {
CGContextEndTransparencyLayer(context); CGContextEndTransparencyLayer(context);
} }
CGContextRestoreGState(context); CGContextRestoreGState(context);
} }
- (void)renderClip:(CGContextRef)context
{
if (self.clipPathRef) {
self.clipPath = [[[self getSvgView] getDefinedClipPath:self.clipPathRef] getPath:context];
}
}
- (void)setClipPath:(CGPathRef)clipPath - (void)setClipPath:(CGPathRef)clipPath
{ {
if (_clipPath == clipPath) { if (_clipPath == clipPath) {
@@ -99,25 +111,12 @@
return CGPathCreateMutable(); return CGPathCreateMutable();
} }
- (CGPathRef)getClipPath
{
CGPathRef clipPath = nil;
if (self.clipPath) {
clipPath = self.clipPath;
} else if (self.clipPathRef) {
clipPath = [[self getSvgView] getDefinedClipPath:self.clipPathRef];
}
return clipPath;
}
- (void)clip:(CGContextRef)context - (void)clip:(CGContextRef)context
{ {
CGPathRef clipPath = [self getClipPath]; CGPathRef clipPath = self.clipPath;
if (clipPath) { if (clipPath) {
CGContextAddPath(context, [self getClipPath]); CGContextAddPath(context, clipPath);
if (self.clipRule == kRNSVGCGFCRuleEvenodd) { if (self.clipRule == kRNSVGCGFCRuleEvenodd) {
CGContextEOClip(context); CGContextEOClip(context);
} else { } else {
@@ -182,12 +181,17 @@
} }
} }
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList
- (void)mergeProperties:(__kindof RNSVGNode *)target
{ {
originOpacity = self.opacity;
self.opacity = target.opacity * self.opacity; self.opacity = target.opacity * self.opacity;
} }
- (void)resetProperties
{
self.opacity = originOpacity;
}
- (void)dealloc - (void)dealloc
{ {
CGPathRelease(_clipPath); CGPathRelease(_clipPath);

View File

@@ -9,6 +9,10 @@
#import "RNSVGRenderable.h" #import "RNSVGRenderable.h"
@implementation RNSVGRenderable @implementation RNSVGRenderable
{
NSMutableDictionary *originProperties;
NSArray *changedList;
}
- (id)init - (id)init
{ {
@@ -93,6 +97,7 @@
CGContextSaveGState(context); CGContextSaveGState(context);
CGContextConcatCTM(context, self.transform); CGContextConcatCTM(context, self.transform);
CGContextSetAlpha(context, self.opacity); CGContextSetAlpha(context, self.opacity);
[self renderClip:context];
[self renderLayerTo:context]; [self renderLayerTo:context];
CGContextRestoreGState(context); CGContextRestoreGState(context);
} }
@@ -100,7 +105,7 @@
// hitTest delagate // hitTest delagate
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{ {
CGPathRef clipPath = [self getClipPath]; CGPathRef clipPath = self.clipPath;
if (self.nodeArea && CGPathContainsPoint(self.nodeArea, nil, point, NO)) { if (self.nodeArea && CGPathContainsPoint(self.nodeArea, nil, point, NO)) {
if (!clipPath) { if (!clipPath) {
return self; return self;
@@ -112,38 +117,33 @@
} }
} }
- (void)mergeProperties:(__kindof RNSVGNode *)target - (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList
{ {
RNSVGRenderable* renderableTarget = target;
if (renderableTarget.fill) { if (mergeList.count == 0) {
self.fill = renderableTarget.fill; return;
} }
if (renderableTarget.fillRule) {
self.fillRule = renderableTarget.fillRule; originProperties = [[NSMutableDictionary alloc] init];
changedList = mergeList;
for (NSString *key in mergeList) {
[originProperties setValue:[self valueForKey:key] forKey:key];
[self setValue:[target valueForKey:key] forKey:key];
} }
if (renderableTarget.stroke) {
self.stroke = renderableTarget.stroke; [super mergeProperties:target mergeList:mergeList];
}
- (void)resetProperties
{
if (changedList) {
for (NSString *key in changedList) {
[self setValue:[originProperties valueForKey:key] forKey:key];
}
} }
if (renderableTarget.strokeWidth) { [super resetProperties];
self.strokeWidth = renderableTarget.strokeWidth; changedList = nil;
}
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 - (void)renderLayerTo:(CGContextRef)context

View File

@@ -20,5 +20,5 @@ RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(href, NSString) RCT_EXPORT_VIEW_PROPERTY(href, NSString)
RCT_EXPORT_VIEW_PROPERTY(mergeList, NSArray<NSString *>)
@end @end

View File

@@ -42,6 +42,7 @@ function fontAndLinesDiffer(a, b) {
} }
const NodeAttributes = { const NodeAttributes = {
name: true,
transform: { transform: {
diff: arrayDiffer diff: arrayDiffer
}, },
@@ -50,7 +51,7 @@ const NodeAttributes = {
responsible: true responsible: true
}; };
const RenderableAttributes = merge(NodeAttributes, { const RenderableOnlyAttributes = {
fill: { fill: {
diff: arrayDiffer diff: arrayDiffer
}, },
@@ -70,26 +71,31 @@ const RenderableAttributes = merge(NodeAttributes, {
}, },
strokeDashoffset: true, strokeDashoffset: true,
strokeMiterlimit: true strokeMiterlimit: true
}); };
const GroupAttributes = merge(NodeAttributes, { const RenderableAttributes = merge({}, NodeAttributes, RenderableOnlyAttributes);
const GroupAttributes = merge({
clipPath: { clipPath: {
diff: arrayDiffer diff: arrayDiffer
}, },
clipRule: true clipRule: true
}); }, NodeAttributes);
const UseAttributes = merge(RenderableAttributes, { const UseAttributes = merge({
mergeList: {
diff: arrayDiffer
},
href: true href: true
}); }, RenderableAttributes);
const PathAttributes = merge(RenderableAttributes, { const PathAttributes = merge({
d: { d: {
diff: arrayDiffer diff: arrayDiffer
} }
}); }, RenderableAttributes);
const TextAttributes = merge(RenderableAttributes, { const TextAttributes = merge({
alignment: true, alignment: true,
frame: { frame: {
diff: fontAndLinesDiffer diff: fontAndLinesDiffer
@@ -97,48 +103,48 @@ const TextAttributes = merge(RenderableAttributes, {
path: { path: {
diff: arrayDiffer diff: arrayDiffer
} }
}); }, RenderableAttributes);
const ClipPathAttributes = merge(RenderableAttributes, { const ClipPathAttributes = {
name: true name: true
}); };
const CircleAttributes = merge(RenderableAttributes, { const CircleAttributes = merge({
cx: true, cx: true,
cy: true, cy: true,
r: true r: true
}); }, RenderableAttributes);
const EllipseAttributes = merge(RenderableAttributes, { const EllipseAttributes = merge({
cx: true, cx: true,
cy: true, cy: true,
rx: true, rx: true,
ry: true ry: true
}); }, RenderableAttributes);
const ImageAttributes = merge(RenderableAttributes, { const ImageAttributes = merge({
x: true, x: true,
y: true, y: true,
width: true, width: true,
height: true, height: true,
src: true src: true
}); }, RenderableAttributes);
const LineAttributes = merge(RenderableAttributes, { const LineAttributes = merge({
x1: true, x1: true,
y1: true, y1: true,
x2: true, x2: true,
y2: true y2: true
}); }, RenderableAttributes);
const RectAttributes = merge(RenderableAttributes, { const RectAttributes = merge({
x: true, x: true,
y: true, y: true,
width: true, width: true,
height: true, height: true,
rx: true, rx: true,
ry: true ry: true
}); }, RenderableAttributes);
export { export {
@@ -151,5 +157,6 @@ export {
ImageAttributes, ImageAttributes,
LineAttributes, LineAttributes,
RectAttributes, RectAttributes,
UseAttributes UseAttributes,
RenderableOnlyAttributes
}; };

View File

@@ -1,21 +1,11 @@
import SerializablePath from 'react-native/Libraries/ART/ARTSerializablePath'; import SerializablePath from 'react-native/Libraries/ART/ARTSerializablePath';
import clipReg from './patternReg'; import clipReg from './patternReg';
let clipPatterns = {};
const clipRules = { const clipRules = {
evenodd: 0, evenodd: 0,
nonzero: 1 nonzero: 1
}; };
function set(id, pattern) {
clipPatterns[id] = pattern;
}
function remove(id) {
delete clipPatterns[id];
}
export default function (props) { export default function (props) {
let {clipPath, clipRule} = props; let {clipPath, clipRule} = props;
let clippingProps = {}; let clippingProps = {};
@@ -26,22 +16,11 @@ export default function (props) {
let matched = clipPath.match(clipReg); let matched = clipPath.match(clipReg);
if (matched) { if (matched) {
let patternName = `${matched[1]}:${props.svgId}`; clippingProps.clipPathRef = matched[1];
let pattern = clipPatterns[patternName];
if (pattern) {
clippingProps.clipPathRef = pattern;
} else {
clippingProps = null;
// TODO: warn
}
} else { } else {
clippingProps.clipPath = new SerializablePath(clipPath).toJSON(); clippingProps.clipPath = new SerializablePath(clipPath).toJSON();
} }
} }
return clippingProps; return clippingProps;
} }
export {
set,
remove
};

View File

@@ -14,7 +14,7 @@ function fillFilter(props) {
if (fill === 'none') { if (fill === 'none') {
return null; return null;
} else if (fill) { } else if (fill) {
return patterns(fill, fillOpacity, props.svgId); return patterns(fill, fillOpacity);
} else if (props.fill === undefined) { } else if (props.fill === undefined) {
return rgba('#000', isNaN(fillOpacity) ? 1 : fillOpacity).rgbaString(); return rgba('#000', isNaN(fillOpacity) ? 1 : fillOpacity).rgbaString();
} else { } else {

View File

@@ -39,7 +39,7 @@ function strokeFilter(props) {
// TODO: propTypes check // TODO: propTypes check
return { return {
stroke: patterns(stroke, +props.strokeOpacity, props.svgId), stroke: patterns(stroke, +props.strokeOpacity),
strokeLinecap: caps[props.strokeLinecap] || 0, strokeLinecap: caps[props.strokeLinecap] || 0,
strokeLinejoin: joins[props.strokeLinejoin] || 0, strokeLinejoin: joins[props.strokeLinejoin] || 0,
strokeDasharray: strokeDasharray || null, strokeDasharray: strokeDasharray || null,

View File

@@ -86,44 +86,34 @@ const pathProps = {
}; };
const circleProps = { const circleProps = {
cx: numberProp, cx: numberProp.isRequired,
cy: numberProp, cy: numberProp.isRequired,
r: numberProp r: numberProp.isRequired
}; };
const ellipseProps = { const ellipseProps = {
cx: numberProp, cx: numberProp.isRequired,
cy: numberProp, cy: numberProp.isRequired,
rx: numberProp, rx: numberProp.isRequired,
ry: numberProp ry: numberProp.isRequired
}; };
const lineProps = { const lineProps = {
x1: numberProp, x1: numberProp.isRequired,
x2: numberProp, x2: numberProp.isRequired,
y1: numberProp, y1: numberProp.isRequired,
y2: numberProp y2: numberProp.isRequired
}; };
const rectProps = { const rectProps = {
x: numberProp, x: numberProp.isRequired,
y: numberProp, y: numberProp.isRequired,
width: numberProp, width: numberProp.isRequired,
height: numberProp, height: numberProp.isRequired,
rx: numberProp, rx: numberProp,
ry: numberProp ry: numberProp
}; };
const contextProps = {
...circleProps,
...ellipseProps,
...lineProps,
...rectProps,
...fillProps,
...strokeProps,
...textProps
};
export { export {
numberProp, numberProp,
fillProps, fillProps,
@@ -135,7 +125,6 @@ export {
ellipseProps, ellipseProps,
lineProps, lineProps,
rectProps, rectProps,
contextProps,
pathProps, pathProps,
responderProps, responderProps,
responderPropsKeys, responderPropsKeys,