Add gradient elements and examples
Add gradient elements and examples
This commit is contained in:
Родитель
a28668b69f
Коммит
060cf7746b
|
@ -10,6 +10,7 @@ import * as Text from './examples/Text';
|
|||
import * as G from './examples/G';
|
||||
import * as Stroking from './examples/Stroking';
|
||||
import * as Use from './examples/Use';
|
||||
import * as Gradients from './examples/Gradients';
|
||||
|
||||
export {
|
||||
Svg,
|
||||
|
@ -23,5 +24,6 @@ export {
|
|||
Text,
|
||||
Stroking,
|
||||
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 {
|
||||
constructor() {
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
"start": "react-native start"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-native": "^0 .18.1",
|
||||
"react-native-art-svg": "^1.0.1",
|
||||
"react-native": "^0.18.1",
|
||||
"react-native-art-svg": "^1.0.3",
|
||||
"react-native-root-modal": "^1.0.2"
|
||||
}
|
||||
}
|
||||
|
|
16
README.md
16
README.md
|
@ -9,15 +9,7 @@ TODO:
|
|||
5. animations https://github.com/maxwellito/vivus
|
||||
|
||||
elements:
|
||||
1.defs
|
||||
2.tref !
|
||||
3.tspan !
|
||||
4.clipPath (wait for official todo)
|
||||
5.glyph ? missing-glyph?
|
||||
6.linearGradient
|
||||
7.radialGradient
|
||||
8.marker?
|
||||
9.pattern
|
||||
10.textPath
|
||||
11.stop
|
||||
12.symbol
|
||||
1.clipPath (wait for official complete)
|
||||
2.textPath (wait for official complete)
|
||||
3.symbol
|
||||
4.pattern
|
||||
|
|
|
@ -8,6 +8,8 @@ import React, {
|
|||
let {Group} = ART;
|
||||
let map = {};
|
||||
|
||||
import LinearGradient from './LinearGradient';
|
||||
import RadialGradient from './RadialGradient';
|
||||
let onlyChild = Children.only;
|
||||
|
||||
class DefsItem extends Component{
|
||||
|
@ -81,6 +83,11 @@ class Defs extends Component{
|
|||
|
||||
getChildren = () => {
|
||||
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) {
|
||||
return <DefsItem {...child.props} svgId={this.props.svgId}>{child}</DefsItem>;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@ import React, {
|
|||
PropTypes,
|
||||
ART
|
||||
} from 'react-native';
|
||||
import fillFilter from '../lib/fillFilter';
|
||||
import strokeFilter from '../lib/strokeFilter';
|
||||
import Path from './Path';
|
||||
|
||||
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,
|
||||
ART
|
||||
} from 'react-native';
|
||||
import fillFilter from '../lib/fillFilter';
|
||||
import strokeFilter from '../lib/strokeFilter';
|
||||
import Path from './Path';
|
||||
|
||||
class Polygon extends Component{
|
||||
|
|
|
@ -5,10 +5,6 @@ import React, {
|
|||
} from 'react-native';
|
||||
import Path from './Path';
|
||||
|
||||
|
||||
import strokeFilter from '../lib/strokeFilter';
|
||||
import fillFilter from '../lib/fillFilter';
|
||||
import transformFilter from '../lib/transformFilter';
|
||||
class Polyline extends Component{
|
||||
static displayName = 'Polyline';
|
||||
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
|
||||
} from 'react-native';
|
||||
|
||||
import fillFilter from '../lib/fillFilter';
|
||||
import strokeFilter from '../lib/strokeFilter';
|
||||
import transformFilter from '../lib/transformFilter';
|
||||
import Path from './Path';
|
||||
|
||||
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
||||
|
|
|
@ -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,
|
||||
PropTypes
|
||||
} from 'react-native';
|
||||
|
||||
let {
|
||||
Text:ARTText
|
||||
} = ART;
|
||||
|
@ -38,6 +39,7 @@ class Text extends Component{
|
|||
id={this.props.id}
|
||||
svgId={this.props.svgId}
|
||||
visible={true}
|
||||
text={true}
|
||||
>
|
||||
<Text {...this.props} id={null} />
|
||||
</Defs.Item>;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React, {
|
||||
Component,
|
||||
PropTypes,
|
||||
ART
|
||||
ART,
|
||||
cloneElement
|
||||
} from 'react-native';
|
||||
|
||||
import Defs from './Defs';
|
||||
class Use extends Component{
|
||||
static displayName = 'Use';
|
||||
|
|
10
index.js
10
index.js
|
@ -14,10 +14,13 @@ import G from './elements/G';
|
|||
import Text from './elements/Text';
|
||||
import Use from './elements/Use';
|
||||
import Defs from './elements/Defs';
|
||||
import LinearGradient from './elements/LinearGradient';
|
||||
import RadialGradient from './elements/RadialGradient';
|
||||
import Stop from './elements/Stop';
|
||||
|
||||
let {
|
||||
Group
|
||||
} = ART;
|
||||
} = ART;
|
||||
|
||||
export {
|
||||
Svg,
|
||||
|
@ -32,7 +35,10 @@ export {
|
|||
Line,
|
||||
Rect,
|
||||
Use,
|
||||
Defs
|
||||
Defs,
|
||||
LinearGradient,
|
||||
RadialGradient,
|
||||
Stop
|
||||
};
|
||||
|
||||
export default Svg;
|
||||
|
|
|
@ -1,4 +1,47 @@
|
|||
import rgba from './rgba';
|
||||
import {
|
||||
ART
|
||||
} from 'react-native';
|
||||
|
||||
let {
|
||||
LinearGradient,
|
||||
RadialGradient
|
||||
} = ART;
|
||||
|
||||
let fillPatterns = {};
|
||||
let fillReg = /^url\(#(\w+?)\)$/;
|
||||
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);
|
||||
}
|
||||
|
||||
function set(id, pattern) {
|
||||
fillPatterns[id] = pattern;
|
||||
}
|
||||
|
||||
function remove(id) {
|
||||
delete fillPatterns[id];
|
||||
}
|
||||
|
||||
export {
|
||||
set,
|
||||
remove
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
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 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 {
|
||||
rotation: props.rotation || props.rotate || 0,
|
||||
scale: props.scale || 1,
|
||||
originX,
|
||||
originY,
|
||||
rotation: +props.rotation / 2 || +props.rotate / 2 || 0,
|
||||
scale: isNaN(scale) ? 1 : scale,
|
||||
originX: +originX || 0,
|
||||
originY: +originY || 0,
|
||||
x: +props.x || 0,
|
||||
y: +props.y || 0
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче