mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-19 21:45:10 +00:00
add some android support
add path for Text add stroke pattern add text dashed and fix dasharray bug
This commit is contained in:
@@ -32,9 +32,9 @@ class StrokeLinecap extends Component{
|
||||
render() {
|
||||
return <Svg height="80" width="225">
|
||||
<G fill="none" stroke="black">
|
||||
<Path strokeLinecap="butt" strokeWidth="2" d="M5 20 l215 0" />
|
||||
<Path strokeLinecap="round" strokeWidth="4" d="M5 40 l215 0" />
|
||||
<Path strokeLinecap="square" strokeWidth="6" d="M5 60 l215 0" />
|
||||
<Path strokeLinecap="butt" strokeWidth="8" d="M5 20 l215 0" />
|
||||
<Path strokeLinecap="round" strokeWidth="8" d="M5 40 l215 0" />
|
||||
<Path strokeLinecap="square" strokeWidth="8" d="M5 60 l215 0" />
|
||||
</G>
|
||||
</Svg>;
|
||||
}
|
||||
|
||||
@@ -131,7 +131,10 @@ class TextPath extends Component{
|
||||
C 20 10 30 0 40 10
|
||||
C 50 20 60 30 70 20
|
||||
C 80 10 90 10 90 10
|
||||
C 110 20 120 30 120 20
|
||||
C 140 10 150 10 150 10
|
||||
`}
|
||||
y="20"
|
||||
>We go up, then we go down, then up again</Text>
|
||||
</Svg>;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ package com.horcrux.svg;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.DashPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
@@ -144,6 +145,38 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
||||
markUpdateSeen();
|
||||
}
|
||||
|
||||
/*
|
||||
* sorting stops and stopsColors from array
|
||||
*/
|
||||
private static void parseGradientStops(float[] value, int stopsCount, float[] stops, int[] stopsColors, int startColorsPosition) {
|
||||
int startStops = value.length - stopsCount;
|
||||
for (int i = 0; i < stopsCount; i++) {
|
||||
stops[i] = value[startStops + i];
|
||||
stopsColors[i] = Color.argb(
|
||||
(int) (value[startColorsPosition + i * 4 + 3] * 255),
|
||||
(int) (value[startColorsPosition + i * 4] * 255),
|
||||
(int) (value[startColorsPosition + i * 4 + 1] * 255),
|
||||
(int) (value[startColorsPosition + i * 4 + 2] * 255));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets up {@link #mPaint} according to the props set on a shadow view. Returns {@code true}
|
||||
* if the fill should be drawn, {@code false} if not.
|
||||
*/
|
||||
protected boolean setupFillPaint(Paint paint, float opacity) {
|
||||
if (mFillColor != null && mFillColor.length > 0) {
|
||||
paint.reset();
|
||||
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
setupPaint(paint, opacity, mFillColor);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up {@link #mPaint} according to the props set on a shadow view. Returns {@code true}
|
||||
* if the stroke should be drawn, {@code false} if not.
|
||||
@@ -185,88 +218,63 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
||||
}
|
||||
|
||||
paint.setStrokeWidth(mStrokeWidth * mScale);
|
||||
paint.setARGB(
|
||||
(int) (mStrokeColor.length > 3 ? mStrokeColor[3] * opacity * 255 : opacity * 255),
|
||||
(int) (mStrokeColor[0] * 255),
|
||||
(int) (mStrokeColor[1] * 255),
|
||||
(int) (mStrokeColor[2] * 255));
|
||||
|
||||
setupPaint(paint, opacity, mStrokeColor);
|
||||
|
||||
if (mStrokeDash != null && mStrokeDash.length > 0) {
|
||||
// TODO(6352067): Support dashes
|
||||
FLog.w(ReactConstants.TAG, "RNSVG: Dashes are not supported yet!");
|
||||
// todo: dashoffset
|
||||
paint.setPathEffect(new DashPathEffect(mStrokeDash, 0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* sorting stops and stopsColors from array
|
||||
*/
|
||||
private static void parseGradientStops(float[] value, int stopsCount, float[] stops, int[] stopsColors, int startColorsPosition) {
|
||||
int startStops = value.length - stopsCount;
|
||||
for (int i = 0; i < stopsCount; i++) {
|
||||
stops[i] = value[startStops + i];
|
||||
stopsColors[i] = Color.argb(
|
||||
(int) (value[startColorsPosition + i * 4 + 3] * 255),
|
||||
(int) (value[startColorsPosition + i * 4] * 255),
|
||||
(int) (value[startColorsPosition + i * 4 + 1] * 255),
|
||||
(int) (value[startColorsPosition + i * 4 + 2] * 255));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets up {@link #mPaint} according to the props set on a shadow view. Returns {@code true}
|
||||
* if the fill should be drawn, {@code false} if not.
|
||||
*/
|
||||
protected boolean setupFillPaint(Paint paint, float opacity) {
|
||||
private void setupPaint(Paint paint, float opacity, float[] colors) {
|
||||
int stopsCount;
|
||||
int [] stopsColors;
|
||||
float [] stops;
|
||||
if (mFillColor != null && mFillColor.length > 0) {
|
||||
paint.reset();
|
||||
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
int colorType = (int) mFillColor[0];
|
||||
|
||||
int colorType = (int) colors[0];
|
||||
switch (colorType) {
|
||||
case 0:
|
||||
paint.setARGB(
|
||||
(int) (mFillColor.length > 4 ? mFillColor[4] * opacity * 255 : opacity * 255),
|
||||
(int) (mFillColor[1] * 255),
|
||||
(int) (mFillColor[2] * 255),
|
||||
(int) (mFillColor[3] * 255));
|
||||
(int) (colors.length > 4 ? colors[4] * opacity * 255 : opacity * 255),
|
||||
(int) (colors[1] * 255),
|
||||
(int) (colors[2] * 255),
|
||||
(int) (colors[3] * 255));
|
||||
break;
|
||||
case 1:
|
||||
stopsCount = (mFillColor.length - 5) / 5;
|
||||
stopsCount = (colors.length - 5) / 5;
|
||||
stopsColors = new int [stopsCount];
|
||||
stops = new float[stopsCount];
|
||||
|
||||
parseGradientStops(mFillColor, stopsCount, stops, stopsColors, 5);
|
||||
parseGradientStops(colors, stopsCount, stops, stopsColors, 5);
|
||||
paint.setShader(
|
||||
new LinearGradient(
|
||||
mFillColor[1] * mScale,
|
||||
mFillColor[2] * mScale,
|
||||
mFillColor[3] * mScale,
|
||||
mFillColor[4] * mScale,
|
||||
colors[1] * mScale,
|
||||
colors[2] * mScale,
|
||||
colors[3] * mScale,
|
||||
colors[4] * mScale,
|
||||
stopsColors,
|
||||
stops,
|
||||
Shader.TileMode.CLAMP));
|
||||
break;
|
||||
case 2:
|
||||
stopsCount = (mFillColor.length - 7) / 5;
|
||||
stopsCount = (colors.length - 7) / 5;
|
||||
stopsColors = new int [stopsCount];
|
||||
stops = new float[stopsCount];
|
||||
parseGradientStops(mFillColor, stopsCount, stops, stopsColors, 7);
|
||||
parseGradientStops(colors, stopsCount, stops, stopsColors, 7);
|
||||
|
||||
// TODO: support focus
|
||||
float focusX = mFillColor[1];
|
||||
float focusY = mFillColor[2];
|
||||
float focusX = colors[1];
|
||||
float focusY = colors[2];
|
||||
|
||||
float radius = mFillColor[3];
|
||||
float radiusRatio = mFillColor[4] / radius;
|
||||
float radius = colors[3];
|
||||
float radiusRatio = colors[4] / radius;
|
||||
Shader radialGradient = new RadialGradient(
|
||||
mFillColor[5] * mScale,
|
||||
mFillColor[6] * mScale / radiusRatio,
|
||||
colors[5] * mScale,
|
||||
colors[6] * mScale / radiusRatio,
|
||||
radius * mScale,
|
||||
stopsColors,
|
||||
stops,
|
||||
@@ -284,8 +292,5 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
||||
// TODO: Support pattern.
|
||||
FLog.w(ReactConstants.TAG, "RNSVG: Color type " + colorType + " not supported!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,11 @@ import javax.annotation.Nullable;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
@@ -41,6 +43,7 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode {
|
||||
|
||||
private @Nullable ReadableMap mFrame;
|
||||
private int mTextAlignment = TEXT_ALIGNMENT_LEFT;
|
||||
private Path mPath;
|
||||
|
||||
@ReactProp(name = "frame")
|
||||
public void setFrame(@Nullable ReadableMap frame) {
|
||||
@@ -52,6 +55,14 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode {
|
||||
mTextAlignment = alignment;
|
||||
}
|
||||
|
||||
@ReactProp(name = "path")
|
||||
public void setPath(@Nullable ReadableArray textPath) {
|
||||
float[] pathData = PropHelper.toFloatArray(textPath);
|
||||
Path path = new Path();
|
||||
mPath = super.createPath(pathData, path);
|
||||
markUpdated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas, Paint paint, float opacity) {
|
||||
if (mFrame == null) {
|
||||
|
||||
@@ -52,6 +52,9 @@ public abstract class RNSVGVirtualNode extends ReactShadowNode {
|
||||
private static final int CLIP_RULE_NONZERO = 1;
|
||||
protected final float mScale;
|
||||
private float[] mClipData;
|
||||
private int mClipRule;
|
||||
private boolean mClipRuleSet;
|
||||
private boolean mClipDataSet;
|
||||
|
||||
public RNSVGVirtualNode() {
|
||||
mScale = DisplayMetricsHolder.getWindowDisplayMetrics().density;
|
||||
@@ -93,24 +96,16 @@ public abstract class RNSVGVirtualNode extends ReactShadowNode {
|
||||
@ReactProp(name = "clipPath")
|
||||
public void setClipPath(@Nullable ReadableArray clipPath) {
|
||||
mClipData = PropHelper.toFloatArray(clipPath);
|
||||
mClipDataSet = true;
|
||||
setupClip();
|
||||
markUpdated();
|
||||
}
|
||||
|
||||
@ReactProp(name = "clipRule", defaultInt = CLIP_RULE_NONZERO)
|
||||
public void setClipRule(int clipRule) {
|
||||
Path path = new Path();
|
||||
switch (clipRule) {
|
||||
case CLIP_RULE_EVENODD:
|
||||
path.setFillType(Path.FillType.EVEN_ODD);
|
||||
break;
|
||||
case CLIP_RULE_NONZERO:
|
||||
break;
|
||||
default:
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"clipRule " + clipRule + " unrecognized");
|
||||
}
|
||||
|
||||
mClipPath = createPath(mClipData, path);
|
||||
mClipRule = clipRule;
|
||||
mClipRuleSet = true;
|
||||
setupClip();
|
||||
markUpdated();
|
||||
}
|
||||
|
||||
@@ -135,6 +130,24 @@ public abstract class RNSVGVirtualNode extends ReactShadowNode {
|
||||
markUpdated();
|
||||
}
|
||||
|
||||
private void setupClip() {
|
||||
if (mClipDataSet && mClipRuleSet) {
|
||||
Path path = new Path();
|
||||
switch (mClipRule) {
|
||||
case CLIP_RULE_EVENODD:
|
||||
path.setFillType(Path.FillType.EVEN_ODD);
|
||||
break;
|
||||
case CLIP_RULE_NONZERO:
|
||||
break;
|
||||
default:
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"clipRule " + mClipRule + " unrecognized");
|
||||
}
|
||||
|
||||
mClipPath = createPath(mClipData, path);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setupMatrix() {
|
||||
sRawMatrix[0] = sMatrixData[0];
|
||||
sRawMatrix[1] = sMatrixData[2];
|
||||
@@ -163,7 +176,9 @@ public abstract class RNSVGVirtualNode extends ReactShadowNode {
|
||||
protected Path createPath(float[] data, Path path) {
|
||||
path.moveTo(0, 0);
|
||||
int i = 0;
|
||||
|
||||
while (i < data.length) {
|
||||
|
||||
int type = (int) data[i++];
|
||||
switch (type) {
|
||||
case PATH_TYPE_MOVETO:
|
||||
|
||||
@@ -31,13 +31,13 @@ export default function (props) {
|
||||
if (pattern) {
|
||||
clippingProps.clipPath = new SerializablePath(pattern).toJSON();
|
||||
} else {
|
||||
clippingProps = {};
|
||||
// TODO: warn
|
||||
}
|
||||
} else {
|
||||
clippingProps.clipPath = new SerializablePath(clipPath).toJSON();
|
||||
}
|
||||
}
|
||||
|
||||
return clippingProps;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,15 +28,21 @@ function strokeFilter(props, dimensions) {
|
||||
strokeDasharray = strokeDasharray.split(separator).map(dash => +dash);
|
||||
}
|
||||
|
||||
// strokeDasharray length must be more than 1.
|
||||
if (strokeDasharray && strokeDasharray.length === 1) {
|
||||
strokeDasharray.push(strokeDasharray[0]);
|
||||
}
|
||||
|
||||
if (!stroke) {
|
||||
stroke = '#000';
|
||||
}
|
||||
|
||||
|
||||
// TODO: dashoffset
|
||||
// TODO: propTypes check
|
||||
return {
|
||||
stroke: patterns(stroke, +props.strokeOpacity, dimensions, props.svgId),
|
||||
strokeLinecap: caps[props.strokeLinecap] || 2,
|
||||
strokeLinecap: caps[props.strokeLinecap] || 0,
|
||||
strokeLinejoin: joins[props.strokeLinejoin] || 0,
|
||||
strokeDash: strokeDasharray || null,
|
||||
strokeWidth: strokeWidth || 1
|
||||
|
||||
@@ -82,15 +82,12 @@ const alignments = {
|
||||
};
|
||||
|
||||
export default function(props) {
|
||||
let textPath = props.path ? new SerializablePath(props.path).toJSON() : null;
|
||||
var textFrame = extractFontAndLines(
|
||||
props,
|
||||
childrenAsString(props.children)
|
||||
);
|
||||
|
||||
return {
|
||||
alignment: alignments[props.textAnchor] || 0,
|
||||
frame: textFrame,
|
||||
path: textPath
|
||||
frame: extractFontAndLines(
|
||||
props,
|
||||
childrenAsString(props.children)
|
||||
),
|
||||
path: props.path ? new SerializablePath(props.path).toJSON() : undefined
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user