From 3c56ad3dd79d6f43e1046eabe2d3e02aca7c380d Mon Sep 17 00:00:00 2001 From: Horcrux Date: Thu, 28 Jan 2016 18:12:59 +0800 Subject: [PATCH] refactor LinearGradient and RadialGradient refactor LinearGradient and RadialGradient and add fillOpacity support for LinearGradient and RadialGradient --- Example/examples/Gradients.js | 28 ++++++++++++- elements/Gradient.js | 59 +++++++++++++++++++++++++++ elements/LinearGradient.js | 68 ++++++++----------------------- elements/RadialGradient.js | 77 ++++++++++------------------------- lib/fillFilter.js | 21 +++++++--- lib/stopsOpacity.js | 7 ++++ package.json | 3 +- 7 files changed, 149 insertions(+), 114 deletions(-) create mode 100644 elements/Gradient.js create mode 100644 lib/stopsOpacity.js diff --git a/Example/examples/Gradients.js b/Example/examples/Gradients.js index 2fff912f..db7de123 100644 --- a/Example/examples/Gradients.js +++ b/Example/examples/Gradients.js @@ -146,6 +146,32 @@ class RadialGradientPercent extends Component{ } } +class FillGradientWithOpacity extends Component{ + static title = 'Fill a radial gradient with fillOpacity prop'; + render() { + return + + + + + + + + ; + } +} + const icon = ; -const samples = [LinearGradientHorizontal, LinearGradientVertical, LinearGradientPercent, RadialGradientExample, RadialGradientPercent, RadialGradientPart]; +const samples = [LinearGradientHorizontal, LinearGradientVertical, LinearGradientPercent, RadialGradientExample, RadialGradientPercent, RadialGradientPart, FillGradientWithOpacity]; export { icon, diff --git a/elements/Gradient.js b/elements/Gradient.js new file mode 100644 index 00000000..f2fbacb2 --- /dev/null +++ b/elements/Gradient.js @@ -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 ; + } +} + +export default RadialGradient; + diff --git a/elements/LinearGradient.js b/elements/LinearGradient.js index 87798d62..aed2b8c9 100644 --- a/elements/LinearGradient.js +++ b/elements/LinearGradient.js @@ -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) { - return new ARTLinearGradient( - stops, - 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`); + let gradientProps = [x1, y1, x2, y2]; + return super.render( + gradientProps, + function (factories, stops, boundingBox, opacity) { + return new ARTLinearGradient( + stopsOpacity(stops, opacity), + factories[0](boundingBox.width), + factories[1](boundingBox.height), + factories[2](boundingBox.width), + factories[3](boundingBox.height) + ); + }, + function (stops, opacity) { + return new ARTLinearGradient(stopsOpacity(stops, opacity), ...gradientProps); } - }); - return ; + ); } } diff --git a/elements/RadialGradient.js b/elements/RadialGradient.js index 3538f89e..24fcf87f 100644 --- a/elements/RadialGradient.js +++ b/elements/RadialGradient.js @@ -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,42 +34,28 @@ 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 {x1,y1,width, height} = boundingBox; - return new ARTRadialGradient( - stops, - x1 + factories[0](width), - 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`); + 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( + stopsOpacity(stops, opacity), + x1 + factories[0](width), + y1 + factories[1](height), + factories[2](width), + factories[3](height), + x1 + factories[4](width), + y1 + factories[5](height) + ); + }, + function (stops, opacity) { + return new ARTRadialGradient(stopsOpacity(stops, opacity), ...gradientProps); } - }); - return ; + ); } } diff --git a/lib/fillFilter.js b/lib/fillFilter.js index 94ef7490..648827ce 100644 --- a/lib/fillFilter.js +++ b/lib/fillFilter.js @@ -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) { diff --git a/lib/stopsOpacity.js b/lib/stopsOpacity.js new file mode 100644 index 00000000..ebf9bcd0 --- /dev/null +++ b/lib/stopsOpacity.js @@ -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; + }, {}); +} diff --git a/package.json b/package.json index 64fb2ee4..a719f255 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ ], "dependencies": { "color": "^0.11.1", - "svgpath": "^2.1.5" + "svgpath": "^2.1.5", + "lodash": "^4.0.0" } }