Add gradient elements and examples

Add gradient elements and examples
This commit is contained in:
Horcrux 2016-01-25 15:50:06 +08:00
Родитель a28668b69f
Коммит 060cf7746b
18 изменённых файлов: 375 добавлений и 38 удалений

Просмотреть файл

@ -10,6 +10,7 @@ import * as Text from './examples/Text';
import * as G from './examples/G'; import * as G from './examples/G';
import * as Stroking from './examples/Stroking'; import * as Stroking from './examples/Stroking';
import * as Use from './examples/Use'; import * as Use from './examples/Use';
import * as Gradients from './examples/Gradients';
export { export {
Svg, Svg,
@ -23,5 +24,6 @@ export {
Text, Text,
Stroking, Stroking,
G, G,
Use Use,
Gradients
}; };

Просмотреть файл

@ -0,0 +1,121 @@
import React, {
Component
} from 'react-native';
import Svg, {
Defs,
LinearGradient,
RadialGradient,
Stop,
Ellipse,
Circle
} from 'react-native-art-svg';
class LinearGradientHorizontal extends Component{
static title = 'Define an ellipse with a horizontal linear gradient from yellow to red';
render() {
return <Svg
height="150"
width="300"
>
<Defs>
<LinearGradient id="grad" x1="0" y1="0" x2="170" y2="0">
<Stop offset="0%" stopColor="rgb(255,255,0)" stopOpacity="0" />
<Stop offset="100%" stopColor="red" stopOpacity="1" />
</LinearGradient>
</Defs>
<Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" />
</Svg>;
}
}
class LinearGradientVertical extends Component{
static title = 'Define an ellipse with a horizontal linear gradient from yellow to red';
render() {
return <Svg
height="150"
width="300"
>
<Defs>
<LinearGradient id="grad" x1="0" y1="0" x2="0" y2="150">
<Stop offset="0%" stopColor="rgb(255,255,0)" stopOpacity="0" />
<Stop offset="100%" stopColor="red" stopOpacity="1" />
</LinearGradient>
</Defs>
<Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" />
</Svg>;
}
}
class RadialGradientExample extends Component{
static title = 'Define an ellipse with a radial gradient from yellow to purple';
render() {
return <Svg
height="150"
width="300"
>
<Defs>
<RadialGradient id="grad" cx="150" cy="75" rx="85" ry="55" fx="150" fy="75">
<Stop
offset="0%"
stopColor="#ff0"
stopOpacity="1"
/>
<Stop
offset="100%"
stopColor="#83a"
stopOpacity="1"
/>
</RadialGradient>
</Defs>
<Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" />
</Svg>;
}
}
class RadialGradientExample2 extends Component{
static title = 'Define another ellipse with a radial gradient from white to blue';
render() {
return <Svg
height="150"
width="300"
>
<Defs>
<RadialGradient id="grad" cx="60" cy="45" rx="34" ry="33" fx="150" fy="75">
<Stop
offset="0%"
stopColor="#fff"
stopOpacity="1"
/>
<Stop
offset="100%"
stopColor="#00f"
stopOpacity="1"
/>
</RadialGradient>
</Defs>
<Ellipse cx="150" cy="75" rx="85" ry="55" fill="url(#grad)" />
</Svg>;
}
}
const icon = <Svg
height="20"
width="20"
>
<Defs>
<LinearGradient id="grad" x1="0" y1="0" x2="0" y2="20">
<Stop offset="0%" stopColor="rgb(255,255,0)" stopOpacity="0" />
<Stop offset="100%" stopColor="red" stopOpacity="1" />
</LinearGradient>
</Defs>
<Circle cx="10" cy="10" r="10" fill="url(#grad)" />
</Svg>;
const samples = [LinearGradientHorizontal, LinearGradientVertical, RadialGradientExample, RadialGradientExample2];
export {
icon,
samples
};

Просмотреть файл

@ -104,7 +104,7 @@ const styles = StyleSheet.create({
} }
}); });
const names = ['Svg', 'Stroking', 'Path', 'Line', 'Rect', 'Polygon', 'Polyline', 'Circle', 'Ellipse', 'G', 'Text', 'Use']; const names = ['Svg', 'Stroking', 'Path', 'Line', 'Rect', 'Polygon', 'Polyline', 'Circle', 'Ellipse', 'G', 'Text', 'Use', 'Gradients'];
class ArtSvgExample extends Component { class ArtSvgExample extends Component {
constructor() { constructor() {

Просмотреть файл

@ -6,8 +6,8 @@
"start": "react-native start" "start": "react-native start"
}, },
"dependencies": { "dependencies": {
"react-native": "^0 .18.1", "react-native": "^0.18.1",
"react-native-art-svg": "^1.0.1", "react-native-art-svg": "^1.0.3",
"react-native-root-modal": "^1.0.2" "react-native-root-modal": "^1.0.2"
} }
} }

Просмотреть файл

