mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-20 14:05:09 +00:00
refactor LinearGradient and RadialGradient
refactor LinearGradient and RadialGradient and add fillOpacity support for LinearGradient and RadialGradient
This commit is contained in:
@@ -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
|
const icon = <Svg
|
||||||
height="20"
|
height="20"
|
||||||
width="20"
|
width="20"
|
||||||
@@ -160,7 +186,7 @@ const icon = <Svg
|
|||||||
</Svg>;
|
</Svg>;
|
||||||
|
|
||||||
|
|
||||||
const samples = [LinearGradientHorizontal, LinearGradientVertical, LinearGradientPercent, RadialGradientExample, RadialGradientPercent, RadialGradientPart];
|
const samples = [LinearGradientHorizontal, LinearGradientVertical, LinearGradientPercent, RadialGradientExample, RadialGradientPercent, RadialGradientPart, FillGradientWithOpacity];
|
||||||
|
|
||||||
export {
|
export {
|
||||||
icon,
|
icon,
|
||||||
|
|||||||
59
elements/Gradient.js
Normal file
59
elements/Gradient.js
Normal 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;
|
||||||
|
|
||||||
@@ -5,16 +5,12 @@ import React, {
|
|||||||
Children
|
Children
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
let {
|
let {
|
||||||
Group,
|
|
||||||
LinearGradient: ARTLinearGradient
|
LinearGradient: ARTLinearGradient
|
||||||
} = ART;
|
} = ART;
|
||||||
import {set, remove} from '../lib/fillFilter';
|
import stopsOpacity from '../lib/stopsOpacity';
|
||||||
import percentFactory from '../lib/percentFactory';
|
import Gradient from './Gradient';
|
||||||
import percentToFloat from '../lib/percentToFloat';
|
|
||||||
import Stop from './Stop';
|
|
||||||
import Color from 'color';
|
|
||||||
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
||||||
class LinearGradient extends Component{
|
class LinearGradient extends Gradient{
|
||||||
static displayName = 'LinearGradient';
|
static displayName = 'LinearGradient';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
x1: propType,
|
x1: propType,
|
||||||
@@ -24,22 +20,6 @@ class LinearGradient extends Component{
|
|||||||
id: PropTypes.string
|
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() {
|
render() {
|
||||||
let {
|
let {
|
||||||
x1,
|
x1,
|
||||||
@@ -47,34 +27,22 @@ class LinearGradient extends Component{
|
|||||||
x2,
|
x2,
|
||||||
y2
|
y2
|
||||||
} = this.props;
|
} = this.props;
|
||||||
let stops = {};
|
let gradientProps = [x1, y1, x2, y2];
|
||||||
Children.forEach(this.props.children, child => {
|
return super.render(
|
||||||
if (child.type === Stop && child.props.stopColor && child.props.offset) {
|
gradientProps,
|
||||||
// convert percent to float.
|
function (factories, stops, boundingBox, opacity) {
|
||||||
let offset = percentToFloat(child.props.offset);
|
return new ARTLinearGradient(
|
||||||
|
stopsOpacity(stops, opacity),
|
||||||
// add stop
|
factories[0](boundingBox.width),
|
||||||
stops[offset] = Color(child.props.stopColor).alpha(+child.props.stopOpacity).rgbaString();
|
factories[1](boundingBox.height),
|
||||||
|
factories[2](boundingBox.width),
|
||||||
let factories = percentFactory(x1, y1, x2, y2);
|
factories[3](boundingBox.height)
|
||||||
if (factories) {
|
);
|
||||||
set(this.id, function (boundingBox) {
|
},
|
||||||
return new ARTLinearGradient(
|
function (stops, opacity) {
|
||||||
stops,
|
return new ARTLinearGradient(stopsOpacity(stops, opacity), ...gradientProps);
|
||||||
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));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.warn(`'LinearGradient' can only receive 'Stop' elements as children`);
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
return <Group />;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,17 +5,12 @@ import React, {
|
|||||||
Children
|
Children
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
let {
|
let {
|
||||||
Group,
|
|
||||||
RadialGradient: ARTRadialGradient
|
RadialGradient: ARTRadialGradient
|
||||||
} = ART;
|
} = ART;
|
||||||
import {set, remove} from '../lib/fillFilter';
|
import stopsOpacity from '../lib/stopsOpacity';
|
||||||
import percentFactory from '../lib/percentFactory';
|
import Gradient from './Gradient';
|
||||||
import percentToFloat from '../lib/percentToFloat';
|
|
||||||
import Stop from './Stop';
|
|
||||||
import Color from 'color';
|
|
||||||
let percentReg = /^(\-?\d+(?:\.\d+)?)(%?)$/;
|
|
||||||
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
||||||
class RadialGradient extends Component{
|
class RadialGradient extends Gradient{
|
||||||
static displayName = 'RadialGradient';
|
static displayName = 'RadialGradient';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
fx: propType,
|
fx: propType,
|
||||||
@@ -28,22 +23,6 @@ class RadialGradient extends Component{
|
|||||||
id: PropTypes.string
|
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() {
|
render() {
|
||||||
let {
|
let {
|
||||||
fx,
|
fx,
|
||||||
@@ -55,42 +34,28 @@ class RadialGradient extends Component{
|
|||||||
r
|
r
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
// TODO: render differently from svg in html
|
|
||||||
if (r) {
|
if (r) {
|
||||||
rx = ry = +r;
|
rx = ry = +r;
|
||||||
}
|
}
|
||||||
let stops = {};
|
let gradientProps = [fx, fy, rx, ry, cx, cy];
|
||||||
Children.forEach(this.props.children, child => {
|
return super.render(
|
||||||
if (child.type === Stop && child.props.stopColor && child.props.offset) {
|
gradientProps,
|
||||||
// convert percent to float.
|
function (factories, stops, boundingBox, opacity) {
|
||||||
let offset = percentToFloat(child.props.offset);
|
let {x1,y1,width, height} = boundingBox;
|
||||||
|
return new ARTRadialGradient(
|
||||||
// add stop
|
stopsOpacity(stops, opacity),
|
||||||
stops[offset] = Color(child.props.stopColor).alpha(+child.props.stopOpacity).rgbaString();
|
x1 + factories[0](width),
|
||||||
|
y1 + factories[1](height),
|
||||||
|
factories[2](width),
|
||||||
let factories = percentFactory(fx, fy, rx, ry, cx, cy);
|
factories[3](height),
|
||||||
if (factories) {
|
x1 + factories[4](width),
|
||||||
set(this.id, function (boundingBox) {
|
y1 + factories[5](height)
|
||||||
let {x1,y1,width, height} = boundingBox;
|
);
|
||||||
return new ARTRadialGradient(
|
},
|
||||||
stops,
|
function (stops, opacity) {
|
||||||
x1 + factories[0](width),
|
return new ARTRadialGradient(stopsOpacity(stops, opacity), ...gradientProps);
|
||||||
y1 + factories[1](height),
|
|
||||||
factories[2](width),
|
|
||||||
factories[3](height),
|
|
||||||
x1 + factories[4](width),
|
|
||||||
y1 + factories[5](height)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
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 />;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
let {
|
let {
|
||||||
LinearGradient,
|
LinearGradient,
|
||||||
RadialGradient
|
RadialGradient
|
||||||
} = ART;
|
} = ART;
|
||||||
|
|
||||||
let fillPatterns = {};
|
let fillPatterns = {};
|
||||||
let fillReg = /^url\(#(\w+?)\)$/;
|
let fillReg = /^url\(#(\w+?)\)$/;
|
||||||
@@ -23,25 +23,34 @@ export default function (props) {
|
|||||||
return fill;
|
return fill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fillOpacity = +props.fillOpacity;
|
||||||
|
if (isNaN(fillOpacity)) {
|
||||||
|
fillOpacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// 尝试匹配 fill="url(#pattern)"
|
// 尝试匹配 fill="url(#pattern)"
|
||||||
let matched = fill.match(fillReg);
|
let matched = fill.match(fillReg);
|
||||||
if (matched) {
|
if (matched) {
|
||||||
let patternName = `${matched[1]}:${props.svgId}`;
|
let patternName = `${matched[1]}:${props.svgId}`;
|
||||||
let pattern = fillPatterns[patternName];
|
let pattern = fillPatterns[patternName];
|
||||||
if (pattern) {
|
if (pattern) {
|
||||||
if (typeof pattern === 'function') {
|
if (pattern.length === 2) {
|
||||||
let dimensions = this.getBoundingBox();
|
let dimensions = this.getBoundingBox();
|
||||||
return pattern(dimensions);
|
return pattern(dimensions, fillOpacity);
|
||||||
} else {
|
} else {
|
||||||
return pattern;
|
return pattern(fillOpacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
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) {
|
function set(id, pattern) {
|
||||||
|
|||||||
7
lib/stopsOpacity.js
Normal file
7
lib/stopsOpacity.js
Normal 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;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color": "^0.11.1",
|
"color": "^0.11.1",
|
||||||
"svgpath": "^2.1.5"
|
"svgpath": "^2.1.5",
|
||||||
|
"lodash": "^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user