refactor LinearGradient and RadialGradient

refactor LinearGradient and RadialGradient
and add fillOpacity support for LinearGradient and RadialGradient
This commit is contained in:
Horcrux
2016-01-28 18:12:59 +08:00
parent bda7b1fa5c
commit 3c56ad3dd7
7 changed files with 149 additions and 114 deletions

View File

@@ -146,6 +146,32 @@ class RadialGradientPercent extends Component{
}
}
class FillGradientWithOpacity extends Component{
static title = 'Fill a radial gradient with fillOpacity prop';
render() {
return <Svg
height="150"
width="300"
>
<Defs>
<RadialGradient id="grad" cx="50%" cy="50%" rx="50%" ry="50%" fx="50%" fy="50%">
<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)" fillOpacity="0.2" />
</Svg>;
}
}
const icon = <Svg
height="20"
width="20"
@@ -160,7 +186,7 @@ const icon = <Svg
</Svg>;
const samples = [LinearGradientHorizontal, LinearGradientVertical, LinearGradientPercent, RadialGradientExample, RadialGradientPercent, RadialGradientPart];
const samples = [LinearGradientHorizontal, LinearGradientVertical, LinearGradientPercent, RadialGradientExample, RadialGradientPercent, RadialGradientPart, FillGradientWithOpacity];
export {
icon,

59
elements/Gradient.js Normal file
View File

@@ -0,0 +1,59 @@
import React, {
Component,
PropTypes,
ART,
Children
} from 'react-native';
let {
Group
} = ART;
import {set, remove} from '../lib/fillFilter';
import percentFactory from '../lib/percentFactory';
import percentToFloat from '../lib/percentToFloat';
import Stop from './Stop';
import Color from 'color';
class RadialGradient extends Component{
static displayName = 'Gradient';
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(distanceProps, fromPercent, fromNumber) {
let stops = {};
Children.forEach(this.props.children, child => {
if (child.type === Stop && child.props.stopColor && child.props.offset) {
// convert percent to float.
let offset = percentToFloat(child.props.offset);
// add stop
stops[offset] = Color(child.props.stopColor).alpha(+child.props.stopOpacity);
let factories = percentFactory(...distanceProps);
if (factories) {
set(this.id, fromPercent.bind(null, factories, stops));
} else {
set(this.id, fromNumber.bind(null, stops));
}
} else {
console.warn(`'RadialGradient' can only receive 'Stop' elements as children`);
}
});
return <Group />;
}
}
export default RadialGradient;

View File

@@ -5,16 +5,12 @@ import React, {
Children
} from 'react-native';
let {
Group,
LinearGradient: ARTLinearGradient
} = ART;
import {set, remove} from '../lib/fillFilter';
import percentFactory from '../lib/percentFactory';
import percentToFloat from '../lib/percentToFloat';
import Stop from './Stop';
import Color from 'color';
import stopsOpacity from '../lib/stopsOpacity';
import Gradient from './Gradient';
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
class LinearGradient extends Component{
class LinearGradient extends Gradient{
static displayName = 'LinearGradient';
static propTypes = {
x1: propType,
@@ -24,22 +20,6 @@ class LinearGradient extends Component{
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,
@@ -47,34 +27,22 @@ class LinearGradient extends Component{
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 offset = percentToFloat(child.props.offset);
// add stop
stops[offset] = Color(child.props.stopColor).alpha(+child.props.stopOpacity).rgbaString();
let factories = percentFactory(x1, y1, x2, y2);
if (factories) {
set(this.id, function (boundingBox) {
let gradientProps = [x1, y1, x2, y2];
return super.render(
gradientProps,
function (factories, stops, boundingBox, opacity) {
return new ARTLinearGradient(
stops,
stopsOpacity(stops, opacity),
factories[0](boundingBox.width),
factories[1](boundingBox.height),
factories[2](boundingBox.width),
factories[3](boundingBox.height)
);
});
} else {
set(this.id, new ARTLinearGradient(stops, x1, y1, x2, y2));
},
function (stops, opacity) {
return new ARTLinearGradient(stopsOpacity(stops, opacity), ...gradientProps);
}
} else {
console.warn(`'LinearGradient' can only receive 'Stop' elements as children`);
}
});
return <Group />;
);
}
}

View File

@@ -5,17 +5,12 @@ import React, {
Children
} from 'react-native';
let {
Group,
RadialGradient: ARTRadialGradient
} = ART;
import {set, remove} from '../lib/fillFilter';
import percentFactory from '../lib/percentFactory';
import percentToFloat from '../lib/percentToFloat';
import Stop from './Stop';
import Color from 'color';
let percentReg = /^(\-?\d+(?:\.\d+)?)(%?)$/;
import stopsOpacity from '../lib/stopsOpacity';
import Gradient from './Gradient';
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
class RadialGradient extends Component{
class RadialGradient extends Gradient{
static displayName = 'RadialGradient';
static propTypes = {
fx: propType,
@@ -28,22 +23,6 @@ class RadialGradient extends Component{
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,
@@ -55,26 +34,16 @@ class RadialGradient extends Component{
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 offset = percentToFloat(child.props.offset);
// add stop
stops[offset] = Color(child.props.stopColor).alpha(+child.props.stopOpacity).rgbaString();
let factories = percentFactory(fx, fy, rx, ry, cx, cy);
if (factories) {
set(this.id, function (boundingBox) {
let gradientProps = [fx, fy, rx, ry, cx, cy];
return super.render(
gradientProps,
function (factories, stops, boundingBox, opacity) {
let {x1,y1,width, height} = boundingBox;
return new ARTRadialGradient(
stops,
stopsOpacity(stops, opacity),
x1 + factories[0](width),
y1 + factories[1](height),
factories[2](width),
@@ -82,15 +51,11 @@ class RadialGradient extends Component{
x1 + factories[4](width),
y1 + factories[5](height)
);
});
} else {
set(this.id, new ARTRadialGradient(stops, fx, fy, rx, ry, cx, cy));
},
function (stops, opacity) {
return new ARTRadialGradient(stopsOpacity(stops, opacity), ...gradientProps);
}
} else {
console.warn(`'RadialGradient' can only receive 'Stop' elements as children`);
}
});
return <Group />;
);
}
}

View File

@@ -6,7 +6,7 @@ import {
let {
LinearGradient,
RadialGradient
} = ART;
} = ART;
let fillPatterns = {};
let fillReg = /^url\(#(\w+?)\)$/;
@@ -23,25 +23,34 @@ export default function (props) {
return fill;
}
let fillOpacity = +props.fillOpacity;
if (isNaN(fillOpacity)) {
fillOpacity = 1;
}
// 尝试匹配 fill="url(#pattern)"
let matched = fill.match(fillReg);
if (matched) {
let patternName = `${matched[1]}:${props.svgId}`;
let pattern = fillPatterns[patternName];
if (pattern) {
if (typeof pattern === 'function') {
if (pattern.length === 2) {
let dimensions = this.getBoundingBox();
return pattern(dimensions);
return pattern(dimensions, fillOpacity);
} else {
return pattern;
return pattern(fillOpacity);
}
}
return null;
}
return rgba(props.fill, fillOpacity);
} else if (props.fill === undefined) {
return rgba('#000', fillOpacity);
} else {
return null;
}
// TODO: support fillOpacity for other fill pattern
return rgba(props.fill === undefined ? '#000' : props.fill, props.fillOpacity);
}
function set(id, pattern) {

7
lib/stopsOpacity.js Normal file
View File

@@ -0,0 +1,7 @@
import _ from 'lodash';
export default function (stops, opacity) {
return _.reduce(stops, (ret, color, key) => {
ret[key] = color.alpha(opacity).rgbaString();
return ret;
}, {});
}

View File

@@ -19,6 +19,7 @@
],
"dependencies": {
"color": "^0.11.1",
"svgpath": "^2.1.5"
"svgpath": "^2.1.5",
"lodash": "^4.0.0"
}
}