@ -9,15 +9,7 @@ TODO:
5. animations https://github.com/maxwellito/vivus 5. animations https://github.com/maxwellito/vivus
elements: elements:
1.defs 1.clipPath (wait for official complete)
2.tref ! 2.textPath (wait for official complete)
3.tspan ! 3.symbol
4.clipPath (wait for official todo) 4.pattern
5.glyph ? missing-glyph?
6.linearGradient
7.radialGradient
8.marker?
9.pattern
10.textPath
11.stop
12.symbol

Просмотреть файл

@ -8,6 +8,8 @@ import React, {
let {Group} = ART; let {Group} = ART;
let map = {}; let map = {};
import LinearGradient from './LinearGradient';
import RadialGradient from './RadialGradient';
let onlyChild = Children.only; let onlyChild = Children.only;
class DefsItem extends Component{ class DefsItem extends Component{
@ -81,6 +83,11 @@ class Defs extends Component{
getChildren = () => { getChildren = () => {
return Children.map(this.props.children, child => { return Children.map(this.props.children, child => {
if (child.type === LinearGradient || child.type === RadialGradient) {
return cloneElement(child, {
svgId: this.props.svgId
});
}
if (child.props.id) { if (child.props.id) {
return <DefsItem {...child.props} svgId={this.props.svgId}>{child}</DefsItem>; return <DefsItem {...child.props} svgId={this.props.svgId}>{child}</DefsItem>;
} }

Просмотреть файл

@ -3,8 +3,6 @@ import React, {
PropTypes, PropTypes,
ART ART
} from 'react-native'; } from 'react-native';
import fillFilter from '../lib/fillFilter';
import strokeFilter from '../lib/strokeFilter';
import Path from './Path'; import Path from './Path';
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);

Просмотреть файл

@ -0,0 +1,71 @@
import React, {
Component,
PropTypes,
ART,
Children
} from 'react-native';
let {
Group,
LinearGradient: ARTLinearGradient
} = ART;
import {set, remove} from '../lib/fillFilter';
import Stop from './Stop';
import Color from 'color';
let percentReg = /^(\-?\d+(?:\.\d+)?)(%?)$/;
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
class LinearGradient extends Component{
static displayName = 'LinearGradient';
static propTypes = {
x1: propType,
x2: propType,
y1: propType,
y2: propType,
id: PropTypes.string
};
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() {
let {
x1,
y1,
x2,
y2
} = this.props;
let stops = {};
Children.forEach(this.props.children, child => {
if (child.type === Stop && child.props.stopColor && child.props.offset) {
// convert percent to float.
let matched = child.props.offset.match(percentReg);
let offset = matched[2] ? matched[1] / 100 : matched[1];
// add stop
stops[offset] = Color(child.props.stopColor).alpha(+child.props.stopOpacity).rgbaString();
// TODO: convert percent to float.
set(this.id, new ARTLinearGradient(stops, x1, y1, x2, y2));
} else {
console.warn(`'LinearGradient' can only receive 'Stop' elements as children`);
}
});
return <Group />;
}
}
export default LinearGradient;

Просмотреть файл

@ -3,8 +3,6 @@ import React, {
PropTypes, PropTypes,
ART ART
} from 'react-native'; } from 'react-native';
import fillFilter from '../lib/fillFilter';
import strokeFilter from '../lib/strokeFilter';
import Path from './Path'; import Path from './Path';
class Polygon extends Component{ class Polygon extends Component{

Просмотреть файл

@ -5,10 +5,6 @@ import React, {
} from 'react-native'; } from 'react-native';
import Path from './Path'; import Path from './Path';
import strokeFilter from '../lib/strokeFilter';
import fillFilter from '../lib/fillFilter';
import transformFilter from '../lib/transformFilter';
class Polyline extends Component{ class Polyline extends Component{
static displayName = 'Polyline'; static displayName = 'Polyline';
static propTypes = { static propTypes = {

Просмотреть файл

@ -0,0 +1,83 @@
import React, {
Component,
PropTypes,
ART,
Children
} from 'react-native';
let {
Group,
RadialGradient: ARTRadialGradient
} = ART;
import {set, remove} from '../lib/fillFilter';
import Stop from './Stop';
import Color from 'color';
let percentReg = /^(\-?\d+(?:\.\d+)?)(%?)$/;
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
class RadialGradient extends Component{
static displayName = 'RadialGradient';
static propTypes = {
fx: propType,
fy: propType,
rx: propType,
ry: propType,
cx: propType,
cy: propType,
r: propType,
id: PropTypes.string
};
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() {
let {
fx,
fy,
rx,
ry,
cx,
cy,
r
} = this.props;
// TODO: render differently from svg in html
if (r) {
rx = ry = +r;
}
let stops = {};
Children.forEach(this.props.children, child => {
if (child.type === Stop && child.props.stopColor && child.props.offset) {
// convert percent to float.
let matched = child.props.offset.match(percentReg);
let offset = matched[2] ? matched[1] / 100 : matched[1];
// add stop
stops[offset] = Color(child.props.stopColor).alpha(+child.props.stopOpacity).rgbaString();
// TODO: convert percent to float.
set(this.id, new ARTRadialGradient(stops, fx, fy, rx, ry, cx, cy));
} else {
console.warn(`'RadialGradient' can only receive 'Stop' elements as children`);
}
});
return <Group />;
}
}
export default RadialGradient;

Просмотреть файл

@ -4,9 +4,6 @@ import React, {
ART ART
} from 'react-native'; } from 'react-native';
import fillFilter from '../lib/fillFilter';
import strokeFilter from '../lib/strokeFilter';
import transformFilter from '../lib/transformFilter';
import Path from './Path'; import Path from './Path';
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);

21
elements/Stop.js Normal file
Просмотреть файл

@ -0,0 +1,21 @@
import React, {
Component,
PropTypes,
ART
} from 'react-native';
let {
Group
} = ART;
class Stop extends Component{
static displayName = 'Stop';
static propTypes = {
stopColor: PropTypes.string,
stopOpacity: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
};
render() {
return null;
}
}
export default Stop;

Просмотреть файл

@ -3,6 +3,7 @@ import React, {
Component, Component,
PropTypes PropTypes
} from 'react-native'; } from 'react-native';
let { let {
Text:ARTText Text:ARTText
} = ART; } = ART;
@ -38,6 +39,7 @@ class Text extends Component{
id={this.props.id} id={this.props.id}
svgId={this.props.svgId} svgId={this.props.svgId}
visible={true} visible={true}
text={true}
> >
<Text {...this.props} id={null} /> <Text {...this.props} id={null} />
</Defs.Item>; </Defs.Item>;

Просмотреть файл

@ -1,9 +1,9 @@
import React, { import React, {
Component, Component,
PropTypes, PropTypes,
ART ART,
cloneElement
} from 'react-native'; } from 'react-native';
import Defs from './Defs'; import Defs from './Defs';
class Use extends Component{ class Use extends Component{
static displayName = 'Use'; static displayName = 'Use';

Просмотреть файл

@ -14,10 +14,13 @@ import G from './elements/G';
import Text from './elements/Text'; import Text from './elements/Text';
import Use from './elements/Use'; import Use from './elements/Use';
import Defs from './elements/Defs'; import Defs from './elements/Defs';
import LinearGradient from './elements/LinearGradient';
import RadialGradient from './elements/RadialGradient';
import Stop from './elements/Stop';
let { let {
Group Group
} = ART; } = ART;
export { export {
Svg, Svg,
@ -32,7 +35,10 @@ export {
Line, Line,
Rect, Rect,
Use, Use,
Defs Defs,
LinearGradient,
RadialGradient,
Stop
}; };
export default Svg; export default Svg;

Просмотреть файл

@ -1,4 +1,47 @@
import rgba from './rgba'; import rgba from './rgba';
import {
ART
} from 'react-native';
let {
LinearGradient,
RadialGradient
} = ART;
let fillPatterns = {};
let fillReg = /^url\(#(\w+?)\)$/;
export default function (props) { export default function (props) {
let {fill} = props;
if (fill) {
if (fill instanceof LinearGradient || fill instanceof RadialGradient) {
return fill;
}
// 尝试匹配 fill="url(#pattern)"
let matched = fill.match(fillReg);
if (matched) {
let patternName = `${matched[1]}:${props.svgId}`;
if (fillPatterns[patternName]) {
return fillPatterns[patternName];
} else {
return null;
}
}
}
return rgba(props.fill === undefined ? '#000' : props.fill, props.fillOpacity); return rgba(props.fill === undefined ? '#000' : props.fill, props.fillOpacity);
} }
function set(id, pattern) {
fillPatterns[id] = pattern;
}
function remove(id) {
delete fillPatterns[id];
}
export {
set,
remove
}

Просмотреть файл

@ -1,14 +1,14 @@
export default function (props) { export default function (props) {
let coords = props.origin ? props.origin.split(',') : []; let coords = props.origin ? props.origin.split(/\s*,\s*/) : [];
let originX = coords.length === 2 ? coords[0] : props.originX; let originX = coords.length === 2 ? coords[0] : props.originX;
let originY = coords.length === 2 ? coords[1] :props.originY; let originY = coords.length === 2 ? coords[1] : props.originY;
let scale = props.scale == 0 ? 0 : props.scale;
return { return {
rotation: props.rotation || props.rotate || 0, rotation: +props.rotation / 2 || +props.rotate / 2 || 0,
scale: props.scale || 1, scale: isNaN(scale) ? 1 : scale,
originX, originX: +originX || 0,
originY, originY: +originY || 0,
x: +props.x || 0, x: +props.x || 0,
y: +props.y || 0 y: +props.y || 0
} }