Experiment to allow animating fillOpacity of Rect using native driver.

import React, { Component } from 'react';
import { StyleSheet, View, Dimensions, Animated } from 'react-native';
import { Svg, Rect } from 'react-native-svg';

const { width, height } = Dimensions.get('window');
const AnimatedRect = Animated.createAnimatedComponent(Rect);

export default class App extends Component {
  state = {
    anim: new Animated.Value(0),
  };

  componentDidMount() {
    Animated.timing(this.state.anim, {
      toValue: 1,
      duration: 3000,
      useNativeDriver: true,
    }).start();
  }

  render() {
    const { anim } = this.state;
    return (
      <View style={styles.container}>
        <Svg width={width} height={height} viewBox="0 0 100 100">
          <AnimatedRect
            x="5"
            y="5"
            width="90"
            height="90"
            fill="green"
            fillOpacity={anim}
          />
        </Svg>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#ecf0f1',
  },
});
This commit is contained in:
Mikael Sand
2018-02-24 23:18:50 +02:00
parent f3ea54cd26
commit fbd6591251
11 changed files with 95 additions and 44 deletions

View File

@@ -40,14 +40,14 @@ class RectShadowNode extends RenderableShadowNode {
markUpdated(); markUpdated();
} }
@ReactProp(name = "width") @ReactProp(name = "rectwidth")
public void setWidth(String width) { public void setWidth(String width) {
mW = width; mW = width;
markUpdated(); markUpdated();
} }
@ReactProp(name = "height") @ReactProp(name = "rectheight")
public void setHeight(String height) { public void setHeight(String height) {
mH = height; mH = height;
markUpdated(); markUpdated();

View File

@@ -9,18 +9,19 @@
package com.horcrux.svg; package com.horcrux.svg;
import android.view.View; import android.util.SparseArray;
import com.facebook.react.uimanager.LayoutShadowNode; import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewManager; import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.annotations.ReactProp;
/** /**
* ViewManager for all shadowed RNSVG views: Group, Path and Text. Since these never get rendered * ViewManager for all shadowed RNSVG views: Group, Path and Text. Since these never get rendered
* into native views and don't need any logic (all the logic is in {@link SvgView}), this * into native views and don't need any logic (all the logic is in {@link SvgView}), this
* "stubbed" ViewManager is used for all of them. * "stubbed" ViewManager is used for all of them.
*/ */
class RenderableViewManager extends ViewManager<View, LayoutShadowNode> { class RenderableViewManager extends ViewGroupManager<RenderableView> {
/* package */ private static final String CLASS_GROUP = "RNSVGGroup"; /* package */ private static final String CLASS_GROUP = "RNSVGGroup";
/* package */ private static final String CLASS_PATH = "RNSVGPath"; /* package */ private static final String CLASS_PATH = "RNSVGPath";
@@ -195,17 +196,43 @@ class RenderableViewManager extends ViewManager<View, LayoutShadowNode> {
} }
} }
@Override @ReactProp(name = "fillOpacity")
protected View createViewInstance(ThemedReactContext reactContext) { public void setFillOpacity(RenderableView node, float opacity) {
throw new IllegalStateException("SVG elements does not map into a native view"); RenderableShadowNode shadow = ((RenderableShadowNode)mTagToShadowNode.get(node.getId()));
shadow.setFillOpacity(opacity);
SvgViewShadowNode view = shadow.getSvgShadowNode();
if (view != null ) view.queueDraw();
} }
@Override @Override
public void updateExtraData(View root, Object extraData) { protected RenderableView createViewInstance(ThemedReactContext reactContext) {
throw new IllegalStateException("SVG elements does not map into a native view"); return new RenderableView(reactContext);
} }
@Override @Override
public void onDropViewInstance(View view) { public void updateExtraData(RenderableView root, Object extraData) {
throw new IllegalStateException("SVG elements does not map into a native view");
}
private static final SparseArray<VirtualNode> mTagToShadowNode = new SparseArray<>();
private static final SparseArray<RenderableView> mTagToSvgView = new SparseArray<>();
@Override
public void onDropViewInstance(RenderableView view) {
int tag = view.getId();
mTagToShadowNode.remove(tag);
mTagToSvgView.remove(tag);
}
public static void setView(RenderableView renderableView) {
mTagToSvgView.put(renderableView.getId(), renderableView);
}
static void setShadowNode(VirtualNode virtualNode) {
mTagToShadowNode.put(virtualNode.getReactTag(), virtualNode);
}
static VirtualNode getShadowNodeByTag(int id) {
return mTagToShadowNode.get(id);
} }
} }

