Skip to content

Commit

Permalink
fix #1592 : add marker support on Rect, Line, Ellipse, Circle and Gro…
Browse files Browse the repository at this point in the history
…up (#1594)

PR adding better Marker support on Android. Markers were only displayed on Path on Android. I have looked at the code and Markers are based on elements attribute which was only filled via PathParser. I have modified getPath method on other shapes in order to fill elements attribute. For groups I only copied data from children.

Co-authored-by: Wojciech Lewicki <wojciech.lewicki@swmansion.com>
  • Loading branch information
mlecoq and WoLewicki authored Mar 29, 2023
1 parent 911151a commit a7af70c
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 2 deletions.
1 change: 1 addition & 0 deletions Example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ const names: (keyof typeof examples)[] = [
'Reusable',
'Reanimated',
'Transforms',
'Markers',
];

const initialState = {
Expand Down
2 changes: 2 additions & 0 deletions Example/src/examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import * as TouchEvents from './examples/TouchEvents';
import * as PanResponder from './examples/PanResponder';
import * as Reanimated from './examples/Reanimated';
import * as Transforms from './examples/Transforms';
import * as Markers from './examples/Markers';

export {
Svg,
Expand All @@ -38,4 +39,5 @@ export {
PanResponder,
Reanimated,
Transforms,
Markers,
};
159 changes: 159 additions & 0 deletions Example/src/examples/Markers.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import React, {Component} from 'react';
import {
Svg,
Circle,
Defs,
Marker,
Rect,
Ellipse,
Line,
Polygon,
} from 'react-native-svg';

class CircleExample extends Component {
static title = 'Circle shaped marker on ellipse';
render() {
return (
<Svg height="300" width="400">
<Defs>
<Marker
id="selection"
markerUnits="userSpaceOnUse"
refX="0"
refY="0"
orient="auto">
<Circle
fill="#3a6cff"
r={5}
cx={0}
cy={0}
strokeWidth={1}
stroke="white"
/>
</Marker>
</Defs>
<Ellipse
cx="200"
cy="170"
rx="30%"
ry="10%"
stroke="purple"
strokeWidth="2"
fill="yellow"
marker="url(#selection)"
/>
</Svg>
);
}
}

class StrokeCircle extends Component {
static title = 'Triangle shaped marker on line';
render() {
return (
<Svg height="200" width="200">
<Defs>
<Marker
id="selection"
markerUnits="userSpaceOnUse"
refX="0"
refY="0"
orient="auto">
<Polygon
points="0,0 -5,20 10,30"
fill="lime"
stroke="purple"
strokeWidth="1"
/>
</Marker>
</Defs>
<Line
x1="20"
y1="10%"
x2="60%"
y2="60%"
stroke="red"
strokeWidth="2"
marker="url(#selection)"
/>
</Svg>
);
}
}

class StrokeOpacityCircle extends Component {
static title = 'Rect shaped marker on circle';
render() {
return (
<Svg height="150" width="200">
<Defs>
<Marker
id="selection"
markerUnits="userSpaceOnUse"
refX="0"
refY="0"
orient="auto">
<Rect x="0" y="0" width="15" height="15" fill="rgb(0,0,255)" />
</Marker>
</Defs>
<Circle
cx="70"
cy="70"
r="30%"
stroke="purple"
strokeOpacity="0.5"
strokeWidth="10"
fill="pink"
marker="url(#selection)"
/>
</Svg>
);
}
}

class PieCircle extends Component {
static title = 'Ellipse shaped marker on rect';
render() {
return (
<Svg width="400" height="200" viewBox="0 0 100 100">
<Defs>
<Marker
id="selection"
markerUnits="userSpaceOnUse"
refX="0"
refY="0"
orient="auto">
<Ellipse
fill="#3a6cff"
rx={5}
ry={8}
cx={0}
cy={0}
strokeWidth={1}
stroke="white"
/>
</Marker>
</Defs>
<Rect
x="50%"
y="50"
width="30"
height="30"
fill="none"
stroke="blue"
strokeWidth="5"
marker="url(#selection)"
/>
</Svg>
);
}
}

const icon = (
<Svg height="30" width="30" viewBox="0 0 20 20">
<Circle cx="10" cy="10" r="8" stroke="purple" strokeWidth="1" fill="pink" />
</Svg>
);

const samples = [CircleExample, StrokeCircle, StrokeOpacityCircle, PieCircle];
export {icon, samples};
23 changes: 23 additions & 0 deletions android/src/main/java/com/horcrux/svg/CircleView.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import android.graphics.Path;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import java.util.ArrayList;

@SuppressLint("ViewConstructor")
class CircleView extends RenderableView {
Expand Down Expand Up @@ -79,6 +80,28 @@ Path getPath(Canvas canvas, Paint paint) {
double r = relativeOnOther(mR);

path.addCircle((float) cx, (float) cy, (float) r, Path.Direction.CW);

elements = new ArrayList<>();
elements.add(
new PathElement(
ElementType.kCGPathElementMoveToPoint, new Point[] {new Point(cx, cy - r)}));
elements.add(
new PathElement(
ElementType.kCGPathElementAddLineToPoint,
new Point[] {new Point(cx, cy - r), new Point(cx + r, cy)}));
elements.add(
new PathElement(
ElementType.kCGPathElementAddLineToPoint,
new Point[] {new Point(cx + r, cy), new Point(cx, cy + r)}));
elements.add(
new PathElement(
ElementType.kCGPathElementAddLineToPoint,
new Point[] {new Point(cx, cy + r), new Point(cx - r, cy)}));
elements.add(
new PathElement(
ElementType.kCGPathElementAddLineToPoint,
new Point[] {new Point(cx - r, cy), new Point(cx, cy - r)}));

return path;
}
}
22 changes: 22 additions & 0 deletions android/src/main/java/com/horcrux/svg/EllipseView.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import android.graphics.RectF;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import java.util.ArrayList;

@SuppressLint("ViewConstructor")
class EllipseView extends RenderableView {
Expand Down Expand Up @@ -98,6 +99,27 @@ Path getPath(Canvas canvas, Paint paint) {
new RectF((float) (cx - rx), (float) (cy - ry), (float) (cx + rx), (float) (cy + ry));
path.addOval(oval, Path.Direction.CW);

elements = new ArrayList<>();
elements.add(
new PathElement(
ElementType.kCGPathElementMoveToPoint, new Point[] {new Point(cx, cy - ry)}));
elements.add(
new PathElement(
ElementType.kCGPathElementAddLineToPoint,
new Point[] {new Point(cx, cy - ry), new Point(cx + rx, cy)}));
elements.add(
new PathElement(
ElementType.kCGPathElementAddLineToPoint,
new Point[] {new Point(cx + rx, cy), new Point(cx, cy + ry)}));
elements.add(
new PathElement(
ElementType.kCGPathElementAddLineToPoint,
new Point[] {new Point(cx, cy + ry), new Point(cx - rx, cy)}));
elements.add(
new PathElement(
ElementType.kCGPathElementAddLineToPoint,
new Point[] {new Point(cx - rx, cy), new Point(cx, cy - ry)}));

return path;
}
}
9 changes: 9 additions & 0 deletions android/src/main/java/com/horcrux/svg/GroupView.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import android.view.View;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableMap;
import java.util.ArrayList;
import javax.annotation.Nullable;

@SuppressLint("ViewConstructor")
Expand Down Expand Up @@ -72,13 +73,15 @@ void draw(final Canvas canvas, final Paint paint, final float opacity) {
setupGlyphContext(canvas);
clip(canvas, paint);
drawGroup(canvas, paint, opacity);
renderMarkers(canvas, paint, opacity);
}

void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
pushGlyphContext();
final SvgView svg = getSvgView();
final GroupView self = this;
final RectF groupRect = new RectF();
elements = new ArrayList<>();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child instanceof MaskView) {
Expand All @@ -96,6 +99,7 @@ void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
int count = node.saveAndSetupCanvas(canvas, mCTM);
node.render(canvas, paint, opacity * mOpacity);
RectF r = node.getClientRect();

if (r != null) {
groupRect.union(r);
}
Expand All @@ -109,6 +113,11 @@ void drawGroup(final Canvas canvas, final Paint paint, final float opacity) {
if (node.isResponsible()) {
svg.enableTouchEvents();
}

if (node.elements != null) {
elements.addAll(node.elements);
}

} else if (child instanceof SvgView) {
SvgView svgView = (SvgView) child;
svgView.drawChildren(canvas);
Expand Down
8 changes: 8 additions & 0 deletions android/src/main/java/com/horcrux/svg/LineView.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import android.graphics.Path;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import java.util.ArrayList;

@SuppressLint("ViewConstructor")
class LineView extends RenderableView {
Expand Down Expand Up @@ -96,6 +97,13 @@ Path getPath(Canvas canvas, Paint paint) {

path.moveTo((float) x1, (float) y1);
path.lineTo((float) x2, (float) y2);

elements = new ArrayList<>();
elements.add(
new PathElement(ElementType.kCGPathElementMoveToPoint, new Point[] {new Point(x1, y1)}));
elements.add(
new PathElement(ElementType.kCGPathElementAddLineToPoint, new Point[] {new Point(x2, y2)}));

return path;
}
}
4 changes: 2 additions & 2 deletions android/src/main/java/com/horcrux/svg/MarkerView.java
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,15 @@ void renderMarker(

markerTransform.reset();
Point origin = position.origin;
markerTransform.setTranslate((float) origin.x * mScale, (float) origin.y * mScale);
markerTransform.setTranslate((float) origin.x, (float) origin.y);

double markerAngle = "auto".equals(mOrient) ? -1 : Double.parseDouble(mOrient);
float degrees = 180 + (float) (markerAngle == -1 ? position.angle : markerAngle);
markerTransform.preRotate(degrees);

boolean useStrokeWidth = "strokeWidth".equals(mMarkerUnits);
if (useStrokeWidth) {
markerTransform.preScale(strokeWidth, strokeWidth);
markerTransform.preScale(strokeWidth / mScale, strokeWidth / mScale);
}

double width = relativeOnWidth(mMarkerWidth) / mScale;
Expand Down
17 changes: 17 additions & 0 deletions android/src/main/java/com/horcrux/svg/RectView.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import android.os.Build;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReactContext;
import java.util.ArrayList;

@SuppressLint("ViewConstructor")
class RectView extends RenderableView {
Expand Down Expand Up @@ -170,6 +171,22 @@ Path getPath(Canvas canvas, Paint paint) {
path.close(); // Ensure isSimplePath = false such that rect doesn't become represented using
// integers
}

elements = new ArrayList<>();
elements.add(
new PathElement(ElementType.kCGPathElementMoveToPoint, new Point[] {new Point(x, y)}));
elements.add(
new PathElement(
ElementType.kCGPathElementAddLineToPoint, new Point[] {new Point(x + w, y)}));
elements.add(
new PathElement(
ElementType.kCGPathElementAddLineToPoint, new Point[] {new Point(x + w, y + h)}));
elements.add(
new PathElement(
ElementType.kCGPathElementAddLineToPoint, new Point[] {new Point(x, y + h)}));
elements.add(
new PathElement(ElementType.kCGPathElementAddLineToPoint, new Point[] {new Point(x, y)}));

return path;
}
}

0 comments on commit a7af70c

Please sign in to comment.