diff --git a/Example/examples/Clipping.js b/Example/examples/Clipping.js
index 8cc4d1b3..3195e8a6 100644
--- a/Example/examples/Clipping.js
+++ b/Example/examples/Clipping.js
@@ -7,13 +7,15 @@ import Svg, {
Defs,
RadialGradient,
Stop,
- Line,
Rect,
Text,
- G
+ Ellipse,
+ G,
+ Polygon,
+ Circle
} from 'react-native-art-svg';
-class ClipPathExample extends Component{
+class ClipPathAttr extends Component{
static title = 'Clip by set clip-path with a path data';
render() {
return ;
+ }
+}
+
const icon = ;
-const samples = [ClipPathExample, ClipRulePathExample];
+const samples = [ClipPathAttr, ClipRule, ClipPathElement];
export {
icon,
diff --git a/elements/Circle.js b/elements/Circle.js
index 52a02bea..ea11e311 100644
--- a/elements/Circle.js
+++ b/elements/Circle.js
@@ -16,10 +16,16 @@ class Circle extends Component{
cy: 0
};
+ static getPath = props => Ellipse.getPath({
+ cx: props.cx,
+ cy: props.cy,
+ rx: props.r,
+ ry: props.r
+ });
+
render() {
return
diff --git a/elements/ClipPath.js b/elements/ClipPath.js
index 2410c657..cb9644e7 100644
--- a/elements/ClipPath.js
+++ b/elements/ClipPath.js
@@ -4,6 +4,7 @@ import React, {
Children
} from 'react-native';
import {NativeGroup} from './G';
+import {set, remove} from '../lib/extract/extractClipping';
class ClipPath extends Component{
static displayName = 'ClipPath';
@@ -11,9 +12,41 @@ class ClipPath extends Component{
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);
+ };
+
+ _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() {
- // TODO: 合并children路径
- // TODO: clip-rule
+ this._combinePaths(this.props.children);
+ set(this.id, this._path);
return ;
}
}
diff --git a/elements/Defs.js b/elements/Defs.js
index abb53c22..64e6e9e3 100644
--- a/elements/Defs.js
+++ b/elements/Defs.js
@@ -9,6 +9,7 @@ let map = {};
import LinearGradient from './LinearGradient';
import RadialGradient from './RadialGradient';
+import ClipPath from './ClipPath';
let onlyChild = Children.only;
class DefsItem extends Component{
@@ -82,7 +83,9 @@ class Defs extends Component{
getChildren = () => {
return Children.map(this.props.children, child => {
- if (child.type === LinearGradient || child.type === RadialGradient) {
+ let {type} = child;
+
+ if (type === LinearGradient || type === RadialGradient || type === ClipPath) {
return cloneElement(child, {
svgId: this.props.svgId
});
diff --git a/elements/Ellipse.js b/elements/Ellipse.js
index a35e7ed6..3fcdbc1c 100644
--- a/elements/Ellipse.js
+++ b/elements/Ellipse.js
@@ -12,21 +12,22 @@ class Ellipse extends Component{
rx: propType,
ry: propType
};
- render() {
- let {props} = this;
- let {cx, cy, rx, ry} = this.props;
- let d = `
+
+ static getPath = props => {
+ let {cx, cy, rx, ry} = props;
+ return `
M ${cx - rx} ${cy}
a ${rx}, ${ry} 0 1, 0 ${rx * 2}, 0
a ${rx}, ${ry} 0 1, 0 ${-rx * 2}, 0
Z
`;
+ };
+
+ render() {
+ let {props} = this;
+ let d = Ellipse.getPath(this.props);
return ;
}
diff --git a/elements/Line.js b/elements/Line.js
index aad6119b..bb41d19a 100644
--- a/elements/Line.js
+++ b/elements/Line.js
@@ -18,16 +18,15 @@ class Line extends Component{
strokeCap: PropTypes.oneOf(['butt', 'square', 'round'])
};
-
- _convertPath = (props = this.props) => {
- return `M${props.x1},${props.y1}L${props.x2},${props.y2}Z`;
- };
+ static getPath = (props) => (
+ `M${props.x1},${props.y1}L${props.x2},${props.y2}Z`
+ );
render() {
return ;
}
}
diff --git a/elements/Path.js b/elements/Path.js
index cad066d2..f50b1c0d 100644
--- a/elements/Path.js
+++ b/elements/Path.js
@@ -28,6 +28,8 @@ class Path extends Component{
strokeDasharray: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.number)])
};
+ static getPath = props => props.d;
+
_dimensions = null;
componentWillReceiveProps = nextProps => {
diff --git a/elements/Polygon.js b/elements/Polygon.js
index d8d918fe..0e867041 100644
--- a/elements/Polygon.js
+++ b/elements/Polygon.js
@@ -9,13 +9,12 @@ class Polygon extends Component{
static propTypes = {
points: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};
+ static getPath = props => (`M${props.points.trim().replace(/\s+/g, 'L')}z`);
+
render() {
- let props = this.props;
- let d = 'M' + props.points.trim().replace(/\s+/g, 'L') + 'z';
return ;
}
}
diff --git a/elements/Polyline.js b/elements/Polyline.js
index ca607adc..6eb6f5ab 100644
--- a/elements/Polyline.js
+++ b/elements/Polyline.js
@@ -13,13 +13,12 @@ class Polyline extends Component{
strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round']),
strokeJoin: PropTypes.oneOf(['miter', 'bevel', 'round'])
};
+ static getPath = props => (`M${props.points.trim().replace(/\s+/g, 'L')}`);
+
render() {
- let props = this.props;
- let d = 'M' + props.points.trim().replace(/\s+/g, 'L');
return ;
}
}
diff --git a/elements/Rect.js b/elements/Rect.js
index e15e8c8c..f8838b19 100644
--- a/elements/Rect.js
+++ b/elements/Rect.js
@@ -12,6 +12,38 @@ function processRadius(radius) {
return radius || 0;
}
+function getR(props) {
+ let {
+ width,
+ height,
+ rx,
+ ry
+ } = props;
+
+ rx = processRadius(rx);
+ ry = processRadius(ry);
+
+ if ((rx && !ry) || (ry && !rx)) {
+ if (rx) {
+ ry = rx;
+ } else {
+ rx = ry;
+ }
+ }
+
+ if (rx > width / 2) {
+ rx = width / 2;
+ }
+ if (ry > height / 2) {
+ ry = height / 2;
+ }
+
+ return {
+ rx,
+ ry
+ };
+}
+
class Rect extends Component{
static displayName = 'Rect';
static propTypes = {
@@ -32,36 +64,23 @@ class Rect extends Component{
rx: 0,
ry: 0
};
- render() {
- let {props} = this;
+ static getPath = (props, r) => {
let {
x,
y,
width,
- height,
- rx,
- ry
+ height
} = props;
- rx = processRadius(rx);
- ry = processRadius(ry);
+ if (!r) {
+ r = getR(props);
- if ((rx && !ry) || (ry && !rx)) {
- if (rx) {
- ry = rx;
- } else {
- rx = ry;
- }
}
+
+ let {rx, ry} = r;
- if (rx > width / 2) {
- rx = width / 2;
- }
- if (ry > height / 2) {
- ry = height / 2;
- }
- let d = (rx || ry) ? `
+ return (rx || ry) ? `
M ${x}, ${y}
h ${width - 2 * rx}
a ${rx},${ry} 0 0 1 ${rx},${ry}
@@ -79,15 +98,17 @@ class Rect extends Component{
h ${-width}
Z
`;
+ };
+
+ render() {
+ let r = getR(this.props);
return ;
}
}
diff --git a/index.js b/index.js
index 6b44e21f..682798b6 100644
--- a/index.js
+++ b/index.js
@@ -17,6 +17,7 @@ import Defs from './elements/Defs';
import LinearGradient from './elements/LinearGradient';
import RadialGradient from './elements/RadialGradient';
import Stop from './elements/Stop';
+import ClipPath from './elements/ClipPath';
export {
Svg,
@@ -34,7 +35,8 @@ export {
Defs,
LinearGradient,
RadialGradient,
- Stop
+ Stop,
+ ClipPath
};
export default Svg;
diff --git a/lib/extract/extractClipping.js b/lib/extract/extractClipping.js
index 2624e51e..31477e20 100644
--- a/lib/extract/extractClipping.js
+++ b/lib/extract/extractClipping.js
@@ -1,18 +1,47 @@
import SerializablePath from 'react-native/Libraries/ART/ARTSerializablePath';
+import clipReg from './patternReg';
+let clipPatterns = {};
const clipRules = {
evenodd: 0,
nonzero: 1
};
+function set(id, pattern) {
+ clipPatterns[id] = pattern;
+}
+
+function remove(id) {
+ delete clipPatterns[id];
+}
+
+
export default function (props) {
let {clipPath, clipRule} = props;
let clippingProps = {};
if (clipPath) {
- clippingProps.clipPath = new SerializablePath(clipPath).toJSON();
clippingProps.clipRule = clipRules[clipRule] === 0 ? 0 : 1;
+
+ let matched = clipPath.match(clipReg);
+
+ if (matched) {
+ let patternName = `${matched[1]}:${props.svgId}`;
+ let pattern = clipPatterns[patternName];
+ if (pattern) {
+ clippingProps.clipPath = new SerializablePath(pattern).toJSON();
+ } else {
+ // TODO: warn
+ }
+ } else {
+ clippingProps.clipPath = new SerializablePath(clipPath).toJSON();
+ }
}
return clippingProps;
}
+
+export {
+ set,
+ remove
+}
diff --git a/lib/extract/extractFill.js b/lib/extract/extractFill.js
index 302fe9ed..77dbdf52 100644
--- a/lib/extract/extractFill.js
+++ b/lib/extract/extractFill.js
@@ -2,9 +2,9 @@ import rgba from '../rgba';
import {LinearGradientGenerator} from '../../elements/LinearGradient';
import {RadialGradientGenerator} from '../../elements/RadialGradient';
import extractBrush from './extractBrush';
+import fillReg from './patternReg';
let fillPatterns = {};
-let fillReg = /^url\(#(\w+?)\)$/;
function isGradient(obj) {
return obj instanceof LinearGradientGenerator || obj instanceof RadialGradientGenerator;
diff --git a/lib/extract/patternReg.js b/lib/extract/patternReg.js
new file mode 100644
index 00000000..f8c58fad
--- /dev/null
+++ b/lib/extract/patternReg.js
@@ -0,0 +1 @@
+export default /^url\(#(\w+?)\)$/;