complete basic shapes(ios)

This commit is contained in:
Horcrux
2016-04-26 19:10:57 +08:00
parent d7a9e418ae
commit 24dcc83a80
25 changed files with 540 additions and 256 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

@@ -2,34 +2,32 @@ import React, {
Component, Component,
PropTypes PropTypes
} from 'react-native'; } from 'react-native';
import Ellipse from './Ellipse'; import Shape, {CIRCLE} from './Shape';
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); import {circleProps, pathProps} from '../lib/props';
class Circle extends Component{ import _ from 'lodash';
class Circle extends Shape{
static displayName = 'Circle'; static displayName = 'Circle';
static propTypes = { static propTypes = {
cx: propType, ...pathProps,
cy: propType, ...circleProps
r: propType
}; };
static defaultProps = { static defaultProps = {
cx: 0, cx: 0,
cy: 0 cy: 0,
r: 0
}; };
static getPath = props => Ellipse.getPath({ static contextTypes = {
cx: props.cx, ...circleProps,
cy: props.cy, isInGroup: PropTypes.bool
rx: props.r, };
ry: props.r
});
render() { constructor() {
return <Ellipse super(...arguments);
{...this.props} this.type = CIRCLE;
rx={+this.props.r} };
ry={+this.props.r}
/>
}
} }
export default Circle; export default Circle;

View File

@@ -2,35 +2,32 @@ import React, {
Component, Component,
PropTypes PropTypes
} from 'react-native'; } from 'react-native';
import Path from './Path'; import Shape, {ELLIPSE} from './Shape';
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); import {ellipseProps, pathProps} from '../lib/props';
class Ellipse extends Component{
class Ellipse extends Shape{
static displayName = 'Ellipse'; static displayName = 'Ellipse';
static propTypes = { static propTypes = {
cx: propType, ...pathProps,
cy: propType, ...ellipseProps
rx: propType,
ry: propType
}; };
static getPath = props => { static defaultProps = {
let {cx, cy, rx, ry} = props; cx: 0,
return ` cy: 0,
M ${cx - rx} ${cy} rx: 0,
a ${rx}, ${ry} 0 1, 0 ${rx * 2}, 0 ry: 0
a ${rx}, ${ry} 0 1, 0 ${-rx * 2}, 0
Z
`;
}; };
render() { static contextTypes = {
let {props} = this; ...ellipseProps,
let d = Ellipse.getPath(this.props); isInGroup: PropTypes.bool
return <Path };
{...props}
d={d} constructor() {
/>; super(...arguments);
} this.type = ELLIPSE;
};
} }
export default Ellipse; export default Ellipse;

View File

@@ -2,42 +2,34 @@ import React, {
Component, Component,
Children, Children,
cloneElement, cloneElement,
PropTypes,
requireNativeComponent requireNativeComponent
} from 'react-native'; } from 'react-native';
import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass'; import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass';
import Defs from './Defs'; import Defs from './Defs';
import _ from 'lodash';
import {GroupAttributes} from '../lib/attributes'; import {GroupAttributes} from '../lib/attributes';
import {numberProp, shapeProps} from '../lib/props';
const transformProps = {
scale: null,
scaleX: null,
scaleY: null,
rotate: null,
transform: null,
x: null,
y: null,
originX: null,
originY: null
};
const clipProps = {
clipPath: null,
clipRule: null
};
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';
getChildren = () => { static childContextTypes = {
return Children.map(this.props.children, child => cloneElement(child, { svgId: numberProp,
...this.props, isInGroup: PropTypes.bool,
...transformProps, ...shapeProps
...clipProps, };
...child.props,
id: null getChildContext = () => {
})); return _.reduce(shapeProps, (props, value, key) => {
props[key] = this.props[key];
return props;
}, {
svgId: this.props.svgId,
isInGroup: true
});
}; };
render() { render() {
@@ -56,7 +48,7 @@ class G extends Component{
return <NativeGroup return <NativeGroup
{...extractProps(this.props, {transform: true})} {...extractProps(this.props, {transform: true})}
> >
{this.getChildren()} {this.props.children}
</NativeGroup>; </NativeGroup>;
} }
} }

View File

