add some android support

add path for Text
add stroke pattern
add text dashed and fix dasharray bug
This commit is contained in:
Horcrux
2016-04-23 14:44:48 +08:00
parent d990eaa472
commit 05faac3cf1
8 changed files with 148 additions and 111 deletions

View File

@@ -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>;
}

View File

@@ -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>;
}

View File

@@ -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;
}
}

View File

@@ -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) {

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
}
}