Implement strokeDasharray correctly.

Support units and percentages with whitespace separated and/or (possibly white-space surrounded) comma separated length lists.
This commit is contained in:
Mikael Sand
2017-08-04 00:26:00 +03:00
parent b38ae08e9c
commit 786b5f2375
6 changed files with 51 additions and 65 deletions
@@ -20,34 +20,11 @@ import com.facebook.react.bridge.WritableMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
/**
* Contains static helper methods for accessing props.
*/
class PropHelper {
/**
* Converts {@link ReadableArray} to an array of {@code float}. Returns newly created array.
*
* @return a {@code float[]} if converted successfully, or {@code null} if {@param value} was
* {@code null}.
*/
static
@Nullable
float[] toFloatArray(@Nullable ReadableArray value) {
if (value != null) {
int fromSize = value.size();
float[] into = new float[fromSize];
for (int i = 0; i < fromSize; i++) {
into[i] = (float) value.getDouble(i);
}
return into;
}
return null;
}
private static final int inputMatrixDataSize = 6;
/**
@@ -53,7 +53,7 @@ abstract public class RenderableShadowNode extends VirtualNode {
private static final int FILL_RULE_NONZERO = 1;
public @Nullable ReadableArray mStroke;
public @Nullable float[] mStrokeDasharray;
public @Nullable String[] mStrokeDasharray;
public String mStrokeWidth = "1";
public float mStrokeOpacity = 1;
@@ -117,12 +117,14 @@ abstract public class RenderableShadowNode extends VirtualNode {
@ReactProp(name = "strokeDasharray")
public void setStrokeDasharray(@Nullable ReadableArray strokeDasharray) {
mStrokeDasharray = PropHelper.toFloatArray(strokeDasharray);
if (mStrokeDasharray != null && mStrokeDasharray.length > 0) {
for (int i = 0; i < mStrokeDasharray.length; i++) {
mStrokeDasharray[i] = mStrokeDasharray[i] * mScale;
if (strokeDasharray != null) {
int fromSize = strokeDasharray.size();
mStrokeDasharray = new String[fromSize];
for (int i = 0; i < fromSize; i++) {
mStrokeDasharray[i] = strokeDasharray.getString(i);
}
} else {
mStrokeDasharray = null;
}
markUpdated();
}
@@ -249,8 +251,13 @@ abstract public class RenderableShadowNode extends VirtualNode {
paint.setStrokeWidth((float) strokeWidth);
setupPaint(paint, opacity, mStroke);
if (mStrokeDasharray != null && mStrokeDasharray.length > 0) {
paint.setPathEffect(new DashPathEffect(mStrokeDasharray, mStrokeDashoffset));
if (mStrokeDasharray != null) {
int length = mStrokeDasharray.length;
float[] intervals = new float[length];
for (int i = 0; i < length; i++) {
intervals[i] = (float)relativeOnOther(mStrokeDasharray[i]);
}
paint.setPathEffect(new DashPathEffect(intervals, mStrokeDashoffset));
}
return true;
+14
View File
@@ -0,0 +1,14 @@
const spaceReg = /\s+/;
const commaReg = /,/g;
export default function (lengthList) {
if (typeof lengthList === 'string') {
return lengthList.trim().replace(commaReg, ' ').split(spaceReg);
} else if (typeof lengthList === 'number') {
return [`${lengthList}`];
} else if (lengthList && typeof lengthList.map === 'function') {
return lengthList.map(d => `${d}`);
} else {
return [];
}
}
+14 -14
View File
@@ -1,8 +1,7 @@
import extractBrush from './extractBrush';
import extractOpacity from './extractOpacity';
import {strokeProps} from '../props';
const separator = /\s*,\s*/;
import extractLengthList from "./extractLengthList";
const caps = {
butt: 0,
@@ -26,21 +25,22 @@ export default function(props, styleProperties) {
});
const {stroke} = props;
let strokeWidth = props.strokeWidth;
let strokeDasharray = props.strokeDasharray;
let {
strokeWidth,
strokeDasharray
} = props;
if (!strokeDasharray || strokeDasharray === 'none') {
strokeDasharray = null;
} else if (typeof strokeDasharray === 'string') {
strokeDasharray = strokeDasharray.split(separator).map(dash => +dash);
}
// <dasharray> It's a list of comma and/or white space separated <length>s
// and <percentage>s that specify the lengths of alternating dashes and gaps.
// If an odd number of values is provided, then the list of values is repeated
// to yield an even number of values. Thus, 5,3,2 is equivalent to 5,3,2,5,3,2.
if (strokeDasharray && (strokeDasharray.length % 2) === 1) {
strokeDasharray.concat(strokeDasharray);
} else {
// <dasharray> It's a list of comma and/or white space separated <length>s
// and <percentage>s that specify the lengths of alternating dashes and gaps.
// If an odd number of values is provided, then the list of values is repeated
// to yield an even number of values. Thus, 5,3,2 is equivalent to 5,3,2,5,3,2.
strokeDasharray = extractLengthList(strokeDasharray);
if (strokeDasharray && (strokeDasharray.length % 2) === 1) {
strokeDasharray.concat(strokeDasharray);
}
}
if (!strokeWidth || typeof strokeWidth !== 'string') {
+7 -19
View File
@@ -2,12 +2,12 @@ import _ from 'lodash';
//noinspection JSUnresolvedVariable
import React, {Children} from 'react';
import TSpan from '../../elements/TSpan';
import extractLengthList from './extractLengthList';
const fontRegExp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?[ptexm%])*(?:\s*\/.*?)?\s+)?\s*"?([^"]*)/i;
const fontFamilyPrefix = /^[\s"']*/;
const fontFamilySuffix = /[\s"']*$/;
const spaceReg = /\s+/;
const commaReg = /,/g;
const commaReg = /\s*,\s*/g;
const cachedFontObjectsFromString = {};
@@ -85,18 +85,6 @@ export function extractFont(props) {
return _.defaults(ownedFont, font);
}
function parseSVGLengthList(delta) {
if (typeof delta === 'string') {
return delta.trim().replace(commaReg, ' ').split(spaceReg);
} else if (typeof delta === 'number') {
return [delta.toString()];
} else if (delta && typeof delta.map === 'function') {
return delta.map(d => `${d}`);
} else {
return [];
}
}
export default function(props, container) {
const {
x,
@@ -110,11 +98,11 @@ export default function(props, container) {
children
} = props;
const positionX = parseSVGLengthList(x);
const positionY = parseSVGLengthList(y);
const deltaX = parseSVGLengthList(dx);
const deltaY = parseSVGLengthList(dy);
rotate = parseSVGLengthList(rotate);
const positionX = extractLengthList(x);
const positionY = extractLengthList(y);
const deltaX = extractLengthList(dx);
const deltaY = extractLengthList(dy);
rotate = extractLengthList(rotate);
let content = null;
if (typeof children === 'string' || typeof children === 'number') {
+1 -1
View File
@@ -41,7 +41,7 @@ const strokeProps = {
stroke: PropTypes.string,
strokeWidth: numberProp,
strokeOpacity: numberProp,
strokeDasharray: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.string]),
strokeDasharray: PropTypes.oneOfType([PropTypes.arrayOf(numberProp), PropTypes.string]),
strokeDashoffset: numberProp,
strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']),
strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round']),