fix: scale path markers so they match ios (#2018)

PR adding scaling of markers of path in order to make them behave the same as on iOS. Also bumped the versions of agp and spotless.
This commit is contained in:
Wojciech Lewicki
2023-03-30 14:27:30 +02:00
committed by GitHub
parent a7af70c43d
commit 29382d927a
8 changed files with 324 additions and 37 deletions
+68 -9
View File
@@ -8,9 +8,10 @@ import {
Ellipse,
Line,
Polygon,
Path,
} from 'react-native-svg';
class CircleExample extends Component {
class EllipseExample extends Component {
static title = 'Circle shaped marker on ellipse';
render() {
return (
@@ -47,7 +48,7 @@ class CircleExample extends Component {
}
}
class StrokeCircle extends Component {
class LineExample extends Component {
static title = 'Triangle shaped marker on line';
render() {
return (
@@ -81,7 +82,7 @@ class StrokeCircle extends Component {
}
}
class StrokeOpacityCircle extends Component {
class CircleExample extends Component {
static title = 'Rect shaped marker on circle';
render() {
return (
@@ -111,7 +112,7 @@ class StrokeOpacityCircle extends Component {
}
}
class PieCircle extends Component {
class RectExample extends Component {
static title = 'Ellipse shaped marker on rect';
render() {
return (
@@ -119,7 +120,7 @@ class PieCircle extends Component {
<Defs>
<Marker
id="selection"
markerUnits="userSpaceOnUse"
markerUnits="strokeWidth"
refX="0"
refY="0"
orient="auto">
@@ -141,7 +142,7 @@ class PieCircle extends Component {
height="30"
fill="none"
stroke="blue"
strokeWidth="5"
strokeWidth="1"
marker="url(#selection)"
/>
</Svg>
@@ -149,11 +150,69 @@ class PieCircle extends Component {
}
}
class PathExample extends Component {
static title = 'Path shaped marker on line';
render() {
return (
<Svg height="200" width="400" viewBox="0 0 300 100">
<Defs>
<Marker
id="arrow"
// viewBox="0 0 10 10"
refX="5"
refY="5"
markerWidth="6"
markerHeight="6"
// orient="auto-start-reverse"
>
<Path fill="blue" d="M 0 0 L 10 5 L 0 10 z" />
</Marker>
</Defs>
<Line
x1="10"
y1="10"
x2="90"
y2="90"
stroke="black"
strokeWidth="3"
markerEnd="url(#arrow)"
/>
</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 height="30" width="30" viewBox="0 0 3000 2500">
<Defs>
<Marker
id="Triangle"
// viewBox="0 0 10 10"
refX="0"
refY="5"
markerUnits="strokeWidth"
markerWidth="4"
markerHeight="3"
orient="auto">
<Path fill="blue" d="M 0 0 L 10 5 L 0 10 z" />
</Marker>
</Defs>
<Path
d="M 1000 750 L 2000 750 L 2500 1250"
fill="none"
stroke="black"
strokeWidth="70"
markerEnd="url(#Triangle)"
markerStart="url(#Triangle)"
/>
</Svg>
);
const samples = [CircleExample, StrokeCircle, StrokeOpacityCircle, PieCircle];
const samples = [
EllipseExample,
LineExample,
CircleExample,
RectExample,
PathExample,
];
export {icon, samples};
+1
View File
@@ -113,6 +113,7 @@ const names: (keyof typeof examples)[] = [
'PanResponder',
'Reusable',
'Transforms',
'Markers',
];
const initialState = {
+2
View File
@@ -16,6 +16,7 @@ import * as Reusable from './examples/Reusable';
import * as TouchEvents from './examples/TouchEvents';
import * as PanResponder from './examples/PanResponder';
import * as Transforms from './examples/Transforms';
import * as Markers from './examples/Markers';
export {
Svg,
@@ -36,4 +37,5 @@ export {
Reusable,
PanResponder,
Transforms,
Markers,
};
+218
View File
@@ -0,0 +1,218 @@
import React, {Component} from 'react';
import {
Svg,
Circle,
Defs,
Marker,
Rect,
Ellipse,
Line,
Polygon,
Path,
} from 'react-native-svg';
class EllipseExample 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 LineExample 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 CircleExample 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 RectExample 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="strokeWidth"
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="1"
marker="url(#selection)"
/>
</Svg>
);
}
}
class PathExample extends Component {
static title = 'Path shaped marker on line';
render() {
return (
<Svg height="200" width="400" viewBox="0 0 300 100">
<Defs>
<Marker
id="arrow"
// viewBox="0 0 10 10"
refX="5"
refY="5"
markerWidth="6"
markerHeight="6"
// orient="auto-start-reverse"
>
<Path fill="blue" d="M 0 0 L 10 5 L 0 10 z" />
</Marker>
</Defs>
<Line
x1="10"
y1="10"
x2="90"
y2="90"
stroke="black"
strokeWidth="3"
markerEnd="url(#arrow)"
/>
</Svg>
);
}
}
const icon = (
<Svg height="30" width="30" viewBox="0 0 3000 2500">
<Defs>
<Marker
id="Triangle"
// viewBox="0 0 10 10"
refX="0"
refY="5"
markerUnits="strokeWidth"
markerWidth="4"
markerHeight="3"
orient="auto">
<Path fill="blue" d="M 0 0 L 10 5 L 0 10 z" />
</Marker>
</Defs>
<Path
d="M 1000 750 L 2000 750 L 2500 1250"
fill="none"
stroke="black"
strokeWidth="70"
markerEnd="url(#Triangle)"
markerStart="url(#Triangle)"
/>
</Svg>
);
const samples = [
EllipseExample,
LineExample,
CircleExample,
RectExample,
PathExample,
];
export {icon, samples};
+9 -7
View File
@@ -7,10 +7,10 @@ buildscript {
mavenCentral()
google()
}
dependencies {
classpath("com.android.tools.build:gradle:3.6.1")
classpath "com.diffplug.spotless:spotless-plugin-gradle:5.15.0"
classpath("com.android.tools.build:gradle:7.4.2")
classpath "com.diffplug.spotless:spotless-plugin-gradle:6.17.0"
}
}
}
@@ -39,8 +39,10 @@ def safeExtGet(prop, fallback) {
android {
compileSdkVersion safeExtGet('compileSdkVersion', 28)
namespace "com.horcrux.svg"
def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
if (agpVersion.tokenize('.')[0].toInteger() >= 7) {
namespace "com.horcrux.svg"
}
// Used to override the NDK path/version on internal CI or by allowing
// users to customize the NDK path/version from their root project (e.g. for M1 support)
if (rootProject.hasProperty("ndkPath")) {
@@ -55,7 +57,7 @@ android {
//noinspection OldTargetApi
targetSdkVersion safeExtGet('targetSdkVersion', 28)
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
consumerProguardFiles 'proguard-rules.pro'
}
lintOptions {
@@ -66,7 +68,7 @@ android {
java {
if (!isNewArchitectureEnabled()) {
srcDirs += [
"src/paper/java",
"src/paper/java",
"build/generated/source/codegen/java"
]
}
@@ -27,6 +27,12 @@ class PathView extends RenderableView {
public void setD(String d) {
mPath = PathParser.parse(d);
elements = PathParser.elements;
for (PathElement elem : elements) {
for (Point point : elem.points) {
point.x *= mScale;
point.y *= mScale;
}
}
invalidate();
}
@@ -74,7 +74,7 @@ public class SvgView extends ReactViewGroup implements ReactCompoundView, ReactC
}
@Override
public void onInitializeAccessibilityNodeInfo (AccessibilityNodeInfo info) {
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
Rect r = new Rect();
@@ -7,9 +7,9 @@ import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -23,7 +23,6 @@ import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.uimanager.OnLayoutEvent;
import com.facebook.react.uimanager.PointerEvents;
import com.facebook.react.uimanager.UIManagerHelper;
import com.facebook.react.uimanager.UIManagerModule;
import com.facebook.react.uimanager.events.EventDispatcher;
import com.facebook.react.views.view.ReactViewGroup;
import java.util.ArrayList;
@@ -110,29 +109,28 @@ public abstract class VirtualView extends ReactViewGroup {
}
@Override
public void onInitializeAccessibilityNodeInfo (AccessibilityNodeInfo info) {
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (mClientRect != null) {
SvgView root = getSvgView();
SvgView root = getSvgView();
int[] rootPositionOnScreen = new int[2];
getSvgView().getLocationOnScreen(rootPositionOnScreen);
Rect infoBoundsInScreen = new Rect();
infoBoundsInScreen.left = rootPositionOnScreen[0] + (int) Math.floor(mClientRect.left);
infoBoundsInScreen.top = rootPositionOnScreen[1] + (int) Math.floor(mClientRect.top);
infoBoundsInScreen.right = infoBoundsInScreen.left + (int) Math.ceil(mClientRect.width());
infoBoundsInScreen.bottom = infoBoundsInScreen.top + (int) Math.ceil(mClientRect.height());
int[] rootPositionOnScreen = new int[2];
getSvgView().getLocationOnScreen(rootPositionOnScreen);
Rect infoBoundsInScreen = new Rect();
infoBoundsInScreen.left = rootPositionOnScreen[0] + (int) Math.floor(mClientRect.left);
infoBoundsInScreen.top = rootPositionOnScreen[1] + (int) Math.floor(mClientRect.top);
infoBoundsInScreen.right = infoBoundsInScreen.left + (int) Math.ceil(mClientRect.width());
infoBoundsInScreen.bottom = infoBoundsInScreen.top + (int) Math.ceil(mClientRect.height());
Rect rootVisibleRect = new Rect();
boolean isRootVisible = root.getGlobalVisibleRect(rootVisibleRect);
boolean infoIsVisibleToUser = isRootVisible && infoBoundsInScreen.intersect(rootVisibleRect);
Rect rootVisibleRect = new Rect();
boolean isRootVisible = root.getGlobalVisibleRect(rootVisibleRect);
boolean infoIsVisibleToUser = isRootVisible && infoBoundsInScreen.intersect(rootVisibleRect);
String infoClassName = this.getClass().getCanonicalName();
info.setBoundsInScreen(infoBoundsInScreen);
info.setClassName(infoClassName);
info.setVisibleToUser(infoIsVisibleToUser);
String infoClassName = this.getClass().getCanonicalName();
info.setBoundsInScreen(infoBoundsInScreen);
info.setClassName(infoClassName);
info.setVisibleToUser(infoIsVisibleToUser);
}
}
@@ -599,7 +597,8 @@ public abstract class VirtualView extends ReactViewGroup {
setRight(right);
setBottom(bottom);
}
EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(mContext, getId());
EventDispatcher eventDispatcher =
UIManagerHelper.getEventDispatcherForReactTag(mContext, getId());
if (eventDispatcher != null) {
eventDispatcher.dispatchEvent(OnLayoutEvent.obtain(this.getId(), left, top, width, height));
}