View File

@@ -67,6 +67,12 @@ public class SvgView extends ViewGroup {
mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher(); mEventDispatcher = reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();
} }
public void addView(View child, int index, LayoutParams params) {
if (!(child instanceof RenderableView)) {
super.addView(child, index, params);
}
}
/** /**
* Sets the {@link TextureView.SurfaceTextureListener} used to listen to surface * Sets the {@link TextureView.SurfaceTextureListener} used to listen to surface
* texture events. * texture events.
@@ -111,9 +117,7 @@ public class SvgView extends ViewGroup {
ReactShadowNodeImpl node = getShadowNode(); ReactShadowNodeImpl node = getShadowNode();
for (int i = 0; i < this.getChildCount(); i++) { for (int i = 0; i < this.getChildCount(); i++) {
View child = this.getChildAt(i); View child = this.getChildAt(i);
if (child instanceof TextureView) { if (child instanceof ReactViewGroup) {
child.layout(l, t, r , b);
} else if (child instanceof ReactViewGroup) {
int id = child.getId(); int id = child.getId();
for (int j = 0; j < node.getChildCount(); j++) { for (int j = 0; j < node.getChildCount(); j++) {
ReactShadowNodeImpl nodeChild = node.getChildAt(j); ReactShadowNodeImpl nodeChild = node.getChildAt(j);
@@ -129,6 +133,8 @@ public class SvgView extends ViewGroup {
child.layout(Math.round(x), Math.round(y), Math.round(nr), Math.round(nb)); child.layout(Math.round(x), Math.round(y), Math.round(nr), Math.round(nb));
break; break;
} }
} else {
child.layout(l, t, r , b);
} }
} }
} }

View File

@@ -21,6 +21,7 @@ import android.graphics.RectF;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.util.Base64; import android.util.Base64;
import android.view.Choreographer;
import android.view.Surface; import android.view.Surface;
import android.view.TextureView; import android.view.TextureView;
@@ -43,7 +44,7 @@ import javax.annotation.Nullable;
* Shadow node for RNSVG virtual tree root - RNSVGSvgView * Shadow node for RNSVG virtual tree root - RNSVGSvgView
*/ */
public class SvgViewShadowNode extends LayoutShadowNode public class SvgViewShadowNode extends LayoutShadowNode
implements TextureView.SurfaceTextureListener { implements TextureView.SurfaceTextureListener, Choreographer.FrameCallback {
private @Nullable Surface mSurface; private @Nullable Surface mSurface;
@@ -337,4 +338,21 @@ public class SvgViewShadowNode extends LayoutShadowNode
@Override @Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {} public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
private boolean queued = false;
@Override
public void doFrame(long frameTimeNanos) {
if (queued) {
queued = false;
drawOutput();
}
}
void queueDraw() {
if (!queued) {
queued = true;
Choreographer.getInstance().postFrameCallback(this);
}
}
} }

View File

@@ -38,6 +38,12 @@ abstract class VirtualNode extends LayoutShadowNode {
static final float MIN_OPACITY_FOR_DRAW = 0.01f; static final float MIN_OPACITY_FOR_DRAW = 0.01f;
@Override
public void setReactTag(int reactTag) {
super.setReactTag(reactTag);
RenderableViewManager.setShadowNode(this);
}
private static final float[] sRawMatrix = new float[]{ private static final float[] sRawMatrix = new float[]{
1, 0, 0, 1, 0, 0,
0, 1, 0, 0, 1, 0,
@@ -72,18 +78,17 @@ abstract class VirtualNode extends LayoutShadowNode {
Path mClipRegionPath; Path mClipRegionPath;
VirtualNode() { VirtualNode() {
setIsLayoutOnly(true);
mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density; mScale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
} }
@Override @Override
public boolean isVirtual() { public boolean isVirtual() {
return true; return false;
} }
@Override @Override
public boolean isVirtualAnchor() { public boolean isVirtualAnchor() {
return true; return false;
} }
@Override @Override

View File

@@ -44,8 +44,8 @@ export default class extends Shape {
}, this)} }, this)}
x={props.x.toString()} x={props.x.toString()}
y={props.y.toString()} y={props.y.toString()}
width={props.width.toString()} rectwidth={props.width.toString()}
height={props.height.toString()} rectheight={props.height.toString()}
rx={props.rx.toString()} rx={props.rx.toString()}
ry={props.ry.toString()} ry={props.ry.toString()}
/>; />;

