Implement support for Use element inside ClipPath

```jsx
import React from 'react';
import { View } from 'react-native';
import Svg, {
  Defs,
  ClipPath,
  Path,
  Rect,
  G,
  Text,
  Polygon,
  Use,
} from 'react-native-svg';

const SvgComponent = props => (
  <Svg width="100%" height="30%" viewBox="0 0 140 110" {...props}>
    <Defs>
      <Path
        id="prefix__a"
        d="M25 10a20 20 0 1 0 0 40 20 20 1 1 0 0-40m20 0a20 20 0 1 0 0 40 20 20 1 1 0 0-40"
      />
      <ClipPath id="prefix__b" clipRule="nonzero">
        <Use xlinkHref="#prefix__a" clipRule="nonzero" />
      </ClipPath>
      <ClipPath id="prefix__c" clipRule="evenodd">
        <Path
          id="prefix__a"
          y={50}
          d="M25 10a20 20 0 1 0 0 40 20 20 1 1 0 0-40m20 0a20 20 0 1 0 0 40 20 20 1 1 0 0-40"
        />
      </ClipPath>
      <ClipPath id="prefix__d" clipRule="evenodd">
        <Use xlinkHref="#prefix__a" clipRule="evenodd" y={50} />
      </ClipPath>
    </Defs>
    <Path clipPath="url(#prefix__b)" fill="#6495ed" d="M0 5h70v50H0z" />
    <Text x={15} y={54} fontSize={5} fill="#fff">
      {'non-zero clip-rule'}
    </Text>
    <Use xlinkHref="#prefix__a" x={70} stroke="#6495ed" />
    <Text x={86} y={54} fontSize={5} fill="#fff">
      {'non-zero fill-rule'}
    </Text>
    <G>
      <Path clipPath="url(#prefix__d)" fill="#daa520" d="M0 55h70v50H0z" />
      <Text x={14} y={105} fontSize={5} fill="#fff">
        {'even-odd clip-rule'}
      </Text>
    </G>
    <G>
      <Use
        fillRule="evenodd"
        xlinkHref="#prefix__a"
        x={70}
        y={50}
        stroke="#daa520"
      />
      <Text x={85} y={105} fontSize={5} fill="#fff">
        {'even-odd fill-rule'}
      </Text>
    </G>
  </Svg>
);

const SvgComponent2 = props => (
  <Svg height="90" width="100" viewBox="0 0 100 90">
    <Defs>
      <Path d="M50,0 21,90 98,35 2,35 79,90z" id="star" fill="white" />
      <ClipPath id="emptyStar" clipRule="evenodd">
        <Use href="#star" clipRule="evenodd" />
      </ClipPath>
      <ClipPath id="filledStar" clipRule="nonzero">
        <Use href="#star" clipRule="nonzero" />
      </ClipPath>
    </Defs>
    <Rect clipPath="url(#emptyStar)" width="50" height="90" fill="blue" />
    <Rect
      clipPath="url(#filledStar)"
      width="50"
      height="90"
      x="50"
      fill="red"
    />
  </Svg>
);

const SvgComponent3 = ({ clipRule = 'nonzero' }) => (
  <Svg width="50%" height="500">
    <Defs>
      <ClipPath id="windowClip" clipRule={clipRule}>
        <G>
          <Rect x={30} y={50} height={100} width={100} />
          <Text x={30} y={70} fontSize={40}>
            Hello world
          </Text>
          <Rect x={30} y={200} height={100} width={100} />
          <Rect x={100} y={250} height={100} width={100} />
          <Rect x={30} y={370} height={100} width={100} />
          <Polygon
            transform="translate(70,400)"
            points="0,0 0,100 100,120 120,0"
          />
        </G>
      </ClipPath>
    </Defs>
    <Rect height="100%" width="100%" fill="#0af" clipPath="url(#windowClip)" />
  </Svg>
);

export default class App extends React.Component {
  render() {
    return (
      <View
        style={{
          flex: 1,
          alignContent: 'center',
          justifyContent: 'center',
          backgroundColor: '#ecf0f1',
        }}
      >
        <SvgComponent />
        <SvgComponent2 />
        <View
          style={{
            flex: 1,
            flexDirection: 'row',
          }}
        >
          <SvgComponent3 />
          <SvgComponent3 clipRule="evenodd" />
        </View>
      </View>
    );
  }
}
```
This commit is contained in:
Mikael Sand
2019-04-01 04:34:16 +03:00
parent 134cbad37e
commit 76b8b824e2
2 changed files with 58 additions and 26 deletions
@@ -11,6 +11,7 @@ package com.horcrux.svg;
import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
@@ -66,31 +67,32 @@ class UseView extends RenderableView {
void draw(Canvas canvas, Paint paint, float opacity) {
VirtualView template = getSvgView().getDefinedTemplate(mHref);
if (template != null) {
canvas.translate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
if (template instanceof RenderableView) {
((RenderableView)template).mergeProperties(this);
}
int count = template.saveAndSetupCanvas(canvas);
clip(canvas, paint);
if (template instanceof SymbolView) {
SymbolView symbol = (SymbolView)template;
symbol.drawSymbol(canvas, paint, opacity, (float) relativeOnWidth(mW), (float) relativeOnHeight(mH));
} else {
template.draw(canvas, paint, opacity * mOpacity);
}
this.setClientRect(template.getClientRect());
template.restoreCanvas(canvas, count);
if (template instanceof RenderableView) {
((RenderableView)template).resetProperties();
}
} else {
if (template == null) {
FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
"template named: " + mHref + " is not defined.");
"template named: " + mHref + " is not defined.");
return;
}
canvas.translate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
if (template instanceof RenderableView) {
((RenderableView)template).mergeProperties(this);
}
int count = template.saveAndSetupCanvas(canvas);
clip(canvas, paint);
if (template instanceof SymbolView) {
SymbolView symbol = (SymbolView)template;
symbol.drawSymbol(canvas, paint, opacity, (float) relativeOnWidth(mW), (float) relativeOnHeight(mH));
} else {
template.draw(canvas, paint, opacity * mOpacity);
}
this.setClientRect(template.getClientRect());
template.restoreCanvas(canvas, count);
if (template instanceof RenderableView) {
((RenderableView)template).resetProperties();
}
}
@@ -105,6 +107,12 @@ class UseView extends RenderableView {
mInvTransform.mapPoints(dst);
VirtualView template = getSvgView().getDefinedTemplate(mHref);
if (template == null) {
FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
"template named: " + mHref + " is not defined.");
return -1;
}
int hitChild = template.hitTest(dst);
if (hitChild != -1) {
return (template.isResponsible() || hitChild != template.getId()) ? hitChild : getId();
@@ -115,7 +123,17 @@ class UseView extends RenderableView {
@Override
Path getPath(Canvas canvas, Paint paint) {
// todo:
return new Path();
VirtualView template = getSvgView().getDefinedTemplate(mHref);
if (template == null) {
FLog.w(ReactConstants.TAG, "`Use` element expected a pre-defined svg template as `href` prop, " +
"template named: " + mHref + " is not defined.");
return null;
}
Path path = template.getPath(canvas, paint);
Path use = new Path();
Matrix m = new Matrix();
m.setTranslate((float) relativeOnWidth(mX), (float) relativeOnHeight(mY));
path.transform(m, use);
return use;
}
}
+14
View File
@@ -89,6 +89,9 @@
} else if (self.href) {
// TODO: calling yellow box here
RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href);
return;
} else {
return;
}
CGRect bounds = template.clientRect;
self.clientRect = bounds;
@@ -120,5 +123,16 @@
return nil;
}
- (CGPathRef)getPath: (CGContextRef)context
{
CGAffineTransform transform = CGAffineTransformMakeTranslation([self relativeOnWidth:self.x], [self relativeOnHeight:self.y]);
RNSVGNode const* template = [self.svgView getDefinedTemplate:self.href];
if (!template) {
return nil;
}
CGPathRef path = [template getPath:context];
return CGPathCreateCopyByTransformingPath(path, &transform);
}
@end