@@ -2,30 +2,32 @@ import React, {
Component, Component,
PropTypes PropTypes
} from 'react-native'; } from 'react-native';
import Path from './Path'; import Shape, {LINE} from './Shape';
import {lineProps, pathProps} from '../lib/props';
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); class Line extends Shape{
class Line extends Component{
static displayName = 'Line'; static displayName = 'Line';
static propTypes = { static propTypes = {
x1: propType, ...pathProps,
x2: propType, ...lineProps
y1: propType,
y2: propType,
strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round'])
}; };
static getPath = (props) => ( static defaultProps = {
`M${props.x1},${props.y1} L${props.x2},${props.y2}` x1: 0,
); x2: 0,
y1: 0,
y2: 0
};
render() { static contextTypes = {
return <Path ...lineProps,
{...this.props} isInGroup: PropTypes.bool
d={Line.getPath(this.props)} };
/>;
} constructor() {
super(...arguments);
this.type = LINE;
};
} }
export default Line; export default Line;

View File

@@ -5,7 +5,7 @@ import React, {
} from 'react-native'; } from 'react-native';
import stopsOpacity from '../lib/stopsOpacity'; import stopsOpacity from '../lib/stopsOpacity';
import numberProp from '../lib/numberProp'; import {numberProp} from '../lib/props';
import Gradient from './Gradient'; import Gradient from './Gradient';
import {LINEAR_GRADIENT} from '../lib/extract/extractBrush'; import {LINEAR_GRADIENT} from '../lib/extract/extractBrush';
import insertColorStopsIntoArray from '../lib/insertProcessor'; import insertColorStopsIntoArray from '../lib/insertProcessor';

View File

