Implement support for animating gradient stops, fixes #884

```jsx
import * as React from 'react';
import { View, StyleSheet, Animated } from 'react-native';
import { Svg, Defs, LinearGradient, Stop, Rect } from 'react-native-svg';

const AnimatedStop = Animated.createAnimatedComponent(Stop);

export default class App extends React.Component {
  state = {
    anim: new Animated.Value(0),
  };
  componentDidMount = () => {
    Animated.timing(this.state.anim, {
      duration: 3000,
      toValue: 1,
    }).start();
  };
  render() {
    return (
      <View style={styles.container}>
        <Svg width="100%" height="100%">
          <Defs>
            <LinearGradient id="grad" x1="0" y1="50" x2="100" y2="50">
              <Stop offset="0%" stopColor="#888" stopOpacity="1" />
              <AnimatedStop
                offset={this.state.anim}
                stopColor="#888"
                stopOpacity="0.1"
              />
              <Stop offset="100%" stopColor="#888" stopOpacity="1" />
            </LinearGradient>
          </Defs>
          <Rect
            x="10"
            y="20"
            rx="16"
            ry="16"
            width="96"
            height="32"
            fill="url(#grad)"
          />
        </Svg>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#ecf0f1',
  },
});
```
This commit is contained in:
Mikael Sand 2019-01-23 17:09:35 +02:00
Родитель 2c04da063b
Коммит eb3e67a257
12 изменённых файлов: 37 добавлений и 7 удалений

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

@ -23,7 +23,7 @@ export default class LinearGradient extends Shape {
y1={y1}
x2={x2}
y2={y2}
{...extractGradient(props)}
{...extractGradient(props, this)}
/>
);
}

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

@ -26,7 +26,7 @@ export default class RadialGradient extends Shape {
ry={ry || r}
cx={cx}
cy={cy}
{...extractGradient(props)}
{...extractGradient(props, this)}
/>
);
}

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

@ -7,6 +7,12 @@ export default class Stop extends Component {
stopColor: "#000",
stopOpacity: 1,
};
setNativeProps = () => {
const { parent } = this.props;
if (parent) {
parent.forceUpdate();
}
};
render() {
return null;

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

@ -12,6 +12,7 @@
- (void)parseReference
{
self.dirty = false;
[self.svgView defineClipPath:self clipPathName:self.name];
}

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

@ -18,6 +18,7 @@
- (void)parseReference
{
self.dirty = false;
[self traverseSubviews:^(RNSVGNode *node) {
if ([node isKindOfClass:[RNSVGNode class]]) {
[node parseReference];

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

@ -197,6 +197,7 @@
- (void)parseReference
{
self.dirty = false;
if (self.name) {
typeof(self) __weak weakSelf = self;
[self.svgView defineTemplate:weakSelf templateName:self.name];

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

@ -85,6 +85,7 @@
- (void)parseReference
{
self.dirty = false;
NSArray<RNSVGLength *> *points = @[self.x1, self.y1, self.x2, self.y2];
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
[painter setUnits:self.gradientUnits];

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

@ -19,6 +19,7 @@
- (void)parseReference
{
self.dirty = false;
[self.svgView defineMask:self maskName:self.name];
}

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

@ -19,6 +19,7 @@
- (void)parseReference
{
self.dirty = false;
NSArray<RNSVGLength *> *points = @[self.x, self.y, self.patternwidth, self.patternheight];
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
[painter setUnits:self.patternUnits];

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

@ -102,6 +102,7 @@
- (void)parseReference
{
self.dirty = false;
NSArray<RNSVGLength *> *points = @[self.fx, self.fy, self.rx, self.ry, self.cx, self.cy];
RNSVGPainter *painter = [[RNSVGPainter alloc] initWithPointsArray:points];
[painter setUnits:self.gradientUnits];

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

@ -531,6 +531,7 @@ CGFloat const RNSVG_DEFAULT_FONT_SIZE = 12;
- (void)parseReference
{
self.dirty = false;
if (self.name) {
typeof(self) __weak weakSelf = self;
[self.svgView defineTemplate:weakSelf templateName:self.name];

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

@ -1,4 +1,4 @@
import { Children } from "react";
import React, { Children } from "react";
import Color from "color";
import extractOpacity from "./extractOpacity";
@ -11,6 +11,12 @@ function percentToFloat(percent) {
if (typeof percent === "number") {
return percent;
}
if (
typeof percent === "object" &&
typeof percent.__getAnimatedValue === "function"
) {
return percent.__getAnimatedValue();
}
const matched = percent.match(percentReg);
if (!matched) {
console.warn(
@ -24,20 +30,27 @@ function percentToFloat(percent) {
const offsetComparator = (object, other) => object[0] - other[0];
export default function extractGradient(props) {
export default function extractGradient(props, parent) {
const { id, children, gradientTransform, transform, gradientUnits } = props;
if (!id) {
return null;
}
const stops = [];
const childArray = Children.toArray(children);
const childArray = React.Children.map(children, child =>
React.cloneElement(child, {
parent,
}),
);
const l = childArray.length;
for (let i = 0; i < l; i++) {
const { props: { offset, stopColor, stopOpacity } } = childArray[i];
const offsetNumber = percentToFloat(offset);
if (stopColor && !isNaN(offsetNumber)) {
const color = Color(stopColor).alpha(extractOpacity(stopOpacity)).rgb().array();
const color = Color(stopColor)
.alpha(extractOpacity(stopOpacity))
.rgb()
.array();
const r = color[0] / 255;
const g = color[1] / 255;
const b = color[2] / 255;
@ -58,8 +71,11 @@ export default function extractGradient(props) {
return {
name: id,
children: childArray,
gradient: colors.concat(offsets),
gradientUnits: units[gradientUnits] || 0,
gradientTransform: extractTransform(gradientTransform || transform || props),
gradientTransform: extractTransform(
gradientTransform || transform || props,
),
};
}