View File

@@ -14,8 +14,8 @@
@property (nonatomic, strong) NSString* x; @property (nonatomic, strong) NSString* x;
@property (nonatomic, strong) NSString* y; @property (nonatomic, strong) NSString* y;
@property (nonatomic, strong) NSString* width; @property (nonatomic, strong) NSString* rectwidth;
@property (nonatomic, strong) NSString* height; @property (nonatomic, strong) NSString* rectheight;
@property (nonatomic, strong) NSString* rx; @property (nonatomic, strong) NSString* rx;
@property (nonatomic, strong) NSString* ry; @property (nonatomic, strong) NSString* ry;

View File

@@ -29,22 +29,22 @@
_y = y; _y = y;
} }
- (void)setWidth:(NSString *)width - (void)setWidth:(NSString *)rectwidth
{ {
if (width == _width) { if (rectwidth == _rectwidth) {
return; return;
} }
[self invalidate]; [self invalidate];
_width = width; _rectwidth = rectwidth;
} }
- (void)setHeight:(NSString *)height - (void)setHeight:(NSString *)rectheight
{ {
if (height == _height) { if (rectheight == _rectheight) {
return; return;
} }
[self invalidate]; [self invalidate];
_height = height; _rectheight = rectheight;
} }
- (void)setRx:(NSString *)rx - (void)setRx:(NSString *)rx
@@ -70,8 +70,8 @@
CGMutablePathRef path = CGPathCreateMutable(); CGMutablePathRef path = CGPathCreateMutable();
CGFloat x = [self relativeOnWidth:self.x]; CGFloat x = [self relativeOnWidth:self.x];
CGFloat y = [self relativeOnHeight:self.y]; CGFloat y = [self relativeOnHeight:self.y];
CGFloat width = [self relativeOnWidth:self.width]; CGFloat width = [self relativeOnWidth:self.rectwidth];
CGFloat height = [self relativeOnHeight:self.height]; CGFloat height = [self relativeOnHeight:self.rectheight];
CGFloat rx = [self relativeOnWidth:self.rx]; CGFloat rx = [self relativeOnWidth:self.rx];
CGFloat ry = [self relativeOnHeight:self.ry]; CGFloat ry = [self relativeOnHeight:self.ry];

View File

@@ -24,11 +24,6 @@ RCT_EXPORT_MODULE()
return [self node]; return [self node];
} }
- (RCTShadowView *)shadowView
{
return nil;
}
RCT_EXPORT_VIEW_PROPERTY(name, NSString) RCT_EXPORT_VIEW_PROPERTY(name, NSString)
RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat) RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(matrix, CGAffineTransform) RCT_EXPORT_VIEW_PROPERTY(matrix, CGAffineTransform)

View File

@@ -22,8 +22,8 @@ RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(x, NSString) RCT_EXPORT_VIEW_PROPERTY(x, NSString)
RCT_EXPORT_VIEW_PROPERTY(y, NSString) RCT_EXPORT_VIEW_PROPERTY(y, NSString)
RCT_EXPORT_VIEW_PROPERTY(width, NSString) RCT_EXPORT_VIEW_PROPERTY(rectwidth, NSString)
RCT_EXPORT_VIEW_PROPERTY(height, NSString) RCT_EXPORT_VIEW_PROPERTY(rectheight, NSString)
RCT_EXPORT_VIEW_PROPERTY(rx, NSString) RCT_EXPORT_VIEW_PROPERTY(rx, NSString)
RCT_EXPORT_VIEW_PROPERTY(ry, NSString) RCT_EXPORT_VIEW_PROPERTY(ry, NSString)

View File

@@ -220,8 +220,8 @@ const RectAttributes = {
...RenderableAttributes, ...RenderableAttributes,
x: true, x: true,
y: true, y: true,
width: true, rectwidth: true,
height: true, rectheight: true,
rx: true, rx: true,
ry: true, ry: true,
}; };