@@ -1,8 +1,7 @@
import React, { import React, {
Component, Component,
PropTypes, PropTypes,
requireNativeComponent, requireNativeComponent
cloneElement
} from 'react-native'; } from 'react-native';
import Defs from './Defs'; import Defs from './Defs';
@@ -11,24 +10,23 @@ import calculateBoundingBox from '../lib/calculateBoundingBox';
import extractProps from '../lib/extract/extractProps'; import extractProps from '../lib/extract/extractProps';
import SerializablePath from 'react-native/Libraries/ART/ARTSerializablePath'; import SerializablePath from 'react-native/Libraries/ART/ARTSerializablePath';
import {PathAttributes} from '../lib/attributes'; import {PathAttributes} from '../lib/attributes';
import {pathProps} from '../lib/props';
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
class Path extends Component{ class Path extends Component{
static displayName = 'Path'; static displayName = 'Path';
static propTypes = { static propTypes = {
visible: PropTypes.bool, visible: PropTypes.bool,
d: PropTypes.string, d: PropTypes.string,
x: propType, ...pathProps
y: propType, };
strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']),
strokeCap: PropTypes.oneOf(['butt', 'square', 'round']), static contextTypes = {
strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round']), ...pathProps,
strokeJoin: PropTypes.oneOf(['miter', 'bevel', 'round']), isInGroup: PropTypes.bool
strokeDasharray: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.number)])
}; };
static getPath = props => props.d;
_dimensions = null; _dimensions = null;
@@ -38,16 +36,15 @@ class Path extends Component{
} }
}; };
getBoundingBox = () => {
if (!this._dimensions) {
this._dimensions = calculateBoundingBox(this.props.d);
}
return this._dimensions;
};
render() { render() {
let {props} = this; let {props} = this;
if (this.context.isInGroup) {
props = _.defaults(this.context, props, {
isInGroup: null
});
}
if (props.id) { if (props.id) {
return <Defs.Item return <Defs.Item
id={props.id} id={props.id}
@@ -62,7 +59,7 @@ class Path extends Component{
return ( return (
<NativePath <NativePath
{...extractProps.call(this, props)} {...extractProps(props)}
d={d} d={d}
/> />
); );

View File

@@ -4,7 +4,7 @@ import React, {
Children Children
} from 'react-native'; } from 'react-native';
import stopsOpacity from '../lib/stopsOpacity'; import stopsOpacity from '../lib/stopsOpacity';
import numberProp from '../lib/numberProp'; import {numberProp} from '../lib/props';
import Gradient from './Gradient'; import Gradient from './Gradient';
import {RADIAL_GRADIENT} from '../lib/extract/extractBrush'; import {RADIAL_GRADIENT} from '../lib/extract/extractBrush';
import insertColorStopsIntoArray from '../lib/insertProcessor'; import insertColorStopsIntoArray from '../lib/insertProcessor';

View File

@@ -2,115 +2,36 @@ import React, {
Component, Component,
PropTypes PropTypes
} from 'react-native'; } from 'react-native';
import Shape, {RECT} from './Shape';
import {rectProps, pathProps} from '../lib/props';
import Path from './Path'; class Rect extends Shape{
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
function processRadius(radius) {
radius = +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 displayName = 'Rect';
static propTypes = { static propTypes = {
x: propType, ...pathProps,
y: propType, ...rectProps
width: propType,
height: propType,
rx: propType,
ry: propType,
strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']),
strokeCap: PropTypes.oneOf(['butt', 'square', 'round']),
strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round']),
strokeJoin: PropTypes.oneOf(['miter', 'bevel', 'round'])
}; };
static defaultProps = { static defaultProps = {
x: 0, x: 0,
y: 0, y: 0,
width: 0,
height: 0,
rx: 0, rx: 0,
ry: 0 ry: 0
}; };
static getPath = (props, r) => {
let {
x,
y,
width,
height
} = props;
if (!r) { static contextTypes = {
r = getR(props); ...rectProps,
isInGroup: PropTypes.bool
}
let {rx, ry} = r;
return (rx || ry) ? `
M ${x}, ${y}
h ${width - 2 * rx}
a ${rx},${ry} 0 0 1 ${rx},${ry}
v ${height - 2 * ry}
a ${rx},${ry} 0 0 1 ${-rx},${ry}
h ${2 * rx - width}
a ${rx},${ry} 0 0 1 ${-rx},${-ry}
v ${2 * ry - height}
a ${rx},${ry} 0 0 1 ${rx},${-ry}
Z
` : `
M ${x}, ${y}
h ${width}
v ${height}
h ${-width}
Z
`;
}; };
render() { constructor() {
let r = getR(this.props); super(...arguments);
return <Path this.type = RECT;
{...this.props} };
width={null}
height={null}
x={r.rx || null}
y={null}
d={Rect.getPath(this.props, r)}
/>;
}
} }
export default Rect; export default Rect;

View File

@@ -3,32 +3,43 @@ import React, {
PropTypes PropTypes
} from 'react-native'; } from 'react-native';
import Path from './Path'; import Path from './Path';
import _ from 'lodash';
import extractProps from '../lib/extract/extractProps'; import extractProps from '../lib/extract/extractProps';
import {ShapeAttributes} from '../lib/attributes'; import {ShapeAttributes} from '../lib/attributes';
import SerializableShape from '../lib/SerializableShape';
import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass'; import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass';
import {circleProps, ellipseProps, lineProps, rectProps} from '../lib/props';
/** /**
* Circle shape type * Circle shape type
*/ */
const CIRCLE = 0; const CIRCLE = 0;
/**
* ELLIPSE shape type
*/
const ELLIPSE = 1;
/**
* LINE shape type
*/
const LINE = 2;
/**
* RECT shape type
*/
const RECT = 3;
/** /**
* coord props * coord props
* */
const COORD_PROPS = {
/**
* algorithm for radius in percentage * algorithm for radius in percentage
* radius = Math.sqrt(Math.pow((width*percent), 2) + Math.pow((height*percent), 2)) / Math.sqrt(2); * radius = Math.sqrt(Math.pow((width*percent), 2) + Math.pow((height*percent), 2)) / Math.sqrt(2);
*/ */
const CIRCLE_COORDS = ['cx', 'cy', 'r']; [CIRCLE]: Object.keys(circleProps),
[ELLIPSE]: Object.keys(ellipseProps),
const ELLIPSE = 1; [LINE]: Object.keys(lineProps),
const ELLIPSE_COORDS = ['cx', 'cy', 'rx', 'ry']; [RECT]: Object.keys(rectProps)
};
const LINE = 2;
const LINE_COORDS = ['x1', 'y1', 'x2', 'y2'];
const RECT = 5;
const RECT_COORDS = ['x', 'y', 'width', 'height'];
/** /**
* virtualNode component for shape elements * virtualNode component for shape elements
@@ -37,14 +48,29 @@ class Shape extends Component{
static displayName = 'Shape'; static displayName = 'Shape';
render() { render() {
let {props} = this; let props = this.props;
if (this.context.isInGroup) {
props = _.defaults(this.context, props, {
isInGroup: null
});
}
let shape = new SerializableShape(props, COORD_PROPS[this.type]).toJSON();
return <NativePath return <NativePath
{...props} {...props}
{...extractProps.call(props)} {...extractProps(this.type === 3 ? {
shape={new SerializableShape(props).toArray()} ...props,
x: null,
y: null
} : props)}
shape={{
...shape,
type: this.type
}}
/>; />;
} };
} }
let NativePath = createReactNativeComponentClass({ let NativePath = createReactNativeComponentClass({
@@ -53,3 +79,10 @@ let NativePath = createReactNativeComponentClass({
}); });
export default Shape; export default Shape;
export {
CIRCLE,
ELLIPSE,
LINE,
RECT
};

View File

@@ -6,7 +6,7 @@ import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/
import extractProps from '../lib/extract/extractProps'; import extractProps from '../lib/extract/extractProps';
import extractText from '../lib/extract/extractText'; import extractText from '../lib/extract/extractText';
import {TextAttributes} from '../lib/attributes'; import {TextAttributes} from '../lib/attributes';
import numberProp from '../lib/numberProp'; import {numberProp} from '../lib/props';
class Text extends Component{ class Text extends Component{
static displayName = 'Text'; static displayName = 'Text';

View File

@@ -1,7 +1,6 @@
import React, { import React, {
Component, Component,
PropTypes, PropTypes
cloneElement
} from 'react-native'; } from 'react-native';
import Defs from './Defs'; import Defs from './Defs';
class Use extends Component{ class Use extends Component{

View File

@@ -25,6 +25,8 @@
10A062FF1CC732020000CEEF /* RNSVGSvgViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A062FD1CC732020000CEEF /* RNSVGSvgViewManager.m */; }; 10A062FF1CC732020000CEEF /* RNSVGSvgViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A062FD1CC732020000CEEF /* RNSVGSvgViewManager.m */; };
10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063011CC7320C0000CEEF /* RNSVGPath.m */; }; 10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063011CC7320C0000CEEF /* RNSVGPath.m */; };
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063031CC7320C0000CEEF /* RNSVGSvgView.m */; }; 10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063031CC7320C0000CEEF /* RNSVGSvgView.m */; };
10C068671CCF0F87007C6982 /* RNSVGShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 10C068651CCF0F87007C6982 /* RNSVGShape.m */; };
10C0686A1CCF1061007C6982 /* RNSVGShapeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10C068691CCF1061007C6982 /* RNSVGShapeManager.m */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
@@ -80,6 +82,10 @@
10A063011CC7320C0000CEEF /* RNSVGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGPath.m; sourceTree = "<group>"; }; 10A063011CC7320C0000CEEF /* RNSVGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGPath.m; sourceTree = "<group>"; };
10A063021CC7320C0000CEEF /* RNSVGSvgView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGSvgView.h; sourceTree = "<group>"; }; 10A063021CC7320C0000CEEF /* RNSVGSvgView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGSvgView.h; sourceTree = "<group>"; };
10A063031CC7320C0000CEEF /* RNSVGSvgView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSvgView.m; sourceTree = "<group>"; }; 10A063031CC7320C0000CEEF /* RNSVGSvgView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSvgView.m; sourceTree = "<group>"; };
10C068641CCF0F87007C6982 /* RNSVGShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGShape.h; sourceTree = SOURCE_ROOT; };
10C068651CCF0F87007C6982 /* RNSVGShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGShape.m; sourceTree = SOURCE_ROOT; };
10C068681CCF1061007C6982 /* RNSVGShapeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGShapeManager.h; sourceTree = "<group>"; };
10C068691CCF1061007C6982 /* RNSVGShapeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGShapeManager.m; sourceTree = "<group>"; };
10FEAC6A1CC7D05200F1C23C /* RNSVGCGFCRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGCGFCRule.h; sourceTree = "<group>"; }; 10FEAC6A1CC7D05200F1C23C /* RNSVGCGFCRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGCGFCRule.h; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@@ -104,10 +110,12 @@
0CF68ADC1AF0549300FF9E5C /* RNSVGContainer.h */, 0CF68ADC1AF0549300FF9E5C /* RNSVGContainer.h */,
0CF68ADD1AF0549300FF9E5C /* RNSVGGroup.h */, 0CF68ADD1AF0549300FF9E5C /* RNSVGGroup.h */,
0CF68ADE1AF0549300FF9E5C /* RNSVGGroup.m */, 0CF68ADE1AF0549300FF9E5C /* RNSVGGroup.m */,
0CF68ADF1AF0549300FF9E5C /* RNSVGNode.h */, 10C068641CCF0F87007C6982 /* RNSVGShape.h */,
0CF68AE01AF0549300FF9E5C /* RNSVGNode.m */, 10C068651CCF0F87007C6982 /* RNSVGShape.m */,
10A063001CC7320C0000CEEF /* RNSVGPath.h */, 10A063001CC7320C0000CEEF /* RNSVGPath.h */,
10A063011CC7320C0000CEEF /* RNSVGPath.m */, 10A063011CC7320C0000CEEF /* RNSVGPath.m */,
0CF68ADF1AF0549300FF9E5C /* RNSVGNode.h */,
0CF68AE01AF0549300FF9E5C /* RNSVGNode.m */,
10A063021CC7320C0000CEEF /* RNSVGSvgView.h */, 10A063021CC7320C0000CEEF /* RNSVGSvgView.h */,
10A063031CC7320C0000CEEF /* RNSVGSvgView.m */, 10A063031CC7320C0000CEEF /* RNSVGSvgView.m */,
0CF68AE11AF0549300FF9E5C /* RNSVGRenderable.h */, 0CF68AE11AF0549300FF9E5C /* RNSVGRenderable.h */,
@@ -149,6 +157,8 @@
0CF68AF81AF0549300FF9E5C /* ViewManagers */ = { 0CF68AF81AF0549300FF9E5C /* ViewManagers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
10C068681CCF1061007C6982 /* RNSVGShapeManager.h */,
10C068691CCF1061007C6982 /* RNSVGShapeManager.m */,
10A062FA1CC732020000CEEF /* RNSVGPathManager.h */, 10A062FA1CC732020000CEEF /* RNSVGPathManager.h */,
10A062FB1CC732020000CEEF /* RNSVGPathManager.m */, 10A062FB1CC732020000CEEF /* RNSVGPathManager.m */,
10A062FC1CC732020000CEEF /* RNSVGSvgViewManager.h */, 10A062FC1CC732020000CEEF /* RNSVGSvgViewManager.h */,
@@ -220,6 +230,7 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
10C068671CCF0F87007C6982 /* RNSVGShape.m in Sources */,
0CF68B161AF0549300FF9E5C /* RNSVGTextManager.m in Sources */, 0CF68B161AF0549300FF9E5C /* RNSVGTextManager.m in Sources */,
10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */, 10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */,
0CF68B111AF0549300FF9E5C /* RNSVGGroupManager.m in Sources */, 0CF68B111AF0549300FF9E5C /* RNSVGGroupManager.m in Sources */,
@@ -233,6 +244,7 @@
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */, 10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */,
0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */, 0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */,
0CF68B101AF0549300FF9E5C /* RCTConvert+RNSVG.m in Sources */, 0CF68B101AF0549300FF9E5C /* RCTConvert+RNSVG.m in Sources */,
10C0686A1CCF1061007C6982 /* RNSVGShapeManager.m in Sources */,
10A062FE1CC732020000CEEF /* RNSVGPathManager.m in Sources */, 10A062FE1CC732020000CEEF /* RNSVGPathManager.m in Sources */,
0CF68B061AF0549300FF9E5C /* RNSVGNode.m in Sources */, 0CF68B061AF0549300FF9E5C /* RNSVGNode.m in Sources */,
0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColor.m in Sources */, 0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColor.m in Sources */,

View File

@@ -18,8 +18,8 @@
@interface RNSVGNode : UIView @interface RNSVGNode : UIView
@property (nonatomic, assign) CGRect rect;
@property (nonatomic, assign) CGFloat opacity; @property (nonatomic, assign) CGFloat opacity;
@property (nonatomic, assign) CGPathRef clipPath; @property (nonatomic, assign) CGPathRef clipPath;
@property (nonatomic, assign) RNSVGCGFCRule clipRule; @property (nonatomic, assign) RNSVGCGFCRule clipRule;

View File

@@ -80,7 +80,6 @@
_clipPath = CGPathRetain(clipPath); _clipPath = CGPathRetain(clipPath);
} }
- (void)dealloc - (void)dealloc
{ {
CGPathRelease(_clipPath); CGPathRelease(_clipPath);

17
ios/RNSVGShape.h Normal file
View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2015-present, Horcrux.
* All rights reserved.
*
* This source code is licensed under the MIT-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import "RNSVGRenderable.h"
#import "RNSVGPath.h"
@interface RNSVGShape : RNSVGPath
@property (nonatomic, strong) NSDictionary* shape;
@end

115
ios/RNSVGShape.m Normal file
View File

@@ -0,0 +1,115 @@
/**
* Copyright (c) 2015-present, Horcrux.
* All rights reserved.
*
* This source code is licensed under the MIT-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGShape.h"
#import "RCTLog.h"
@implementation RNSVGShape
- (void)dealloc
{
}
- (void)renderLayerTo:(CGContextRef)context
{
int type = [[self.shape objectForKey:@"type"] intValue];
CGRect box = CGContextGetClipBoundingBox(context);
CGMutablePathRef path = CGPathCreateMutable();
float height = CGRectGetHeight(box);
float width = CGRectGetWidth(box);
switch (type) {
case 0:
{
// draw circle
CGFloat cx = [self getActualProp:@"cx" relative:width];
CGFloat cy = [self getActualProp:@"cy" relative:height];
CGFloat r = [self getActualProp:@"r" relative:height];
CGPathAddArc(path, nil, cx, cy, r, 0, 2*M_PI, YES);
break;
}
case 1:
{
// draw ellipse
CGFloat cx = [self getActualProp:@"cx" relative:width];
CGFloat cy = [self getActualProp:@"cy" relative:height];
CGFloat rx = [self getActualProp:@"rx" relative:height];
CGFloat ry = [self getActualProp:@"ry" relative:height];
CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx / 2, cy - ry / 2, rx * 2, ry * 2));
break;
}
case 2:
{
// draw line
CGFloat x1 = [self getActualProp:@"x1" relative:height];
CGFloat y1 = [self getActualProp:@"y1" relative:height];
CGFloat x2 = [self getActualProp:@"x2" relative:height];
CGFloat y2 = [self getActualProp:@"y2" relative:height];
CGPathMoveToPoint(path, nil, x1, y1);
CGPathAddLineToPoint(path, nil, x2, y2);
break;
}
case 3:
{
// draw rect
CGPathMoveToPoint(path, NULL, 0, 0);
CGFloat x = [self getActualProp:@"x" relative:width];
CGFloat y = [self getActualProp:@"y" relative:height];
CGFloat w = [self getActualProp:@"width" relative:width];
CGFloat h = [self getActualProp:@"height" relative:height];
CGFloat rx = [self getActualProp:@"rx" relative:width];
CGFloat ry = [self getActualProp:@"ry" relative:height];
if (rx != 0 || ry != 0) {
if (rx == 0) {
rx = ry;
} else if (ry == 0) {
ry = rx;
}
if (rx > w / 2) {
rx = w / 2;
}
if (ry > h / 2) {
ry = h / 2;
}
CGPathAddRoundedRect(path, nil, CGRectMake(x, y, w, h), rx, ry);
} else {
CGPathAddRect(path, nil, CGRectMake(x, y, w, h));
}
break;
}
default:
RCTLogError(@"Invalid Shape type %d at %@", type, self.shape);
//CGPathRelease(path);
}
self.d = path;
[super renderLayerTo:context];
//NSLog(@"%@", NSStringFromCGRect(box));
//NSLog(@"%@", self.shape);
}
- (CGFloat)getActualProp:(NSString *)name relative:(float)relative
{
NSDictionary *prop = [self.shape objectForKey:name];
CGFloat value = [[prop objectForKey:@"value"] floatValue];
if ([[prop objectForKey:@"percentage"] integerValue] == 1) {
return relative * value;
} else {
return value;
}
}
@end

View File

@@ -0,0 +1,13 @@
/**
* Copyright (c) 2015-present, Horcrux.
* All rights reserved.
*
* This source code is licensed under the MIT-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGRenderableManager.h"
@interface RNSVGShapeManager : RNSVGRenderableManager
@end

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2015-present, Horcrux.
* All rights reserved.
*
* This source code is licensed under the MIT-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RNSVGShapeManager.h"
#import "RNSVGShape.h"
#import "RCTConvert+RNSVG.h"
@implementation RNSVGShapeManager
RCT_EXPORT_MODULE()
- (RNSVGRenderable *)node
{
return [RNSVGShape new];
}
RCT_EXPORT_VIEW_PROPERTY(shape, NSDictionary)
@end

View File

@@ -1,10 +1,55 @@
export default class { /**
constructor(props) { * Format potential percentage props
*
* convert somet props like those
* width="50%"
* height="500"
*
* to
* {
* width: {
* percentage: true,
* value: 0.5
* },
* height: {
* percentage: false,
* value: 500
* }
* }
*
*
*/
import percentToFloat from './percentToFloat';
function percentageTransform(value) {
if (typeof value === 'number') {
return {
percentage: false,
value
};
} }
toArray = () => { let float = percentToFloat(value);
return {
percentage: float !== +value,
value: float
}
}
export default class {
constructor(props, list) {
this.shape = {};
list.forEach(name => {
if (props[name] != null) {
this.shape[name] = percentageTransform(props[name]);
}
});
}
toJSON = () => {
return this.shape;
}; };
}; };

View File

@@ -6,7 +6,7 @@ function arrayDiffer(a, b) {
if (a.length !== b.length) { if (a.length !== b.length) {
return true; return true;
} }
for (var i = 0; i < a.length; i++) { for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) { if (a[i] !== b[i]) {
return true; return true;
} }
@@ -14,6 +14,22 @@ function arrayDiffer(a, b) {
return false; return false;
} }
function shapeDiffer(a, b) {
if (a === b) {
return false;
}
for (let key in a) {
if (a.hasOwnProperty(key)) {
if (key === 'type' && a.type !== b.type) {
return true;
} else if (a[key].percentage !== b[key].percentage || a[key].value !== b[key].value) {
return true;
}
}
}
return false;
}
function fontAndLinesDiffer(a, b) { function fontAndLinesDiffer(a, b) {
if (a === b) { if (a === b) {
return false; return false;
@@ -92,7 +108,7 @@ const TextAttributes = {
const ShapeAttributes = { const ShapeAttributes = {
shape: { shape: {
diff: arrayDiffer diff: shapeDiffer
}, },
...RenderableAttributes ...RenderableAttributes
}; };

View File

@@ -75,7 +75,7 @@ function extractFont(font) {
}; };
} }
const alignments = { const anchord = {
right: 1, right: 1,
center: 2, center: 2,
left: 0 left: 0
@@ -83,7 +83,7 @@ const alignments = {
export default function(props) { export default function(props) {
return { return {
alignment: alignments[props.textAnchor] || 0, alignment: anchord[props.textAnchor] || 0,
frame: extractFontAndLines( frame: extractFontAndLines(
props, props,
childrenAsString(props.children) childrenAsString(props.children)

103
lib/props.js Normal file
View File

@@ -0,0 +1,103 @@
import {
PropTypes
} from 'react-native';
const numberProp = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
const fillProps = {
fill: PropTypes.string,
fillOpacity: numberProp,
fillRule: PropTypes.oneOf(['evenodd', 'nonzero'])
};
const clipProps = {
clipRule: PropTypes.oneOf(['evenodd', 'nonzero']),
clipPath: PropTypes.string
};
const strokeProps = {
stroke: PropTypes.string,
strokeWidth: numberProp,
strokeOpacity: numberProp,
strokeDasharray: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.string]),
strokeDashoffset: numberProp,
strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']),
strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round'])
};
const textProps = {
textAnchor: PropTypes.oneOf(['right', 'center', 'left']),
path: PropTypes.string
};
const transformProps = {
scale: numberProp,
scaleX: numberProp,
scaleY: numberProp,
rotate: numberProp,
x: numberProp,
y: numberProp,
originX: numberProp,
originY: numberProp,
transform: PropTypes.string
};
const pathProps = {
...fillProps,
...strokeProps,
...clipProps,
...transformProps
};
const circleProps = {
cx: numberProp,
cy: numberProp,
r: numberProp
};
const ellipseProps = {
cx: numberProp,
cy: numberProp,
rx: numberProp,
ry: numberProp
};
const lineProps = {
x1: numberProp,
x2: numberProp,
y1: numberProp,
y2: numberProp
};
const rectProps = {
x: numberProp,
y: numberProp,
width: numberProp,
height: numberProp,
rx: numberProp,
ry: numberProp
};
const shapeProps = {
...circleProps,
...ellipseProps,
...lineProps,
...rectProps,
...fillProps,
...strokeProps
};
export {
numberProp,
fillProps,
clipProps,
strokeProps,
transformProps,
textProps,
circleProps,
ellipseProps,
lineProps,
rectProps,
shapeProps,
pathProps
}