mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-20 05:55:10 +00:00
Merge pull request #25 from magicismight/propInPercents
support percentage props for elements
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -43,3 +43,7 @@ ios/marry/main.jsbundle
|
|||||||
# unfinished documents
|
# unfinished documents
|
||||||
#
|
#
|
||||||
cn-doc.md
|
cn-doc.md
|
||||||
|
|
||||||
|
# experimental code
|
||||||
|
#
|
||||||
|
experimental/
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ class CircleExample extends Component{
|
|||||||
render() {
|
render() {
|
||||||
return <Svg
|
return <Svg
|
||||||
height="100"
|
height="100"
|
||||||
width="100"
|
width="140"
|
||||||
>
|
>
|
||||||
<Circle
|
<Circle
|
||||||
cx="50"
|
cx="50%"
|
||||||
cy="50"
|
cy="50%"
|
||||||
r="50"
|
r="40%"
|
||||||
fill="pink"
|
fill="pink"
|
||||||
/>
|
/>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ class TextClipping extends Component{
|
|||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
fill="red"
|
fill="red"
|
||||||
stroke="blue"
|
stroke="blue"
|
||||||
textAnchor="center"
|
textAnchor="middle"
|
||||||
clipPath="url(#clip)"
|
clipPath="url(#clip)"
|
||||||
>NOT THE FACE</Text>
|
>NOT THE FACE</Text>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
@@ -213,7 +213,7 @@ const icon = <Svg
|
|||||||
</G>
|
</G>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
|
|
||||||
const samples = [ClipPathAttr, ClipRule, ClipPathElement, TextClipping];
|
const samples = [ClipPathAttr, ClipRule];//, ClipPathElement, TextClipping
|
||||||
|
|
||||||
export {
|
export {
|
||||||
icon,
|
icon,
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ class EllipseExample extends Component{
|
|||||||
width="110"
|
width="110"
|
||||||
>
|
>
|
||||||
<Ellipse
|
<Ellipse
|
||||||
cx="55"
|
cx="50%"
|
||||||
cy="55"
|
cy="50%"
|
||||||
rx="50"
|
rx="45%"
|
||||||
ry="30"
|
ry="30%"
|
||||||
stroke="purple"
|
stroke="purple"
|
||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
fill="yellow"
|
fill="yellow"
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class GExample extends Component{
|
|||||||
<Circle
|
<Circle
|
||||||
cx="25"
|
cx="25"
|
||||||
cy="75"
|
cy="75"
|
||||||
|
stroke="red"
|
||||||
/>
|
/>
|
||||||
<Circle
|
<Circle
|
||||||
cx="50"
|
cx="50"
|
||||||
@@ -39,6 +40,7 @@ class GExample extends Component{
|
|||||||
<Circle
|
<Circle
|
||||||
cx="75"
|
cx="75"
|
||||||
cy="25"
|
cy="25"
|
||||||
|
stroke="red"
|
||||||
/>
|
/>
|
||||||
<Circle
|
<Circle
|
||||||
cx="75"
|
cx="75"
|
||||||
@@ -82,7 +84,7 @@ class GTransform extends Component{
|
|||||||
y="75"
|
y="75"
|
||||||
stroke="#600"
|
stroke="#600"
|
||||||
fill="#600"
|
fill="#600"
|
||||||
textAnchor="center"
|
textAnchor="middle"
|
||||||
>
|
>
|
||||||
Text grouped with shapes</Text>
|
Text grouped with shapes</Text>
|
||||||
</G>
|
</G>
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ class LineExample extends Component{
|
|||||||
width="100"
|
width="100"
|
||||||
>
|
>
|
||||||
<Line
|
<Line
|
||||||
x1="0"
|
x1="10%"
|
||||||
y1="0"
|
y1="10%"
|
||||||
x2="100"
|
x2="90%"
|
||||||
y2="100"
|
y2="90%"
|
||||||
stroke="red"
|
stroke="red"
|
||||||
strokeWidth="2"
|
strokeWidth="2"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ class RectExample extends Component{
|
|||||||
height="60"
|
height="60"
|
||||||
>
|
>
|
||||||
<Rect
|
<Rect
|
||||||
x="25"
|
x="5%"
|
||||||
y="5"
|
y="5%"
|
||||||
width="150"
|
width="90%"
|
||||||
height="50"
|
height="90%"
|
||||||
fill="rgb(0,0,255)"
|
fill="rgb(0,0,255)"
|
||||||
strokeWidth="3"
|
strokeWidth="3"
|
||||||
stroke="rgb(0,0,0)"
|
stroke="rgb(0,0,0)"
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class StrokeDashoffset extends Component{
|
|||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
x="100"
|
x="100"
|
||||||
y="40"
|
y="40"
|
||||||
textAnchor="center"
|
textAnchor="middle"
|
||||||
strokeDasharray="100"
|
strokeDasharray="100"
|
||||||
strokeDashoffset="60"
|
strokeDashoffset="60"
|
||||||
>STROKE</Text>
|
>STROKE</Text>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class TextExample extends Component{
|
|||||||
x="50"
|
x="50"
|
||||||
y="9"
|
y="9"
|
||||||
fill="red"
|
fill="red"
|
||||||
textAnchor="center"
|
textAnchor="middle"
|
||||||
>I love SVG!</Text>
|
>I love SVG!</Text>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
}
|
}
|
||||||
@@ -60,6 +60,34 @@ class TextRotate extends Component{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: iOS not support text stroke with pattern
|
||||||
|
class TextStroke extends Component{
|
||||||
|
static title = 'Stroke the text';
|
||||||
|
render() {
|
||||||
|
return <Svg
|
||||||
|
height="60"
|
||||||
|
width="200"
|
||||||
|
>
|
||||||
|
<Defs>
|
||||||
|
<LinearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||||
|
<Stop offset="100%" stopColor="red" stopOpacity="1" />
|
||||||
|
<Stop offset="0%" stopColor="#000" stopOpacity="0.5" />
|
||||||
|
</LinearGradient>
|
||||||
|
</Defs>
|
||||||
|
<Text
|
||||||
|
stroke="url(#grad)"
|
||||||
|
strokeWidth="2"
|
||||||
|
fill="none"
|
||||||
|
fontSize="30"
|
||||||
|
fontWeight="bold"
|
||||||
|
x="100"
|
||||||
|
y="20"
|
||||||
|
textAnchor="middle"
|
||||||
|
>STROKE TEXT</Text>
|
||||||
|
</Svg>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TextFill extends Component{
|
class TextFill extends Component{
|
||||||
static title = 'Fill the text with LinearGradient';
|
static title = 'Fill the text with LinearGradient';
|
||||||
render() {
|
render() {
|
||||||
@@ -68,8 +96,8 @@ class TextFill extends Component{
|
|||||||
width="200"
|
width="200"
|
||||||
>
|
>
|
||||||
<Defs>
|
<Defs>
|
||||||
<LinearGradient id="grad" x1="0" y1="0" x2="200" y2="0">
|
<LinearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||||
<Stop offset="0%" stopColor="rgb(255,255,0)" stopOpacity="0" />
|
<Stop offset="0%" stopColor="rgb(255,255,0)" stopOpacity="0.5" />
|
||||||
<Stop offset="100%" stopColor="red" stopOpacity="1" />
|
<Stop offset="100%" stopColor="red" stopOpacity="1" />
|
||||||
</LinearGradient>
|
</LinearGradient>
|
||||||
</Defs>
|
</Defs>
|
||||||
@@ -81,40 +109,12 @@ class TextFill extends Component{
|
|||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
x="100"
|
x="100"
|
||||||
y="20"
|
y="20"
|
||||||
textAnchor="center"
|
textAnchor="middle"
|
||||||
>FILL TEXT</Text>
|
>FILL TEXT</Text>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: iOS not support text stroke with pattern
|
|
||||||
class TextStroke extends Component{
|
|
||||||
static title = 'Stroke the text';
|
|
||||||
render() {
|
|
||||||
return <Svg
|
|
||||||
height="60"
|
|
||||||
width="200"
|
|
||||||
>
|
|
||||||
<Defs>
|
|
||||||
<LinearGradient id="grad" x1="0" y1="0" x2="100" y2="0">
|
|
||||||
<Stop offset="100%" stopColor="#fff" stopOpacity="0" />
|
|
||||||
<Stop offset="0%" stopColor="#000" stopOpacity="1" />
|
|
||||||
</LinearGradient>
|
|
||||||
</Defs>
|
|
||||||
<Text
|
|
||||||
stroke="url(#grad)"
|
|
||||||
strokeWidth="2"
|
|
||||||
fill="none"
|
|
||||||
fontSize="30"
|
|
||||||
fontWeight="bold"
|
|
||||||
x="100"
|
|
||||||
y="20"
|
|
||||||
textAnchor="center"
|
|
||||||
>STROKE TEXT</Text>
|
|
||||||
</Svg>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class TextPath extends Component{
|
class TextPath extends Component{
|
||||||
static title = 'Transform the text';
|
static title = 'Transform the text';
|
||||||
@@ -148,13 +148,19 @@ const icon = <Svg
|
|||||||
y="2"
|
y="2"
|
||||||
fontSize="14"
|
fontSize="14"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
textAnchor="center"
|
textAnchor="middle"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="blue"
|
stroke="blue"
|
||||||
>字</Text>
|
>字</Text>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
|
|
||||||
const samples = [TextExample, TextRotate, TextStroke, TextFill, TextPath];
|
const samples = [
|
||||||
|
TextExample,
|
||||||
|
TextRotate,
|
||||||
|
TextStroke,
|
||||||
|
TextFill,
|
||||||
|
TextPath
|
||||||
|
];
|
||||||
|
|
||||||
export {
|
export {
|
||||||
icon,
|
icon,
|
||||||
|
|||||||
@@ -562,15 +562,12 @@ npm install
|
|||||||
4. [morph animations](https://github.com/gorangajic/react-svg-morph)
|
4. [morph animations](https://github.com/gorangajic/react-svg-morph)
|
||||||
5. fix propTypes
|
5. fix propTypes
|
||||||
6. more Text features support
|
6. more Text features support
|
||||||
7. support percent props
|
7. Pattern element
|
||||||
8. Pattern element
|
8. Image element
|
||||||
9. Image element
|
|
||||||
|
|
||||||
|
|
||||||
#### Thanks:
|
#### Thanks:
|
||||||
|
|
||||||
* [SVG bounding Algorithm](https://github.com/icons8/svg-path-bounding-box)
|
|
||||||
* [Circle drawing with svg arc path](http://stackoverflow.com/questions/5737975/circle-drawing-with-svgs-arc-path/10477334#10477334)
|
|
||||||
* [w3schools.com SVG Tutorial](http://www.w3schools.com/svg/)
|
* [w3schools.com SVG Tutorial](http://www.w3schools.com/svg/)
|
||||||
* [SVG Tutorial](http://tutorials.jenkov.com/svg/index.html)
|
* [SVG Tutorial](http://tutorials.jenkov.com/svg/index.html)
|
||||||
* [MDN](https://developer.mozilla.org/en/docs/Web/SVG)
|
* [MDN](https://developer.mozilla.org/en/docs/Web/SVG)
|
||||||
|
|||||||
@@ -9,10 +9,15 @@
|
|||||||
|
|
||||||
package com.horcrux.svg;
|
package com.horcrux.svg;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.facebook.react.bridge.ReadableArray;
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains static helper methods for accessing props.
|
* Contains static helper methods for accessing props.
|
||||||
*/
|
*/
|
||||||
@@ -51,4 +56,20 @@ import com.facebook.react.bridge.ReadableArray;
|
|||||||
return value.size();
|
return value.size();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Converts percentage string into actual based on a relative number
|
||||||
|
*
|
||||||
|
* @param percentage percentage string
|
||||||
|
* @param relative relative number
|
||||||
|
* @return actual float based on relative number
|
||||||
|
*/
|
||||||
|
/*package*/ static float fromPercentageToFloat(String percentage, float relative, float offset, float scale) {
|
||||||
|
Pattern pattern = Pattern.compile("^(\\-?\\d+(?:\\.\\d+)?)%$");
|
||||||
|
Matcher matched = pattern.matcher(percentage);
|
||||||
|
if (matched.matches()) {
|
||||||
|
return Float.valueOf(matched.group(1)) / 100 * relative + offset;
|
||||||
|
} else {
|
||||||
|
return Float.valueOf(percentage) * scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import android.graphics.Paint;
|
|||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.RadialGradient;
|
import android.graphics.RadialGradient;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
@@ -48,9 +49,9 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
private static final int FILL_RULE_EVENODD = 0;
|
private static final int FILL_RULE_EVENODD = 0;
|
||||||
private static final int FILL_RULE_NONZERO = 1;
|
private static final int FILL_RULE_NONZERO = 1;
|
||||||
|
|
||||||
protected @Nullable Path mPath;
|
protected Path mPath;
|
||||||
private @Nullable float[] mStrokeColor;
|
private @Nullable ReadableArray mStrokeColor;
|
||||||
private @Nullable float[] mFillColor;
|
private @Nullable ReadableArray mFillColor;
|
||||||
private @Nullable float[] mStrokeDasharray;
|
private @Nullable float[] mStrokeDasharray;
|
||||||
private float mStrokeWidth = 1;
|
private float mStrokeWidth = 1;
|
||||||
private float mStrokeDashoffset = 0;
|
private float mStrokeDashoffset = 0;
|
||||||
@@ -60,6 +61,7 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
private boolean mFillRuleSet;
|
private boolean mFillRuleSet;
|
||||||
private boolean mPathSet;
|
private boolean mPathSet;
|
||||||
private float[] mShapePath;
|
private float[] mShapePath;
|
||||||
|
protected RectF mContentBoundingBox;
|
||||||
private Point mPaint;
|
private Point mPaint;
|
||||||
|
|
||||||
@ReactProp(name = "d")
|
@ReactProp(name = "d")
|
||||||
@@ -72,7 +74,7 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
|
|
||||||
@ReactProp(name = "fill")
|
@ReactProp(name = "fill")
|
||||||
public void setFill(@Nullable ReadableArray fillColors) {
|
public void setFill(@Nullable ReadableArray fillColors) {
|
||||||
mFillColor = PropHelper.toFloatArray(fillColors);
|
mFillColor = fillColors;
|
||||||
markUpdated();
|
markUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +89,7 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
|
|
||||||
@ReactProp(name = "stroke")
|
@ReactProp(name = "stroke")
|
||||||
public void setStroke(@Nullable ReadableArray strokeColors) {
|
public void setStroke(@Nullable ReadableArray strokeColors) {
|
||||||
mStrokeColor = PropHelper.toFloatArray(strokeColors);
|
mStrokeColor = strokeColors;
|
||||||
markUpdated();
|
markUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,10 +141,10 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
|
|
||||||
clip(canvas, paint);
|
clip(canvas, paint);
|
||||||
|
|
||||||
if (setupFillPaint(paint, opacity)) {
|
if (setupFillPaint(paint, opacity, null)) {
|
||||||
canvas.drawPath(mPath, paint);
|
canvas.drawPath(mPath, paint);
|
||||||
}
|
}
|
||||||
if (setupStrokePaint(paint, opacity)) {
|
if (setupStrokePaint(paint, opacity, null)) {
|
||||||
canvas.drawPath(mPath, paint);
|
canvas.drawPath(mPath, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,21 +170,24 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mPath = super.createPath(mShapePath, path);
|
mPath = super.createPath(mShapePath, path);
|
||||||
|
RectF box = new RectF();
|
||||||
|
mPath.computeBounds(box, true);
|
||||||
|
mContentBoundingBox = box;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* sorting stops and stopsColors from array
|
* sorting stops and stopsColors from array
|
||||||
*/
|
*/
|
||||||
private static void parseGradientStops(float[] value, int stopsCount, float[] stops, int[] stopsColors, int startColorsPosition) {
|
private static void parseGradientStops(ReadableArray value, int stopsCount, float[] stops, int[] stopsColors, int startColorsPosition) {
|
||||||
int startStops = value.length - stopsCount;
|
int startStops = value.size() - stopsCount;
|
||||||
for (int i = 0; i < stopsCount; i++) {
|
for (int i = 0; i < stopsCount; i++) {
|
||||||
stops[i] = value[startStops + i];
|
stops[i] = (float)value.getDouble(startStops + i);
|
||||||
stopsColors[i] = Color.argb(
|
stopsColors[i] = Color.argb(
|
||||||
(int) (value[startColorsPosition + i * 4 + 3] * 255),
|
(int) (value.getDouble(startColorsPosition + i * 4 + 3) * 255),
|
||||||
(int) (value[startColorsPosition + i * 4] * 255),
|
(int) (value.getDouble(startColorsPosition + i * 4) * 255),
|
||||||
(int) (value[startColorsPosition + i * 4 + 1] * 255),
|
(int) (value.getDouble(startColorsPosition + i * 4 + 1) * 255),
|
||||||
(int) (value[startColorsPosition + i * 4 + 2] * 255));
|
(int) (value.getDouble(startColorsPosition + i * 4 + 2) * 255));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,12 +197,12 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
* Sets up {@link #mPaint} according to the props set on a shadow view. Returns {@code true}
|
* 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.
|
* if the fill should be drawn, {@code false} if not.
|
||||||
*/
|
*/
|
||||||
protected boolean setupFillPaint(Paint paint, float opacity) {
|
protected boolean setupFillPaint(Paint paint, float opacity, @Nullable RectF box) {
|
||||||
if (mFillColor != null && mFillColor.length > 0) {
|
if (mFillColor != null && mFillColor.size() > 0) {
|
||||||
paint.reset();
|
paint.reset();
|
||||||
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
paint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||||
paint.setStyle(Paint.Style.FILL);
|
paint.setStyle(Paint.Style.FILL);
|
||||||
setupPaint(paint, opacity, mFillColor);
|
setupPaint(paint, opacity, mFillColor, box);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -207,8 +212,8 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
* Sets up {@link #mPaint} according to the props set on a shadow view. Returns {@code true}
|
* 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.
|
* if the stroke should be drawn, {@code false} if not.
|
||||||
*/
|
*/
|
||||||
protected boolean setupStrokePaint(Paint paint, float opacity) {
|
protected boolean setupStrokePaint(Paint paint, float opacity, @Nullable RectF box) {
|
||||||
if (mStrokeWidth == 0 || mStrokeColor == null || mStrokeColor.length == 0) {
|
if (mStrokeWidth == 0 || mStrokeColor == null || mStrokeColor.size() == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
paint.reset();
|
paint.reset();
|
||||||
@@ -244,8 +249,7 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
paint.setStrokeWidth(mStrokeWidth * mScale);
|
paint.setStrokeWidth(mStrokeWidth * mScale);
|
||||||
|
setupPaint(paint, opacity, mStrokeColor, box);
|
||||||
setupPaint(paint, opacity, mStrokeColor);
|
|
||||||
|
|
||||||
if (mStrokeDasharray != null && mStrokeDasharray.length > 0) {
|
if (mStrokeDasharray != null && mStrokeDasharray.length > 0) {
|
||||||
paint.setPathEffect(new DashPathEffect(mStrokeDasharray, mStrokeDashoffset));
|
paint.setPathEffect(new DashPathEffect(mStrokeDasharray, mStrokeDashoffset));
|
||||||
@@ -255,67 +259,73 @@ public class RNSVGPathShadowNode extends RNSVGVirtualNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void setupPaint(Paint paint, float opacity, float[] colors) {
|
private void setupPaint(Paint paint, float opacity, ReadableArray colors, @Nullable RectF box) {
|
||||||
int stopsCount;
|
int colorType = colors.getInt(0);
|
||||||
int [] stopsColors;
|
if (colorType == 0) {
|
||||||
float [] stops;
|
// solid color
|
||||||
|
paint.setARGB(
|
||||||
|
(int) (colors.size() > 4 ? colors.getDouble(4) * opacity * 255 : opacity * 255),
|
||||||
|
(int) (colors.getDouble(1) * 255),
|
||||||
|
(int) (colors.getDouble(2) * 255),
|
||||||
|
(int) (colors.getDouble(3) * 255));
|
||||||
|
} else if (colorType == 1 || colorType == 2) {
|
||||||
|
if (box == null) {
|
||||||
|
box = mContentBoundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
int colorType = (int) colors[0];
|
int startColorsPosition = colorType == 1 ? 5 : 7;
|
||||||
switch (colorType) {
|
|
||||||
case 0:
|
|
||||||
paint.setARGB(
|
|
||||||
(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 = (colors.length - 5) / 5;
|
|
||||||
stopsColors = new int [stopsCount];
|
|
||||||
stops = new float[stopsCount];
|
|
||||||
|
|
||||||
parseGradientStops(colors, stopsCount, stops, stopsColors, 5);
|
int stopsCount = (colors.size() - startColorsPosition) / 5;
|
||||||
|
int [] stopsColors = new int [stopsCount];
|
||||||
|
float [] stops = new float[stopsCount];
|
||||||
|
float height = box.height();
|
||||||
|
float width = box.width();
|
||||||
|
float midX = box.centerX();
|
||||||
|
float midY = box.centerY();
|
||||||
|
float offsetX = (midX - width / 2);
|
||||||
|
float offsetY = (midY - height / 2);
|
||||||
|
|
||||||
|
parseGradientStops(colors, stopsCount, stops, stopsColors, startColorsPosition);
|
||||||
|
|
||||||
|
if (colorType == 1) {
|
||||||
|
float x1 = PropHelper.fromPercentageToFloat(colors.getString(1), width, offsetX, mScale);
|
||||||
|
float y1 = PropHelper.fromPercentageToFloat(colors.getString(2), height, offsetY, mScale);
|
||||||
|
float x2 = PropHelper.fromPercentageToFloat(colors.getString(3), width, offsetX, mScale);
|
||||||
|
float y2 = PropHelper.fromPercentageToFloat(colors.getString(4), height, offsetY, mScale);
|
||||||
paint.setShader(
|
paint.setShader(
|
||||||
new LinearGradient(
|
new LinearGradient(
|
||||||
colors[1] * mScale,
|
x1,
|
||||||
colors[2] * mScale,
|
y1,
|
||||||
colors[3] * mScale,
|
x2,
|
||||||
colors[4] * mScale,
|
y2,
|
||||||
stopsColors,
|
stopsColors,
|
||||||
stops,
|
stops,
|
||||||
Shader.TileMode.CLAMP));
|
Shader.TileMode.CLAMP));
|
||||||
break;
|
} else {
|
||||||
case 2:
|
float rx = PropHelper.fromPercentageToFloat(colors.getString(3), width, 0f, mScale);
|
||||||
stopsCount = (colors.length - 7) / 5;
|
float ry = PropHelper.fromPercentageToFloat(colors.getString(4), height, 0f, mScale);
|
||||||
stopsColors = new int [stopsCount];
|
float cx = PropHelper.fromPercentageToFloat(colors.getString(5), width, offsetX, mScale);
|
||||||
stops = new float[stopsCount];
|
float cy = PropHelper.fromPercentageToFloat(colors.getString(6), height, offsetY, mScale) / (ry / rx);
|
||||||
parseGradientStops(colors, stopsCount, stops, stopsColors, 7);
|
// TODO: do not support focus point.
|
||||||
|
float fx = PropHelper.fromPercentageToFloat(colors.getString(1), width, offsetX, mScale);
|
||||||
// TODO: support focus
|
float fy = PropHelper.fromPercentageToFloat(colors.getString(2), height, offsetY, mScale) / (ry / rx);
|
||||||
float focusX = colors[1];
|
|
||||||
float focusY = colors[2];
|
|
||||||
|
|
||||||
float radius = colors[3];
|
|
||||||
float radiusRatio = colors[4] / radius;
|
|
||||||
Shader radialGradient = new RadialGradient(
|
Shader radialGradient = new RadialGradient(
|
||||||
colors[5] * mScale,
|
cx,
|
||||||
colors[6] * mScale / radiusRatio,
|
cy,
|
||||||
radius * mScale,
|
rx,
|
||||||
stopsColors,
|
stopsColors,
|
||||||
stops,
|
stops,
|
||||||
Shader.TileMode.CLAMP
|
Shader.TileMode.CLAMP
|
||||||
);
|
);
|
||||||
|
|
||||||
Matrix radialMatrix = new Matrix();
|
Matrix radialMatrix = new Matrix();
|
||||||
|
radialMatrix.preScale(1f, ry / rx);
|
||||||
// seems like a bug here?
|
|
||||||
radialMatrix.preScale(1f, radiusRatio);
|
|
||||||
radialGradient.setLocalMatrix(radialMatrix);
|
radialGradient.setLocalMatrix(radialMatrix);
|
||||||
paint.setShader(radialGradient);
|
paint.setShader(radialGradient);
|
||||||
break;
|
}
|
||||||
default:
|
} else {
|
||||||
// TODO: Support pattern.
|
// TODO: Support pattern.
|
||||||
FLog.w(ReactConstants.TAG, "RNSVG: Color type " + colorType + " not supported!");
|
FLog.w(ReactConstants.TAG, "RNSVG: Color type " + colorType + " not supported!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public class RNSVGRenderableViewManager extends ViewManager<View, ReactShadowNod
|
|||||||
/* package */ static final String CLASS_GROUP = "RNSVGGroup";
|
/* package */ static final String CLASS_GROUP = "RNSVGGroup";
|
||||||
/* package */ static final String CLASS_SVG = "RNSVGPath";
|
/* package */ static final String CLASS_SVG = "RNSVGPath";
|
||||||
/* package */ static final String CLASS_TEXT = "RNSVGText";
|
/* package */ static final String CLASS_TEXT = "RNSVGText";
|
||||||
|
/* package */ static final String CLASS_SHAPE = "RNSVGShape";
|
||||||
|
|
||||||
private final String mClassName;
|
private final String mClassName;
|
||||||
|
|
||||||
@@ -42,6 +43,10 @@ public class RNSVGRenderableViewManager extends ViewManager<View, ReactShadowNod
|
|||||||
return new RNSVGRenderableViewManager(CLASS_TEXT);
|
return new RNSVGRenderableViewManager(CLASS_TEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RNSVGRenderableViewManager createRNSVGShapeViewManager() {
|
||||||
|
return new RNSVGRenderableViewManager(CLASS_SHAPE);
|
||||||
|
}
|
||||||
|
|
||||||
private RNSVGRenderableViewManager(String className) {
|
private RNSVGRenderableViewManager(String className) {
|
||||||
mClassName = className;
|
mClassName = className;
|
||||||
}
|
}
|
||||||
@@ -57,6 +62,8 @@ public class RNSVGRenderableViewManager extends ViewManager<View, ReactShadowNod
|
|||||||
return new RNSVGGroupShadowNode();
|
return new RNSVGGroupShadowNode();
|
||||||
} else if (mClassName == CLASS_SVG) {
|
} else if (mClassName == CLASS_SVG) {
|
||||||
return new RNSVGPathShadowNode();
|
return new RNSVGPathShadowNode();
|
||||||
|
} else if (mClassName == CLASS_SHAPE) {
|
||||||
|
return new RNSVGShapeShadowNode();
|
||||||
} else if (mClassName == CLASS_TEXT) {
|
} else if (mClassName == CLASS_TEXT) {
|
||||||
return new RNSVGTextShadowNode();
|
return new RNSVGTextShadowNode();
|
||||||
} else {
|
} else {
|
||||||
@@ -70,9 +77,11 @@ public class RNSVGRenderableViewManager extends ViewManager<View, ReactShadowNod
|
|||||||
return RNSVGGroupShadowNode.class;
|
return RNSVGGroupShadowNode.class;
|
||||||
} else if (mClassName == CLASS_SVG) {
|
} else if (mClassName == CLASS_SVG) {
|
||||||
return RNSVGPathShadowNode.class;
|
return RNSVGPathShadowNode.class;
|
||||||
|
} else if (mClassName == CLASS_SHAPE) {
|
||||||
|
return RNSVGShapeShadowNode.class;
|
||||||
} else if (mClassName == CLASS_TEXT) {
|
} else if (mClassName == CLASS_TEXT) {
|
||||||
return RNSVGTextShadowNode.class;
|
return RNSVGTextShadowNode.class;
|
||||||
} else {
|
}else {
|
||||||
throw new IllegalStateException("Unexpected type " + mClassName);
|
throw new IllegalStateException("Unexpected type " + mClassName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
143
android/src/main/java/com/horcrux/svg/RNSVGShapeShadowNode.java
Normal file
143
android/src/main/java/com/horcrux/svg/RNSVGShapeShadowNode.java
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Horcrux.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.horcrux.svg;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.facebook.common.logging.FLog;
|
||||||
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
|
import com.facebook.react.common.ReactConstants;
|
||||||
|
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shadow node for virtual RNSVGPath view
|
||||||
|
*/
|
||||||
|
public class RNSVGShapeShadowNode extends RNSVGPathShadowNode {
|
||||||
|
|
||||||
|
protected ReadableMap mShape;
|
||||||
|
|
||||||
|
@ReactProp(name = "shape")
|
||||||
|
public void setShape(@Nullable ReadableMap shape) {
|
||||||
|
mShape = shape;
|
||||||
|
markUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(Canvas canvas, Paint paint, float opacity) {
|
||||||
|
|
||||||
|
if (mShape != null) {
|
||||||
|
int type = mShape.getInt("type");
|
||||||
|
Rect box = canvas.getClipBounds();
|
||||||
|
float height = box.height();
|
||||||
|
float width = box.width();
|
||||||
|
mPath = new Path();
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 0: {
|
||||||
|
// draw circle
|
||||||
|
// TODO:
|
||||||
|
float cx = getActualProp("cx", width);
|
||||||
|
float cy = getActualProp("cy", height);
|
||||||
|
|
||||||
|
float r;
|
||||||
|
ReadableMap value = mShape.getMap("r");
|
||||||
|
if (value.getBoolean("percentage")) {
|
||||||
|
float percent = (float)value.getDouble("value");
|
||||||
|
float powX = (float)Math.pow((width * percent), 2);
|
||||||
|
float powY = (float)Math.pow((height*percent), 2);
|
||||||
|
r = (float)Math.sqrt(powX + powY) / (float)Math.sqrt(2);
|
||||||
|
} else {
|
||||||
|
r = (float)value.getDouble("value") * mScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPath.addCircle(cx, cy, r, Path.Direction.CW);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
// draw ellipse
|
||||||
|
float cx = getActualProp("cx", width);
|
||||||
|
float cy = getActualProp("cy", height);
|
||||||
|
float rx = getActualProp("rx", width);
|
||||||
|
float ry = getActualProp("ry", height);
|
||||||
|
RectF oval = new RectF(cx - rx, cy - ry, cx + rx, cy + ry);
|
||||||
|
mPath.addOval(oval, Path.Direction.CW);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
// draw line
|
||||||
|
float x1 = getActualProp("x1", width);
|
||||||
|
float y1 = getActualProp("y1", height);
|
||||||
|
float x2 = getActualProp("x2", width);
|
||||||
|
float y2 = getActualProp("y2", height);
|
||||||
|
mPath.moveTo(x1, y1);
|
||||||
|
mPath.lineTo(x2, y2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: {
|
||||||
|
// draw rect
|
||||||
|
float x = getActualProp("x", width);
|
||||||
|
float y = getActualProp("y", height);
|
||||||
|
float w = getActualProp("width", width);
|
||||||
|
float h = getActualProp("height", height);
|
||||||
|
float rx = getActualProp("rx", width);
|
||||||
|
float ry = getActualProp("ry", height);
|
||||||
|
|
||||||
|
if (rx != 0 || ry != 0) {
|
||||||
|
if (rx == 0) {
|
||||||
|
rx = ry;
|
||||||
|
} else if (ry == 0) {
|
||||||
|
ry = rx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx > w / 2) {
|
||||||
|
rx = w / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ry > h / 2) {
|
||||||
|
ry = h / 2;
|
||||||
|
}
|
||||||
|
mPath.addRoundRect(new RectF(x, y, x + w, y + h), rx, ry, Path.Direction.CW);
|
||||||
|
} else {
|
||||||
|
mPath.addRect(x, y, x + w, y + h, Path.Direction.CW);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
FLog.e(ReactConstants.TAG, "RNSVG: Invalid Shape type " + type + " at " + mShape);
|
||||||
|
}
|
||||||
|
|
||||||
|
RectF shapeBox = new RectF();
|
||||||
|
mPath.computeBounds(shapeBox, true);
|
||||||
|
mContentBoundingBox = shapeBox;
|
||||||
|
super.draw(canvas, paint, opacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getActualProp(String name, float relative) {
|
||||||
|
if (mShape.hasKey(name)) {
|
||||||
|
ReadableMap value = mShape.getMap(name);
|
||||||
|
|
||||||
|
if (value.getBoolean("percentage")) {
|
||||||
|
return (float)value.getDouble("value") * relative;
|
||||||
|
} else {
|
||||||
|
return (float)value.getDouble("value") * mScale;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,8 +14,11 @@ import javax.annotation.Nullable;
|
|||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.RectF;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||||
import com.facebook.react.bridge.ReadableArray;
|
import com.facebook.react.bridge.ReadableArray;
|
||||||
@@ -89,7 +92,11 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode {
|
|||||||
lines[i] = linesProp.getString(i);
|
lines[i] = linesProp.getString(i);
|
||||||
}
|
}
|
||||||
String text = TextUtils.join("\n", lines);
|
String text = TextUtils.join("\n", lines);
|
||||||
if (setupStrokePaint(paint, opacity)) {
|
|
||||||
|
Rect bound = new Rect();
|
||||||
|
paint.getTextBounds(text, 0, text.length(), bound);
|
||||||
|
RectF box = new RectF(bound);
|
||||||
|
if (setupStrokePaint(paint, opacity, box)) {
|
||||||
applyTextPropertiesToPaint(paint);
|
applyTextPropertiesToPaint(paint);
|
||||||
if (mPath == null) {
|
if (mPath == null) {
|
||||||
canvas.drawText(text, 0, -paint.ascent(), paint);
|
canvas.drawText(text, 0, -paint.ascent(), paint);
|
||||||
@@ -97,7 +104,7 @@ public class RNSVGTextShadowNode extends RNSVGPathShadowNode {
|
|||||||
canvas.drawTextOnPath(text, mPath, 0, 0, paint);
|
canvas.drawTextOnPath(text, mPath, 0, 0, paint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (setupFillPaint(paint, opacity)) {
|
if (setupFillPaint(paint, opacity, box)) {
|
||||||
applyTextPropertiesToPaint(paint);
|
applyTextPropertiesToPaint(paint);
|
||||||
if (mPath == null) {
|
if (mPath == null) {
|
||||||
canvas.drawText(text, 0, -paint.ascent(), paint);
|
canvas.drawText(text, 0, -paint.ascent(), paint);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ public class RNSvgPackage implements ReactPackage {
|
|||||||
RNSVGRenderableViewManager.createRNSVGGroupViewManager(),
|
RNSVGRenderableViewManager.createRNSVGGroupViewManager(),
|
||||||
RNSVGRenderableViewManager.createRNSVGPathViewManager(),
|
RNSVGRenderableViewManager.createRNSVGPathViewManager(),
|
||||||
RNSVGRenderableViewManager.createRNSVGTextViewManager(),
|
RNSVGRenderableViewManager.createRNSVGTextViewManager(),
|
||||||
|
RNSVGRenderableViewManager.createRNSVGShapeViewManager(),
|
||||||
new RNSVGSvgViewManager());
|
new RNSVGSvgViewManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,34 +2,28 @@ import React, {
|
|||||||
Component,
|
Component,
|
||||||
PropTypes
|
PropTypes
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Ellipse from './Ellipse';
|
import Shape, {CIRCLE} from './Shape';
|
||||||
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
import {circleProps, pathProps, fillProps, strokeProps} from '../lib/props';
|
||||||
class Circle extends Component{
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
class Circle extends Shape{
|
||||||
static displayName = 'Circle';
|
static displayName = 'Circle';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
cx: propType,
|
...pathProps,
|
||||||
cy: propType,
|
...circleProps
|
||||||
r: propType
|
|
||||||
};
|
|
||||||
static defaultProps = {
|
|
||||||
cx: 0,
|
|
||||||
cy: 0
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static getPath = props => Ellipse.getPath({
|
static contextTypes = {
|
||||||
cx: props.cx,
|
...fillProps,
|
||||||
cy: props.cy,
|
...strokeProps,
|
||||||
rx: props.r,
|
...circleProps,
|
||||||
ry: props.r
|
isInGroup: PropTypes.bool
|
||||||
});
|
};
|
||||||
|
|
||||||
render() {
|
constructor() {
|
||||||
return <Ellipse
|
super(...arguments);
|
||||||
{...this.props}
|
this.type = CIRCLE;
|
||||||
rx={+this.props.r}
|
};
|
||||||
ry={+this.props.r}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Circle;
|
export default Circle;
|
||||||
|
|||||||
@@ -61,11 +61,10 @@ class DefsUse extends Component{
|
|||||||
if (matched) {
|
if (matched) {
|
||||||
let template = map[matched[1] + ':' + this.props.svgId];
|
let template = map[matched[1] + ':' + this.props.svgId];
|
||||||
if (template) {
|
if (template) {
|
||||||
let props = {
|
return cloneElement(template, {
|
||||||
...this.props,
|
...this.props,
|
||||||
href: null
|
href: null
|
||||||
};
|
});
|
||||||
return cloneElement(template, props);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,35 +2,27 @@ import React, {
|
|||||||
Component,
|
Component,
|
||||||
PropTypes
|
PropTypes
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Path from './Path';
|
import Shape, {ELLIPSE} from './Shape';
|
||||||
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
import {ellipseProps, pathProps, fillProps, strokeProps} from '../lib/props';
|
||||||
class Ellipse extends Component{
|
|
||||||
|
class Ellipse extends Shape{
|
||||||
static displayName = 'Ellipse';
|
static displayName = 'Ellipse';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
cx: propType,
|
...pathProps,
|
||||||
cy: propType,
|
...ellipseProps
|
||||||
rx: propType,
|
|
||||||
ry: propType
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static getPath = props => {
|
static contextTypes = {
|
||||||
let {cx, cy, rx, ry} = props;
|
...fillProps,
|
||||||
return `
|
...strokeProps,
|
||||||
M ${cx - rx} ${cy}
|
...ellipseProps,
|
||||||
a ${rx}, ${ry} 0 1, 0 ${rx * 2}, 0
|
isInGroup: PropTypes.bool
|
||||||
a ${rx}, ${ry} 0 1, 0 ${-rx * 2}, 0
|
|
||||||
Z
|
|
||||||
`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
constructor() {
|
||||||
let {props} = this;
|
super(...arguments);
|
||||||
let d = Ellipse.getPath(this.props);
|
this.type = ELLIPSE;
|
||||||
return <Path
|
};
|
||||||
{...props}
|
|
||||||
d={d}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Ellipse;
|
export default Ellipse;
|
||||||
|
|||||||
@@ -2,42 +2,34 @@ import React, {
|
|||||||
Component,
|
Component,
|
||||||
Children,
|
Children,
|
||||||
cloneElement,
|
cloneElement,
|
||||||
|
PropTypes,
|
||||||
requireNativeComponent
|
requireNativeComponent
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass';
|
import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass';
|
||||||
import Defs from './Defs';
|
import Defs from './Defs';
|
||||||
|
import _ from 'lodash';
|
||||||
import {GroupAttributes} from '../lib/attributes';
|
import {GroupAttributes} from '../lib/attributes';
|
||||||
|
import {numberProp, contextProps, textProps} from '../lib/props';
|
||||||
const transformProps = {
|
|
||||||
scale: null,
|
|
||||||
scaleX: null,
|
|
||||||
scaleY: null,
|
|
||||||
rotate: null,
|
|
||||||
transform: null,
|
|
||||||
x: null,
|
|
||||||
y: null,
|
|
||||||
originX: null,
|
|
||||||
originY: null
|
|
||||||
};
|
|
||||||
|
|
||||||
const clipProps = {
|
|
||||||
clipPath: null,
|
|
||||||
clipRule: null
|
|
||||||
};
|
|
||||||
|
|
||||||
import extractProps from '../lib/extract/extractProps';
|
import extractProps from '../lib/extract/extractProps';
|
||||||
|
|
||||||
class G extends Component{
|
class G extends Component{
|
||||||
static displayName = 'G';
|
static displayName = 'G';
|
||||||
|
|
||||||
getChildren = () => {
|
static childContextTypes = {
|
||||||
return Children.map(this.props.children, child => cloneElement(child, {
|
svgId: numberProp,
|
||||||
...this.props,
|
isInGroup: PropTypes.bool,
|
||||||
...transformProps,
|
...contextProps
|
||||||
...clipProps,
|
};
|
||||||
...child.props,
|
|
||||||
id: null
|
getChildContext = () => {
|
||||||
}));
|
return _.reduce(contextProps, (props, value, key) => {
|
||||||
|
props[key] = this.props[key];
|
||||||
|
return props;
|
||||||
|
}, {
|
||||||
|
svgId: this.props.svgId,
|
||||||
|
isInGroup: true
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -56,7 +48,7 @@ class G extends Component{
|
|||||||
return <NativeGroup
|
return <NativeGroup
|
||||||
{...extractProps(this.props, {transform: true})}
|
{...extractProps(this.props, {transform: true})}
|
||||||
>
|
>
|
||||||
{this.getChildren()}
|
{this.props.children}
|
||||||
</NativeGroup>;
|
</NativeGroup>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import React, {
|
|||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {NativeGroup} from './G';
|
import {NativeGroup} from './G';
|
||||||
import {set, remove} from '../lib/extract/patterns';
|
import {set, remove} from '../lib/extract/patterns';
|
||||||
import percentFactory from '../lib/percentFactory';
|
|
||||||
import percentToFloat from '../lib/percentToFloat';
|
import percentToFloat from '../lib/percentToFloat';
|
||||||
import Stop from './Stop';
|
import Stop from './Stop';
|
||||||
import Color from 'color';
|
import Color from 'color';
|
||||||
@@ -28,7 +27,7 @@ class RadialGradient extends Component{
|
|||||||
remove(this.id);
|
remove(this.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
render(distanceProps, fromPercent, fromNumber) {
|
render(generator) {
|
||||||
let stops = {};
|
let stops = {};
|
||||||
Children.forEach(this.props.children, child => {
|
Children.forEach(this.props.children, child => {
|
||||||
if (child.type === Stop && child.props.stopColor && child.props.offset) {
|
if (child.type === Stop && child.props.stopColor && child.props.offset) {
|
||||||
@@ -37,13 +36,7 @@ class RadialGradient extends Component{
|
|||||||
|
|
||||||
// add stop
|
// add stop
|
||||||
stops[offset] = Color(child.props.stopColor).alpha(+child.props.stopOpacity);
|
stops[offset] = Color(child.props.stopColor).alpha(+child.props.stopOpacity);
|
||||||
|
set(this.id, generator.bind(null, stops));
|
||||||
let factories = percentFactory(...distanceProps);
|
|
||||||
if (factories) {
|
|
||||||
set(this.id, fromPercent.bind(null, factories, stops));
|
|
||||||
} else {
|
|
||||||
set(this.id, fromNumber.bind(null, stops));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
console.warn(`'RadialGradient' can only receive 'Stop' elements as children`);
|
console.warn(`'RadialGradient' can only receive 'Stop' elements as children`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,30 +2,27 @@ import React, {
|
|||||||
Component,
|
Component,
|
||||||
PropTypes
|
PropTypes
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Path from './Path';
|
import Shape, {LINE} from './Shape';
|
||||||
|
import {lineProps, pathProps, fillProps, strokeProps} from '../lib/props';
|
||||||
|
|
||||||
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
class Line extends Shape{
|
||||||
|
|
||||||
class Line extends Component{
|
|
||||||
static displayName = 'Line';
|
static displayName = 'Line';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
x1: propType,
|
...pathProps,
|
||||||
x2: propType,
|
...lineProps
|
||||||
y1: propType,
|
|
||||||
y2: propType,
|
|
||||||
strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round'])
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static getPath = (props) => (
|
static contextTypes = {
|
||||||
`M${props.x1},${props.y1} L${props.x2},${props.y2}`
|
...fillProps,
|
||||||
);
|
...strokeProps,
|
||||||
|
...lineProps,
|
||||||
|
isInGroup: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
constructor() {
|
||||||
return <Path
|
super(...arguments);
|
||||||
{...this.props}
|
this.type = LINE;
|
||||||
d={Line.getPath(this.props)}
|
};
|
||||||
/>;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Line;
|
export default Line;
|
||||||
|
|||||||
@@ -5,32 +5,13 @@ import React, {
|
|||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
|
||||||
import stopsOpacity from '../lib/stopsOpacity';
|
import stopsOpacity from '../lib/stopsOpacity';
|
||||||
import numberProp from '../lib/numberProp';
|
import {numberProp} from '../lib/props';
|
||||||
import Gradient from './Gradient';
|
import Gradient from './Gradient';
|
||||||
import {LINEAR_GRADIENT} from '../lib/extract/extractBrush';
|
import {LINEAR_GRADIENT} from '../lib/extract/extractBrush';
|
||||||
import insertColorStopsIntoArray from '../lib/insertProcessor';
|
import insertColorStopsIntoArray from '../lib/insertProcessor';
|
||||||
|
|
||||||
function LinearGradientGenerator(stops, x1, y1, x2, y2) {
|
function LinearGradientGenerator(stops, x1, y1, x2, y2) {
|
||||||
var type = LINEAR_GRADIENT;
|
var brushData = [LINEAR_GRADIENT, x1, y1, x2, y2];
|
||||||
|
|
||||||
if (arguments.length < 5) {
|
|
||||||
var angle = ((x1 == null) ? 270 : x1) * Math.PI / 180;
|
|
||||||
|
|
||||||
var x = Math.cos(angle);
|
|
||||||
var y = -Math.sin(angle);
|
|
||||||
var l = (Math.abs(x) + Math.abs(y)) / 2;
|
|
||||||
|
|
||||||
x *= l; y *= l;
|
|
||||||
|
|
||||||
x1 = 0.5 - x;
|
|
||||||
x2 = 0.5 + x;
|
|
||||||
y1 = 0.5 - y;
|
|
||||||
y2 = 0.5 + y;
|
|
||||||
this._bb = true;
|
|
||||||
} else {
|
|
||||||
this._bb = false;
|
|
||||||
}
|
|
||||||
var brushData = [type, +x1, +y1, +x2, +y2];
|
|
||||||
insertColorStopsIntoArray(stops, brushData, 5);
|
insertColorStopsIntoArray(stops, brushData, 5);
|
||||||
this._brush = brushData;
|
this._brush = brushData;
|
||||||
}
|
}
|
||||||
@@ -53,20 +34,9 @@ class LinearGradient extends Gradient{
|
|||||||
x2,
|
x2,
|
||||||
y2
|
y2
|
||||||
} = this.props;
|
} = this.props;
|
||||||
let gradientProps = [x1, y1, x2, y2];
|
|
||||||
return super.render(
|
return super.render(
|
||||||
gradientProps,
|
(stops, opacity) => {
|
||||||
function (factories, stops, boundingBox, opacity) {
|
return new LinearGradientGenerator(stopsOpacity(stops, opacity), ...[x1, y1, x2, y2]);
|
||||||
return new LinearGradientGenerator(
|
|
||||||
stopsOpacity(stops, opacity),
|
|
||||||
factories[0](boundingBox.width),
|
|
||||||
factories[1](boundingBox.height),
|
|
||||||
factories[2](boundingBox.width),
|
|
||||||
factories[3](boundingBox.height)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
function (stops, opacity) {
|
|
||||||
return new LinearGradientGenerator(stopsOpacity(stops, opacity), ...gradientProps);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,31 @@
|
|||||||
import React, {
|
import React, {
|
||||||
Component,
|
Component,
|
||||||
PropTypes,
|
PropTypes,
|
||||||
requireNativeComponent,
|
requireNativeComponent
|
||||||
cloneElement
|
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
|
||||||
import Defs from './Defs';
|
import Defs from './Defs';
|
||||||
import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass';
|
import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass';
|
||||||
import calculateBoundingBox from '../lib/calculateBoundingBox';
|
|
||||||
import extractProps from '../lib/extract/extractProps';
|
import extractProps from '../lib/extract/extractProps';
|
||||||
import SerializablePath from 'react-native/Libraries/ART/ARTSerializablePath';
|
import SerializablePath from 'react-native/Libraries/ART/ARTSerializablePath';
|
||||||
import {PathAttributes} from '../lib/attributes';
|
import {PathAttributes} from '../lib/attributes';
|
||||||
|
import {pathProps} from '../lib/props';
|
||||||
|
|
||||||
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
||||||
class Path extends Component{
|
class Path extends Component{
|
||||||
static displayName = 'Path';
|
static displayName = 'Path';
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
d: PropTypes.string,
|
d: PropTypes.string,
|
||||||
x: propType,
|
...pathProps
|
||||||
y: propType,
|
};
|
||||||
strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']),
|
|
||||||
strokeCap: PropTypes.oneOf(['butt', 'square', 'round']),
|
static contextTypes = {
|
||||||
strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round']),
|
...pathProps,
|
||||||
strokeJoin: PropTypes.oneOf(['miter', 'bevel', 'round']),
|
isInGroup: PropTypes.bool
|
||||||
strokeDasharray: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.number)])
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static getPath = props => props.d;
|
|
||||||
|
|
||||||
_dimensions = null;
|
_dimensions = null;
|
||||||
|
|
||||||
@@ -38,16 +35,15 @@ class Path extends Component{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getBoundingBox = () => {
|
|
||||||
if (!this._dimensions) {
|
|
||||||
this._dimensions = calculateBoundingBox(this.props.d);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._dimensions;
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {props} = this;
|
let {props} = this;
|
||||||
|
|
||||||
|
if (this.context.isInGroup) {
|
||||||
|
props = _.defaults(this.context, props, {
|
||||||
|
isInGroup: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (props.id) {
|
if (props.id) {
|
||||||
return <Defs.Item
|
return <Defs.Item
|
||||||
id={props.id}
|
id={props.id}
|
||||||
@@ -62,7 +58,7 @@ class Path extends Component{
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<NativePath
|
<NativePath
|
||||||
{...extractProps.call(this, props)}
|
{...extractProps(props)}
|
||||||
d={d}
|
d={d}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,36 +4,15 @@ import React, {
|
|||||||
Children
|
Children
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import stopsOpacity from '../lib/stopsOpacity';
|
import stopsOpacity from '../lib/stopsOpacity';
|
||||||
import numberProp from '../lib/numberProp';
|
import {numberProp} from '../lib/props';
|
||||||
import Gradient from './Gradient';
|
import Gradient from './Gradient';
|
||||||
import {RADIAL_GRADIENT} from '../lib/extract/extractBrush';
|
import {RADIAL_GRADIENT} from '../lib/extract/extractBrush';
|
||||||
import insertColorStopsIntoArray from '../lib/insertProcessor';
|
import insertColorStopsIntoArray from '../lib/insertProcessor';
|
||||||
|
|
||||||
|
|
||||||
function RadialGradientGenerator(stops, fx, fy, rx, ry, cx, cy) {
|
function RadialGradientGenerator(stops, fx, fy, rx, ry, cx, cy) {
|
||||||
if (ry == null) {
|
var brushData = [RADIAL_GRADIENT, fx, fy, rx, ry, cx, cy];
|
||||||
ry = rx;
|
insertColorStopsIntoArray(stops, brushData, 7);
|
||||||
}
|
|
||||||
if (cx == null) {
|
|
||||||
cx = fx;
|
|
||||||
}
|
|
||||||
if (cy == null) {
|
|
||||||
cy = fy;
|
|
||||||
}
|
|
||||||
if (fx == null) {
|
|
||||||
// As a convenience we allow the whole radial gradient to cover the
|
|
||||||
// bounding box. We should consider dropping this API.
|
|
||||||
fx = fy = rx = ry = cx = cy = 0.5;
|
|
||||||
this._bb = true;
|
|
||||||
} else {
|
|
||||||
this._bb = false;
|
|
||||||
}
|
|
||||||
// The ART API expects the radial gradient to be repeated at the edges.
|
|
||||||
// To simulate this we render the gradient twice as large and add double
|
|
||||||
// color stops. Ideally this API would become more restrictive so that this
|
|
||||||
// extra work isn't needed.
|
|
||||||
var brushData = [RADIAL_GRADIENT, +fx, +fy, +rx * 2, +ry * 2, +cx, +cy];
|
|
||||||
insertColorStopsIntoArray(stops, brushData, 7, 0.5);
|
|
||||||
this._brush = brushData;
|
this._brush = brushData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,24 +39,10 @@ class RadialGradient extends Gradient{
|
|||||||
cy,
|
cy,
|
||||||
r
|
r
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let gradientProps = [fx, fy, rx || r, ry || r, cx, cy];
|
|
||||||
return super.render(
|
return super.render(
|
||||||
gradientProps,
|
(stops, opacity) => {
|
||||||
function (factories, stops, boundingBox, opacity) {
|
|
||||||
let {x1,y1,width, height} = boundingBox;
|
return new RadialGradientGenerator(stopsOpacity(stops, opacity), ...[fx, fy, rx || r, ry || r, cx, cy]);
|
||||||
return new RadialGradientGenerator(
|
|
||||||
stopsOpacity(stops, opacity),
|
|
||||||
x1 + factories[0](width),
|
|
||||||
y1 + factories[1](height),
|
|
||||||
factories[2](width),
|
|
||||||
factories[3](height),
|
|
||||||
x1 + factories[4](width),
|
|
||||||
y1 + factories[5](height)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
function (stops, opacity) {
|
|
||||||
return new RadialGradientGenerator(stopsOpacity(stops, opacity), ...gradientProps);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
122
elements/Rect.js
122
elements/Rect.js
@@ -2,115 +2,29 @@ import React, {
|
|||||||
Component,
|
Component,
|
||||||
PropTypes
|
PropTypes
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
import Shape, {RECT} from './Shape';
|
||||||
|
import {rectProps, pathProps, fillProps, strokeProps} from '../lib/props';
|
||||||
|
|
||||||
import Path from './Path';
|
class Rect extends Shape{
|
||||||
|
|
||||||
let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
|
||||||
|
|
||||||
function processRadius(radius) {
|
|
||||||
radius = +radius;
|
|
||||||
return radius || 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getR(props) {
|
|
||||||
let {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
rx,
|
|
||||||
ry
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
rx = processRadius(rx);
|
|
||||||
ry = processRadius(ry);
|
|
||||||
|
|
||||||
if ((rx && !ry) || (ry && !rx)) {
|
|
||||||
if (rx) {
|
|
||||||
ry = rx;
|
|
||||||
} else {
|
|
||||||
rx = ry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rx > width / 2) {
|
|
||||||
rx = width / 2;
|
|
||||||
}
|
|
||||||
if (ry > height / 2) {
|
|
||||||
ry = height / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
rx,
|
|
||||||
ry
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
class Rect extends Component{
|
|
||||||
static displayName = 'Rect';
|
static displayName = 'Rect';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
x: propType,
|
...pathProps,
|
||||||
y: propType,
|
...rectProps
|
||||||
width: propType,
|
|
||||||
height: propType,
|
|
||||||
rx: propType,
|
|
||||||
ry: propType,
|
|
||||||
strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']),
|
|
||||||
strokeCap: PropTypes.oneOf(['butt', 'square', 'round']),
|
|
||||||
strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round']),
|
|
||||||
strokeJoin: PropTypes.oneOf(['miter', 'bevel', 'round'])
|
|
||||||
};
|
|
||||||
static defaultProps = {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
rx: 0,
|
|
||||||
ry: 0
|
|
||||||
};
|
|
||||||
static getPath = (props, r) => {
|
|
||||||
let {
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
if (!r) {
|
|
||||||
r = getR(props);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
let {rx, ry} = r;
|
|
||||||
|
|
||||||
|
|
||||||
return (rx || ry) ? `
|
|
||||||
M ${x}, ${y}
|
|
||||||
h ${width - 2 * rx}
|
|
||||||
a ${rx},${ry} 0 0 1 ${rx},${ry}
|
|
||||||
v ${height - 2 * ry}
|
|
||||||
a ${rx},${ry} 0 0 1 ${-rx},${ry}
|
|
||||||
h ${2 * rx - width}
|
|
||||||
a ${rx},${ry} 0 0 1 ${-rx},${-ry}
|
|
||||||
v ${2 * ry - height}
|
|
||||||
a ${rx},${ry} 0 0 1 ${rx},${-ry}
|
|
||||||
Z
|
|
||||||
` : `
|
|
||||||
M ${x}, ${y}
|
|
||||||
h ${width}
|
|
||||||
v ${height}
|
|
||||||
h ${-width}
|
|
||||||
Z
|
|
||||||
`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
static contextTypes = {
|
||||||
let r = getR(this.props);
|
...fillProps,
|
||||||
return <Path
|
...strokeProps,
|
||||||
{...this.props}
|
...rectProps,
|
||||||
width={null}
|
isInGroup: PropTypes.bool
|
||||||
height={null}
|
};
|
||||||
x={r.rx || null}
|
|
||||||
y={null}
|
constructor() {
|
||||||
d={Rect.getPath(this.props, r)}
|
super(...arguments);
|
||||||
/>;
|
this.type = RECT;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Rect;
|
export default Rect;
|
||||||
|
|||||||
87
elements/Shape.js
Normal file
87
elements/Shape.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import React, {
|
||||||
|
Component,
|
||||||
|
PropTypes
|
||||||
|
} from 'react-native';
|
||||||
|
import Path from './Path';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import extractProps from '../lib/extract/extractProps';
|
||||||
|
import {ShapeAttributes} from '../lib/attributes';
|
||||||
|
import SerializableShape from '../lib/SerializableShape';
|
||||||
|
import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass';
|
||||||
|
import {circleProps, ellipseProps, lineProps, rectProps} from '../lib/props';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Circle shape type
|
||||||
|
*/
|
||||||
|
const CIRCLE = 0;
|
||||||
|
/**
|
||||||
|
* ELLIPSE shape type
|
||||||
|
*/
|
||||||
|
const ELLIPSE = 1;
|
||||||
|
/**
|
||||||
|
* LINE shape type
|
||||||
|
*/
|
||||||
|
const LINE = 2;
|
||||||
|
/**
|
||||||
|
* RECT shape type
|
||||||
|
*/
|
||||||
|
const RECT = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* coord props
|
||||||
|
*/
|
||||||
|
const COORD_PROPS = {
|
||||||
|
/**
|
||||||
|
* algorithm for radius in percentage
|
||||||
|
* radius = Math.sqrt(Math.pow((width*percent), 2) + Math.pow((height*percent), 2)) / Math.sqrt(2);
|
||||||
|
*/
|
||||||
|
[CIRCLE]: Object.keys(circleProps),
|
||||||
|
[ELLIPSE]: Object.keys(ellipseProps),
|
||||||
|
[LINE]: Object.keys(lineProps),
|
||||||
|
[RECT]: Object.keys(rectProps)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* virtualNode component for shape elements
|
||||||
|
*/
|
||||||
|
class Shape extends Component{
|
||||||
|
static displayName = 'Shape';
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let props = this.props;
|
||||||
|
|
||||||
|
if (this.context.isInGroup) {
|
||||||
|
props = _.defaults({}, props, this.context, {
|
||||||
|
isInGroup: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let shape = new SerializableShape(props, COORD_PROPS[this.type]).toJSON();
|
||||||
|
|
||||||
|
return <NativePath
|
||||||
|
{...extractProps(this.type === 3 ? {
|
||||||
|
...props,
|
||||||
|
x: null,
|
||||||
|
y: null
|
||||||
|
} : props)}
|
||||||
|
shape={{
|
||||||
|
...shape,
|
||||||
|
type: this.type
|
||||||
|
}}
|
||||||
|
/>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let NativePath = createReactNativeComponentClass({
|
||||||
|
validAttributes: ShapeAttributes,
|
||||||
|
uiViewClassName: 'RNSVGShape'
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Shape;
|
||||||
|
|
||||||
|
export {
|
||||||
|
CIRCLE,
|
||||||
|
ELLIPSE,
|
||||||
|
LINE,
|
||||||
|
RECT
|
||||||
|
};
|
||||||
@@ -6,7 +6,6 @@ import React, {
|
|||||||
View,
|
View,
|
||||||
requireNativeComponent
|
requireNativeComponent
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import extractViewbox from '../lib/extract/extractViewbox';
|
|
||||||
import ViewBox from './ViewBox';
|
import ViewBox from './ViewBox';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import React, {
|
|||||||
|
|
||||||
import ViewBox from './ViewBox';
|
import ViewBox from './ViewBox';
|
||||||
import Defs from './Defs';
|
import Defs from './Defs';
|
||||||
import extractViewbox from '../lib/extract/extractViewbox';
|
|
||||||
class SymbolElement extends Component{
|
class SymbolElement extends Component{
|
||||||
static displayName = 'Symbol';
|
static displayName = 'Symbol';
|
||||||
static propType = {
|
static propType = {
|
||||||
|
|||||||
@@ -1,24 +1,41 @@
|
|||||||
import React, {
|
import React, {
|
||||||
Component
|
Component,
|
||||||
|
PropTypes
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Defs from './Defs';
|
import Defs from './Defs';
|
||||||
import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass';
|
import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass';
|
||||||
import extractProps from '../lib/extract/extractProps';
|
import extractProps from '../lib/extract/extractProps';
|
||||||
import extractText from '../lib/extract/extractText';
|
import extractText from '../lib/extract/extractText';
|
||||||
import {TextAttributes} from '../lib/attributes';
|
import {TextAttributes} from '../lib/attributes';
|
||||||
import numberProp from '../lib/numberProp';
|
import {numberProp, textProps, fillProps, strokeProps, pathProps} from '../lib/props';
|
||||||
|
|
||||||
class Text extends Component{
|
class Text extends Component{
|
||||||
static displayName = 'Text';
|
static displayName = 'Text';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
x: numberProp,
|
|
||||||
y: numberProp,
|
|
||||||
dx: numberProp,
|
dx: numberProp,
|
||||||
dy: numberProp
|
dy: numberProp,
|
||||||
|
...textProps,
|
||||||
|
...fillProps,
|
||||||
|
...strokeProps,
|
||||||
|
...pathProps
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static contextTypes = {
|
||||||
|
...textProps,
|
||||||
|
...fillProps,
|
||||||
|
...strokeProps,
|
||||||
|
isInGroup: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {props} = this;
|
let {props} = this;
|
||||||
|
|
||||||
|
if (this.context.isInGroup) {
|
||||||
|
props = _.defaults(this.context, props, {
|
||||||
|
isInGroup: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let x = 0;
|
let x = 0;
|
||||||
if (props.x) {
|
if (props.x) {
|
||||||
x = props.dx ? +props.x + (+props.dx) : +props.x;
|
x = props.dx ? +props.x + (+props.dx) : +props.x;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React, {
|
import React, {
|
||||||
Component,
|
Component,
|
||||||
PropTypes,
|
PropTypes
|
||||||
cloneElement
|
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Defs from './Defs';
|
import Defs from './Defs';
|
||||||
class Use extends Component{
|
class Use extends Component{
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class ViewBox extends Component{
|
|||||||
x = viewbox.x;
|
x = viewbox.x;
|
||||||
y = viewbox.y;
|
y = viewbox.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <G
|
return <G
|
||||||
{...this.props}
|
{...this.props}
|
||||||
x={x}
|
x={x}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
@interface RNSVGBrush : NSObject
|
@interface RNSVGBrush : NSObject
|
||||||
|
{
|
||||||
|
NSArray *_points;
|
||||||
|
}
|
||||||
|
|
||||||
/* @abstract */
|
/* @abstract */
|
||||||
- (instancetype)initWithArray:(NSArray *)data NS_DESIGNATED_INITIALIZER;
|
- (instancetype)initWithArray:(NSArray *)data NS_DESIGNATED_INITIALIZER;
|
||||||
@@ -26,6 +29,8 @@
|
|||||||
|
|
||||||
- (BOOL)applyStrokeColor:(CGContextRef)context;
|
- (BOOL)applyStrokeColor:(CGContextRef)context;
|
||||||
|
|
||||||
|
- (CGFloat)getActualProp:(int)percent relative:(CGFloat)relative offset:(CGFloat)offset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* paint fills the context with a brush. The context is assumed to
|
* paint fills the context with a brush. The context is assumed to
|
||||||
* be clipped.
|
* be clipped.
|
||||||
|
|||||||
@@ -29,6 +29,30 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (CGFloat)getActualProp:(int)index relative:(CGFloat)relative offset:(CGFloat)offset
|
||||||
|
{
|
||||||
|
id coord = [_points objectAtIndex:index];
|
||||||
|
|
||||||
|
if ([coord isKindOfClass:[NSString class]]) {
|
||||||
|
__block float matched = [coord floatValue];
|
||||||
|
NSString *percentage = (NSString *)coord;
|
||||||
|
NSRegularExpression *regex = [[NSRegularExpression alloc] initWithPattern:@"^(\\-?\\d+(?:\\.\\d+)?)%$" options:0 error:NULL];
|
||||||
|
[regex enumerateMatchesInString:percentage
|
||||||
|
options:0
|
||||||
|
range:NSMakeRange(0, percentage.length)
|
||||||
|
usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop)
|
||||||
|
{
|
||||||
|
|
||||||
|
matched = [[percentage substringWithRange:NSMakeRange(result.range.location, result.range.length)] floatValue];
|
||||||
|
matched = matched / 100 * relative + offset;
|
||||||
|
}];
|
||||||
|
|
||||||
|
return matched;
|
||||||
|
} else {
|
||||||
|
return [coord floatValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void)paint:(CGContextRef)context
|
- (void)paint:(CGContextRef)context
|
||||||
{
|
{
|
||||||
// abstract
|
// abstract
|
||||||
|
|||||||
@@ -13,36 +13,47 @@
|
|||||||
|
|
||||||
@implementation RNSVGLinearGradient
|
@implementation RNSVGLinearGradient
|
||||||
{
|
{
|
||||||
CGGradientRef _gradient;
|
CGGradientRef _gradient;
|
||||||
CGPoint _startPoint;
|
|
||||||
CGPoint _endPoint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithArray:(NSArray<NSNumber *> *)array
|
- (instancetype)initWithArray:(NSArray *)array
|
||||||
{
|
{
|
||||||
if ((self = [super initWithArray:array])) {
|
if ((self = [super initWithArray:array])) {
|
||||||
if (array.count < 5) {
|
if (array.count < 5) {
|
||||||
RCTLogError(@"-[%@ %@] expects 5 elements, received %@",
|
RCTLogError(@"-[%@ %@] expects 5 elements, received %@",
|
||||||
self.class, NSStringFromSelector(_cmd), array);
|
self.class, NSStringFromSelector(_cmd), array);
|
||||||
return nil;
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
_points = [array subarrayWithRange:NSMakeRange(1, 4)];
|
||||||
|
_gradient = CGGradientRetain([RCTConvert CGGradient:array offset:5]);
|
||||||
}
|
}
|
||||||
_startPoint = [RCTConvert CGPoint:array offset:1];
|
return self;
|
||||||
_endPoint = [RCTConvert CGPoint:array offset:3];
|
|
||||||
_gradient = CGGradientRetain([RCTConvert CGGradient:array offset:5]);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
CGGradientRelease(_gradient);
|
CGGradientRelease(_gradient);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)paint:(CGContextRef)context
|
- (void)paint:(CGContextRef)context
|
||||||
{
|
{
|
||||||
CGGradientDrawingOptions extendOptions =
|
CGGradientDrawingOptions extendOptions =
|
||||||
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
|
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
|
||||||
CGContextDrawLinearGradient(context, _gradient, _startPoint, _endPoint, extendOptions);
|
|
||||||
|
CGRect box = CGContextGetClipBoundingBox(context);
|
||||||
|
float height = CGRectGetHeight(box);
|
||||||
|
float width = CGRectGetWidth(box);
|
||||||
|
float midX = CGRectGetMidX(box);
|
||||||
|
float midY = CGRectGetMidY(box);
|
||||||
|
float offsetX = (midX - width / 2);
|
||||||
|
float offsetY = (midY - height / 2);
|
||||||
|
|
||||||
|
CGFloat x1 = [self getActualProp:0 relative:width offset:offsetX];
|
||||||
|
CGFloat y1 = [self getActualProp:1 relative:height offset:offsetY];
|
||||||
|
CGFloat x2 = [self getActualProp:2 relative:width offset:offsetX];
|
||||||
|
CGFloat y2 = [self getActualProp:3 relative:height offset:offsetY];
|
||||||
|
CGContextDrawLinearGradient(context, _gradient, CGPointMake(x1, y1), CGPointMake(x2, y2), extendOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -13,43 +13,65 @@
|
|||||||
|
|
||||||
@implementation RNSVGRadialGradient
|
@implementation RNSVGRadialGradient
|
||||||
{
|
{
|
||||||
CGGradientRef _gradient;
|
CGGradientRef _gradient;
|
||||||
CGPoint _focusPoint;
|
|
||||||
CGPoint _centerPoint;
|
|
||||||
CGFloat _radius;
|
|
||||||
CGFloat _radiusRatio;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithArray:(NSArray<NSNumber *> *)array
|
- (instancetype)initWithArray:(NSArray *)array
|
||||||
{
|
{
|
||||||
if ((self = [super initWithArray:array])) {
|
if ((self = [super initWithArray:array])) {
|
||||||
if (array.count < 7) {
|
if (array.count < 7) {
|
||||||
RCTLogError(@"-[%@ %@] expects 7 elements, received %@",
|
RCTLogError(@"-[%@ %@] expects 7 elements, received %@",
|
||||||
self.class, NSStringFromSelector(_cmd), array);
|
self.class, NSStringFromSelector(_cmd), array);
|
||||||
return nil;
|
return nil;
|
||||||
|
}
|
||||||
|
_points = [array subarrayWithRange:NSMakeRange(1, 6)];
|
||||||
|
|
||||||
|
NSMutableArray *gradient = [[NSMutableArray alloc] init];
|
||||||
|
int count = ([array count] - 7) / 5;
|
||||||
|
int startStops = [array count] - count;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
[gradient insertObject:[array objectAtIndex:(i * 4 + 7)] atIndex:(i * 4)];
|
||||||
|
[gradient insertObject:[array objectAtIndex:(i * 4 + 8)] atIndex:(i * 4 + 1)];
|
||||||
|
[gradient insertObject:[array objectAtIndex:(i * 4 + 9)] atIndex:(i * 4 + 2)];
|
||||||
|
[gradient insertObject:[array objectAtIndex:(i * 4 + 10)] atIndex:(i * 4 + 3)];
|
||||||
|
NSNumber *stop = [NSNumber numberWithFloat:[[array objectAtIndex:(startStops + (count - i - 1))] floatValue]];
|
||||||
|
|
||||||
|
[gradient insertObject:stop atIndex:i * 4 + 4];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_gradient = CGGradientRetain([RCTConvert CGGradient:gradient offset:0]);
|
||||||
}
|
}
|
||||||
_radius = [RCTConvert CGFloat:array[3]];
|
return self;
|
||||||
_radiusRatio = [RCTConvert CGFloat:array[4]] / _radius;
|
|
||||||
_focusPoint.x = [RCTConvert CGFloat:array[1]];
|
|
||||||
_focusPoint.y = [RCTConvert CGFloat:array[2]] / _radiusRatio;
|
|
||||||
_centerPoint.x = [RCTConvert CGFloat:array[5]];
|
|
||||||
_centerPoint.y = [RCTConvert CGFloat:array[6]] / _radiusRatio;
|
|
||||||
_gradient = CGGradientRetain([RCTConvert CGGradient:array offset:7]);
|
|
||||||
}
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
CGGradientRelease(_gradient);
|
CGGradientRelease(_gradient);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)paint:(CGContextRef)context
|
- (void)paint:(CGContextRef)context
|
||||||
{
|
{
|
||||||
CGAffineTransform transform = CGAffineTransformMakeScale(1, _radiusRatio);
|
|
||||||
CGContextConcatCTM(context, transform);
|
CGRect box = CGContextGetClipBoundingBox(context);
|
||||||
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
|
float height = CGRectGetHeight(box);
|
||||||
CGContextDrawRadialGradient(context, _gradient, _focusPoint, 0, _centerPoint, _radius, extendOptions);
|
float width = CGRectGetWidth(box);
|
||||||
|
float midX = CGRectGetMidX(box);
|
||||||
|
float midY = CGRectGetMidY(box);
|
||||||
|
float offsetX = (midX - width / 2);
|
||||||
|
float offsetY = (midY - height / 2);
|
||||||
|
CGFloat rx = [self getActualProp:2 relative:width offset:0];
|
||||||
|
CGFloat ry = [self getActualProp:3 relative:height offset:0];
|
||||||
|
CGFloat fx = [self getActualProp:0 relative:width offset:offsetX];
|
||||||
|
CGFloat fy = [self getActualProp:1 relative:height offset:offsetY] / (ry / rx);
|
||||||
|
CGFloat cx = [self getActualProp:4 relative:width offset:offsetX];
|
||||||
|
CGFloat cy = [self getActualProp:5 relative:height offset:offsetY] / (ry / rx);
|
||||||
|
|
||||||
|
CGAffineTransform transform = CGAffineTransformMakeScale(1, ry / rx);
|
||||||
|
CGContextConcatCTM(context, transform);
|
||||||
|
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
|
||||||
|
|
||||||
|
CGContextDrawRadialGradient(context, _gradient, CGPointMake(fx, fy), 0, CGPointMake(cx, cy), rx, extendOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -20,14 +20,14 @@
|
|||||||
+ (CGPathRef)CGPath:(id)json
|
+ (CGPathRef)CGPath:(id)json
|
||||||
{
|
{
|
||||||
NSArray *arr = [self NSNumberArray:json];
|
NSArray *arr = [self NSNumberArray:json];
|
||||||
|
|
||||||
NSUInteger count = [arr count];
|
NSUInteger count = [arr count];
|
||||||
|
|
||||||
#define NEXT_VALUE [self double:arr[i++]]
|
#define NEXT_VALUE [self double:arr[i++]]
|
||||||
|
|
||||||
CGMutablePathRef path = CGPathCreateMutable();
|
CGMutablePathRef path = CGPathCreateMutable();
|
||||||
CGPathMoveToPoint(path, NULL, 0, 0);
|
CGPathMoveToPoint(path, NULL, 0, 0);
|
||||||
|
|
||||||
@try {
|
@try {
|
||||||
NSUInteger i = 0;
|
NSUInteger i = 0;
|
||||||
while (i < count) {
|
while (i < count) {
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
CGPathRelease(path);
|
CGPathRelease(path);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (CGPathRef)CFAutorelease(path);
|
return (CGPathRef)CFAutorelease(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,25 +84,25 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
|
|||||||
NSDictionary *dict = [self NSDictionary:json];
|
NSDictionary *dict = [self NSDictionary:json];
|
||||||
RNSVGTextFrame frame;
|
RNSVGTextFrame frame;
|
||||||
frame.count = 0;
|
frame.count = 0;
|
||||||
|
|
||||||
NSArray *lines = [self NSArray:dict[@"lines"]];
|
NSArray *lines = [self NSArray:dict[@"lines"]];
|
||||||
NSUInteger lineCount = [lines count];
|
NSUInteger lineCount = [lines count];
|
||||||
if (lineCount == 0) {
|
if (lineCount == 0) {
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSDictionary *fontDict = dict[@"font"];
|
NSDictionary *fontDict = dict[@"font"];
|
||||||
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] scaleMultiplier:1.0];
|
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] scaleMultiplier:1.0];
|
||||||
if (!font) {
|
if (!font) {
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a dictionary for this font
|
// Create a dictionary for this font
|
||||||
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
|
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
|
||||||
(NSString *)kCTFontAttributeName: (__bridge id)font,
|
(NSString *)kCTFontAttributeName: (__bridge id)font,
|
||||||
(NSString *)kCTForegroundColorFromContextAttributeName: @YES
|
(NSString *)kCTForegroundColorFromContextAttributeName: @YES
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up text frame with font metrics
|
// Set up text frame with font metrics
|
||||||
CGFloat size = CTFontGetSize(font);
|
CGFloat size = CTFontGetSize(font);
|
||||||
frame.count = lineCount;
|
frame.count = lineCount;
|
||||||
@@ -110,18 +110,18 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
|
|||||||
frame.lineHeight = size * 1.1; // Base on RNSVG canvas line height estimate
|
frame.lineHeight = size * 1.1; // Base on RNSVG canvas line height estimate
|
||||||
frame.lines = malloc(sizeof(CTLineRef) * lineCount);
|
frame.lines = malloc(sizeof(CTLineRef) * lineCount);
|
||||||
frame.widths = malloc(sizeof(CGFloat) * lineCount);
|
frame.widths = malloc(sizeof(CGFloat) * lineCount);
|
||||||
|
|
||||||
[lines enumerateObjectsUsingBlock:^(NSString *text, NSUInteger i, BOOL *stop) {
|
[lines enumerateObjectsUsingBlock:^(NSString *text, NSUInteger i, BOOL *stop) {
|
||||||
|
|
||||||
CFStringRef string = (__bridge CFStringRef)text;
|
CFStringRef string = (__bridge CFStringRef)text;
|
||||||
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
|
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
|
||||||
CTLineRef line = CTLineCreateWithAttributedString(attrString);
|
CTLineRef line = CTLineCreateWithAttributedString(attrString);
|
||||||
CFRelease(attrString);
|
CFRelease(attrString);
|
||||||
|
|
||||||
frame.lines[i] = line;
|
frame.lines[i] = line;
|
||||||
frame.widths[i] = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
|
frame.widths[i] = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,11 +129,11 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
|
|||||||
{
|
{
|
||||||
NSArray *arr = [self NSNumberArray:json];
|
NSArray *arr = [self NSNumberArray:json];
|
||||||
NSUInteger count = arr.count;
|
NSUInteger count = arr.count;
|
||||||
|
|
||||||
RNSVGCGFloatArray array;
|
RNSVGCGFloatArray array;
|
||||||
array.count = count;
|
array.count = count;
|
||||||
array.array = NULL;
|
array.array = NULL;
|
||||||
|
|
||||||
if (count) {
|
if (count) {
|
||||||
// Ideally, these arrays should already use the same memory layout.
|
// Ideally, these arrays should already use the same memory layout.
|
||||||
// In that case we shouldn't need this new malloc.
|
// In that case we shouldn't need this new malloc.
|
||||||
@@ -142,7 +142,7 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
|
|||||||
array.array[i] = [arr[i] doubleValue];
|
array.array[i] = [arr[i] doubleValue];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
|
|||||||
{
|
{
|
||||||
NSArray *arr = [self NSArray:json];
|
NSArray *arr = [self NSArray:json];
|
||||||
NSUInteger type = [self NSUInteger:arr.firstObject];
|
NSUInteger type = [self NSUInteger:arr.firstObject];
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 0: // solid color
|
case 0: // solid color
|
||||||
// These are probably expensive allocations since it's often the same value.
|
// These are probably expensive allocations since it's often the same value.
|
||||||
@@ -161,6 +161,8 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
|
|||||||
case 2: // radial gradient
|
case 2: // radial gradient
|
||||||
return [[RNSVGRadialGradient alloc] initWithArray:arr];
|
return [[RNSVGRadialGradient alloc] initWithArray:arr];
|
||||||
case 3: // pattern
|
case 3: // pattern
|
||||||
|
// TODO:
|
||||||
|
return nil;
|
||||||
return [[RNSVGPattern alloc] initWithArray:arr];
|
return [[RNSVGPattern alloc] initWithArray:arr];
|
||||||
default:
|
default:
|
||||||
RCTLogError(@"Unknown brush type: %zd", type);
|
RCTLogError(@"Unknown brush type: %zd", type);
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
10A062FF1CC732020000CEEF /* RNSVGSvgViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A062FD1CC732020000CEEF /* RNSVGSvgViewManager.m */; };
|
10A062FF1CC732020000CEEF /* RNSVGSvgViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A062FD1CC732020000CEEF /* RNSVGSvgViewManager.m */; };
|
||||||
10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063011CC7320C0000CEEF /* RNSVGPath.m */; };
|
10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063011CC7320C0000CEEF /* RNSVGPath.m */; };
|
||||||
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063031CC7320C0000CEEF /* RNSVGSvgView.m */; };
|
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063031CC7320C0000CEEF /* RNSVGSvgView.m */; };
|
||||||
|
10C068671CCF0F87007C6982 /* RNSVGShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 10C068651CCF0F87007C6982 /* RNSVGShape.m */; };
|
||||||
|
10C0686A1CCF1061007C6982 /* RNSVGShapeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10C068691CCF1061007C6982 /* RNSVGShapeManager.m */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
@@ -80,6 +82,10 @@
|
|||||||
10A063011CC7320C0000CEEF /* RNSVGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGPath.m; sourceTree = "<group>"; };
|
10A063011CC7320C0000CEEF /* RNSVGPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGPath.m; sourceTree = "<group>"; };
|
||||||
10A063021CC7320C0000CEEF /* RNSVGSvgView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGSvgView.h; sourceTree = "<group>"; };
|
10A063021CC7320C0000CEEF /* RNSVGSvgView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGSvgView.h; sourceTree = "<group>"; };
|
||||||
10A063031CC7320C0000CEEF /* RNSVGSvgView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSvgView.m; sourceTree = "<group>"; };
|
10A063031CC7320C0000CEEF /* RNSVGSvgView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSvgView.m; sourceTree = "<group>"; };
|
||||||
|
10C068641CCF0F87007C6982 /* RNSVGShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGShape.h; sourceTree = SOURCE_ROOT; };
|
||||||
|
10C068651CCF0F87007C6982 /* RNSVGShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGShape.m; sourceTree = SOURCE_ROOT; };
|
||||||
|
10C068681CCF1061007C6982 /* RNSVGShapeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGShapeManager.h; sourceTree = "<group>"; };
|
||||||
|
10C068691CCF1061007C6982 /* RNSVGShapeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGShapeManager.m; sourceTree = "<group>"; };
|
||||||
10FEAC6A1CC7D05200F1C23C /* RNSVGCGFCRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGCGFCRule.h; sourceTree = "<group>"; };
|
10FEAC6A1CC7D05200F1C23C /* RNSVGCGFCRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGCGFCRule.h; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@@ -104,10 +110,12 @@
|
|||||||
0CF68ADC1AF0549300FF9E5C /* RNSVGContainer.h */,
|
0CF68ADC1AF0549300FF9E5C /* RNSVGContainer.h */,
|
||||||
0CF68ADD1AF0549300FF9E5C /* RNSVGGroup.h */,
|
0CF68ADD1AF0549300FF9E5C /* RNSVGGroup.h */,
|
||||||
0CF68ADE1AF0549300FF9E5C /* RNSVGGroup.m */,
|
0CF68ADE1AF0549300FF9E5C /* RNSVGGroup.m */,
|
||||||
0CF68ADF1AF0549300FF9E5C /* RNSVGNode.h */,
|
10C068641CCF0F87007C6982 /* RNSVGShape.h */,
|
||||||
0CF68AE01AF0549300FF9E5C /* RNSVGNode.m */,
|
10C068651CCF0F87007C6982 /* RNSVGShape.m */,
|
||||||
10A063001CC7320C0000CEEF /* RNSVGPath.h */,
|
10A063001CC7320C0000CEEF /* RNSVGPath.h */,
|
||||||
10A063011CC7320C0000CEEF /* RNSVGPath.m */,
|
10A063011CC7320C0000CEEF /* RNSVGPath.m */,
|
||||||
|
0CF68ADF1AF0549300FF9E5C /* RNSVGNode.h */,
|
||||||
|
0CF68AE01AF0549300FF9E5C /* RNSVGNode.m */,
|
||||||
10A063021CC7320C0000CEEF /* RNSVGSvgView.h */,
|
10A063021CC7320C0000CEEF /* RNSVGSvgView.h */,
|
||||||
10A063031CC7320C0000CEEF /* RNSVGSvgView.m */,
|
10A063031CC7320C0000CEEF /* RNSVGSvgView.m */,
|
||||||
0CF68AE11AF0549300FF9E5C /* RNSVGRenderable.h */,
|
0CF68AE11AF0549300FF9E5C /* RNSVGRenderable.h */,
|
||||||
@@ -149,6 +157,8 @@
|
|||||||
0CF68AF81AF0549300FF9E5C /* ViewManagers */ = {
|
0CF68AF81AF0549300FF9E5C /* ViewManagers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
10C068681CCF1061007C6982 /* RNSVGShapeManager.h */,
|
||||||
|
10C068691CCF1061007C6982 /* RNSVGShapeManager.m */,
|
||||||
10A062FA1CC732020000CEEF /* RNSVGPathManager.h */,
|
10A062FA1CC732020000CEEF /* RNSVGPathManager.h */,
|
||||||
10A062FB1CC732020000CEEF /* RNSVGPathManager.m */,
|
10A062FB1CC732020000CEEF /* RNSVGPathManager.m */,
|
||||||
10A062FC1CC732020000CEEF /* RNSVGSvgViewManager.h */,
|
10A062FC1CC732020000CEEF /* RNSVGSvgViewManager.h */,
|
||||||
@@ -220,6 +230,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
10C068671CCF0F87007C6982 /* RNSVGShape.m in Sources */,
|
||||||
0CF68B161AF0549300FF9E5C /* RNSVGTextManager.m in Sources */,
|
0CF68B161AF0549300FF9E5C /* RNSVGTextManager.m in Sources */,
|
||||||
10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */,
|
10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */,
|
||||||
0CF68B111AF0549300FF9E5C /* RNSVGGroupManager.m in Sources */,
|
0CF68B111AF0549300FF9E5C /* RNSVGGroupManager.m in Sources */,
|
||||||
@@ -233,6 +244,7 @@
|
|||||||
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */,
|
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */,
|
||||||
0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */,
|
0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */,
|
||||||
0CF68B101AF0549300FF9E5C /* RCTConvert+RNSVG.m in Sources */,
|
0CF68B101AF0549300FF9E5C /* RCTConvert+RNSVG.m in Sources */,
|
||||||
|
10C0686A1CCF1061007C6982 /* RNSVGShapeManager.m in Sources */,
|
||||||
10A062FE1CC732020000CEEF /* RNSVGPathManager.m in Sources */,
|
10A062FE1CC732020000CEEF /* RNSVGPathManager.m in Sources */,
|
||||||
0CF68B061AF0549300FF9E5C /* RNSVGNode.m in Sources */,
|
0CF68B061AF0549300FF9E5C /* RNSVGNode.m in Sources */,
|
||||||
0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColor.m in Sources */,
|
0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColor.m in Sources */,
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
|
|
||||||
@interface RNSVGNode : UIView
|
@interface RNSVGNode : UIView
|
||||||
|
|
||||||
|
@property (nonatomic, assign) CGRect rect;
|
||||||
@property (nonatomic, assign) CGFloat opacity;
|
@property (nonatomic, assign) CGFloat opacity;
|
||||||
|
|
||||||
@property (nonatomic, assign) CGPathRef clipPath;
|
@property (nonatomic, assign) CGPathRef clipPath;
|
||||||
@property (nonatomic, assign) RNSVGCGFCRule clipRule;
|
@property (nonatomic, assign) RNSVGCGFCRule clipRule;
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,6 @@
|
|||||||
_clipPath = CGPathRetain(clipPath);
|
_clipPath = CGPathRetain(clipPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
CGPathRelease(_clipPath);
|
CGPathRelease(_clipPath);
|
||||||
|
|||||||
17
ios/RNSVGShape.h
Normal file
17
ios/RNSVGShape.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Horcrux.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import "RNSVGRenderable.h"
|
||||||
|
#import "RNSVGPath.h"
|
||||||
|
|
||||||
|
@interface RNSVGShape : RNSVGPath
|
||||||
|
@property (nonatomic, strong) NSDictionary* shape;
|
||||||
|
|
||||||
|
@end
|
||||||
127
ios/RNSVGShape.m
Normal file
127
ios/RNSVGShape.m
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Horcrux.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RNSVGShape.h"
|
||||||
|
#import "RCTLog.h"
|
||||||
|
|
||||||
|
@implementation RNSVGShape
|
||||||
|
|
||||||
|
- (void)dealloc
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)renderLayerTo:(CGContextRef)context
|
||||||
|
{
|
||||||
|
int type = [[self.shape objectForKey:@"type"] intValue];
|
||||||
|
CGRect box = CGContextGetClipBoundingBox(context);
|
||||||
|
CGMutablePathRef path = CGPathCreateMutable();
|
||||||
|
|
||||||
|
float height = CGRectGetHeight(box);
|
||||||
|
float width = CGRectGetWidth(box);
|
||||||
|
switch (type) {
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
// draw circle
|
||||||
|
CGFloat cx = [self getActualProp:@"cx" relative:width];
|
||||||
|
CGFloat cy = [self getActualProp:@"cy" relative:height];
|
||||||
|
|
||||||
|
|
||||||
|
CGFloat r;
|
||||||
|
// radius in percentage calculate formula:
|
||||||
|
// radius = sqrt(pow((width*percent), 2) + pow((height*percent), 2)) / sqrt(2)
|
||||||
|
NSDictionary *prop = [self.shape objectForKey:@"r"];
|
||||||
|
CGFloat value = [[prop objectForKey:@"value"] floatValue];
|
||||||
|
if ([[prop objectForKey:@"percentage"] integerValue] == 1) {
|
||||||
|
r = sqrt(pow((width * value), 2) + pow((height * value), 2)) / sqrt(2);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
r = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGPathAddArc(path, nil, cx, cy, r, -M_PI, M_PI, YES);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
// draw ellipse
|
||||||
|
CGFloat cx = [self getActualProp:@"cx" relative:width];
|
||||||
|
CGFloat cy = [self getActualProp:@"cy" relative:height];
|
||||||
|
CGFloat rx = [self getActualProp:@"rx" relative:height];
|
||||||
|
CGFloat ry = [self getActualProp:@"ry" relative:height];
|
||||||
|
CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx, cy - ry, rx * 2, ry * 2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
// draw line
|
||||||
|
CGFloat x1 = [self getActualProp:@"x1" relative:height];
|
||||||
|
CGFloat y1 = [self getActualProp:@"y1" relative:height];
|
||||||
|
CGFloat x2 = [self getActualProp:@"x2" relative:height];
|
||||||
|
CGFloat y2 = [self getActualProp:@"y2" relative:height];
|
||||||
|
CGPathMoveToPoint(path, nil, x1, y1);
|
||||||
|
CGPathAddLineToPoint(path, nil, x2, y2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
// draw rect
|
||||||
|
CGFloat x = [self getActualProp:@"x" relative:width];
|
||||||
|
CGFloat y = [self getActualProp:@"y" relative:height];
|
||||||
|
CGFloat w = [self getActualProp:@"width" relative:width];
|
||||||
|
CGFloat h = [self getActualProp:@"height" relative:height];
|
||||||
|
CGFloat rx = [self getActualProp:@"rx" relative:width];
|
||||||
|
CGFloat ry = [self getActualProp:@"ry" relative:height];
|
||||||
|
|
||||||
|
|
||||||
|
if (rx != 0 || ry != 0) {
|
||||||
|
if (rx == 0) {
|
||||||
|
rx = ry;
|
||||||
|
} else if (ry == 0) {
|
||||||
|
ry = rx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rx > w / 2) {
|
||||||
|
rx = w / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ry > h / 2) {
|
||||||
|
ry = h / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGPathAddRoundedRect(path, nil, CGRectMake(x, y, w, h), rx, ry);
|
||||||
|
} else {
|
||||||
|
CGPathAddRect(path, nil, CGRectMake(x, y, w, h));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
RCTLogError(@"Invalid Shape type %d at %@", type, self.shape);
|
||||||
|
//CGPathRelease(path);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
self.d = path;
|
||||||
|
[super renderLayerTo:context];
|
||||||
|
//NSLog(@"%@", NSStringFromCGRect(box));
|
||||||
|
//NSLog(@"%@", self.shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)getActualProp:(NSString *)name relative:(float)relative
|
||||||
|
{
|
||||||
|
NSDictionary *prop = [self.shape objectForKey:name];
|
||||||
|
CGFloat value = [[prop objectForKey:@"value"] floatValue];
|
||||||
|
if ([[prop objectForKey:@"percentage"] integerValue] == 1) {
|
||||||
|
return relative * value;
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
{
|
{
|
||||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||||
for (RNSVGNode *node in self.subviews) {
|
for (RNSVGNode *node in self.subviews) {
|
||||||
[node renderTo:context];
|
[node renderTo:context];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
ios/ViewManagers/RNSVGShapeManager.h
Normal file
13
ios/ViewManagers/RNSVGShapeManager.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Horcrux.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RNSVGRenderableManager.h"
|
||||||
|
|
||||||
|
@interface RNSVGShapeManager : RNSVGRenderableManager
|
||||||
|
|
||||||
|
@end
|
||||||
25
ios/ViewManagers/RNSVGShapeManager.m
Normal file
25
ios/ViewManagers/RNSVGShapeManager.m
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Horcrux.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RNSVGShapeManager.h"
|
||||||
|
|
||||||
|
#import "RNSVGShape.h"
|
||||||
|
#import "RCTConvert+RNSVG.h"
|
||||||
|
|
||||||
|
@implementation RNSVGShapeManager
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
- (RNSVGRenderable *)node
|
||||||
|
{
|
||||||
|
return [RNSVGShape new];
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(shape, NSDictionary)
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
// from https://github.com/gabelerner/canvg/blob/860e418aca67b9a41e858a223d74d375793ec364/canvg.js#L449
|
|
||||||
|
|
||||||
class BoundingBox{
|
|
||||||
constructor(x1, y1, x2, y2) {
|
|
||||||
this.x1 = NaN;
|
|
||||||
this.y1 = NaN;
|
|
||||||
this.x2 = NaN;
|
|
||||||
this.y2 = NaN;
|
|
||||||
|
|
||||||
this.addPoint(x1, y1);
|
|
||||||
this.addPoint(x2, y2);
|
|
||||||
}
|
|
||||||
|
|
||||||
width = () => this.x2 - this.x1;
|
|
||||||
|
|
||||||
height = () => this.y2 - this.y1;
|
|
||||||
|
|
||||||
addPoint = (x, y) => {
|
|
||||||
if (x != null) {
|
|
||||||
if (isNaN(this.x1) || isNaN(this.x2)) {
|
|
||||||
this.x1 = x;
|
|
||||||
this.x2 = x;
|
|
||||||
}
|
|
||||||
if (x < this.x1) this.x1 = x;
|
|
||||||
if (x > this.x2) this.x2 = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y != null) {
|
|
||||||
if (isNaN(this.y1) || isNaN(this.y2)) {
|
|
||||||
this.y1 = y;
|
|
||||||
this.y2 = y;
|
|
||||||
}
|
|
||||||
if (y < this.y1) this.y1 = y;
|
|
||||||
if (y > this.y2) this.y2 = y;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
addX = x => this.addPoint(x, null);
|
|
||||||
|
|
||||||
addY = y => this.addPoint(null, y);
|
|
||||||
|
|
||||||
addQuadraticCurve = (p0x, p0y, p1x, p1y, p2x, p2y) => {
|
|
||||||
let cp1x = p0x + 2 / 3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0)
|
|
||||||
let cp1y = p0y + 2 / 3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0)
|
|
||||||
let cp2x = cp1x + 1 / 3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0)
|
|
||||||
let cp2y = cp1y + 1 / 3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0)
|
|
||||||
this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y, cp2y, p2x, p2y);
|
|
||||||
};
|
|
||||||
|
|
||||||
addBezierCurve = (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) => {
|
|
||||||
// from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html
|
|
||||||
var p0 = [p0x, p0y],
|
|
||||||
p1 = [p1x, p1y],
|
|
||||||
p2 = [p2x, p2y],
|
|
||||||
p3 = [p3x, p3y];
|
|
||||||
|
|
||||||
this.addPoint(p0[0], p0[1]);
|
|
||||||
this.addPoint(p3[0], p3[1]);
|
|
||||||
|
|
||||||
[0, 1].forEach(i => {
|
|
||||||
let f = t => {
|
|
||||||
return Math.pow(1 - t, 3) * p0[i]
|
|
||||||
+ 3 * Math.pow(1 - t, 2) * t * p1[i]
|
|
||||||
+ 3 * (1 - t) * Math.pow(t, 2) * p2[i]
|
|
||||||
+ Math.pow(t, 3) * p3[i];
|
|
||||||
};
|
|
||||||
|
|
||||||
let b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i];
|
|
||||||
let a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i];
|
|
||||||
let c = 3 * p1[i] - 3 * p0[i];
|
|
||||||
|
|
||||||
if (a == 0) {
|
|
||||||
if (b != 0) {
|
|
||||||
let t = -c / b;
|
|
||||||
if (0 < t && t < 1) {
|
|
||||||
if (i == 0) this.addX(f(t));
|
|
||||||
if (i == 1) this.addY(f(t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let b2ac = Math.pow(b, 2) - 4 * c * a;
|
|
||||||
if (b2ac < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let t1 = (-b + Math.sqrt(b2ac)) / (2 * a);
|
|
||||||
if (0 < t1 && t1 < 1) {
|
|
||||||
i == 0 && this.addX(f(t1));
|
|
||||||
i == 1 && this.addY(f(t1));
|
|
||||||
}
|
|
||||||
let t2 = (-b - Math.sqrt(b2ac)) / (2 * a);
|
|
||||||
if (0 < t2 && t2 < 1) {
|
|
||||||
i == 0 && this.addX(f(t2));
|
|
||||||
i == 1 && this.addY(f(t2));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BoundingBox;
|
|
||||||
55
lib/SerializableShape.js
Normal file
55
lib/SerializableShape.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* Format potential percentage props
|
||||||
|
*
|
||||||
|
* convert somet props like those
|
||||||
|
* width="50%"
|
||||||
|
* height="500"
|
||||||
|
*
|
||||||
|
* to
|
||||||
|
* {
|
||||||
|
* width: {
|
||||||
|
* percentage: true,
|
||||||
|
* value: 0.5
|
||||||
|
* },
|
||||||
|
* height: {
|
||||||
|
* percentage: false,
|
||||||
|
* value: 500
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
import percentToFloat from './percentToFloat';
|
||||||
|
|
||||||
|
function percentageTransform(value) {
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
return {
|
||||||
|
percentage: false,
|
||||||
|
value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let float = percentToFloat(value);
|
||||||
|
|
||||||
|
return {
|
||||||
|
percentage: float !== +value,
|
||||||
|
value: float
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
constructor(props, list) {
|
||||||
|
this.shape = {};
|
||||||
|
list.forEach(name => {
|
||||||
|
if (props[name] != null) {
|
||||||
|
this.shape[name] = percentageTransform(props[name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON = () => {
|
||||||
|
return this.shape;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@ function arrayDiffer(a, b) {
|
|||||||
if (a.length !== b.length) {
|
if (a.length !== b.length) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (var i = 0; i < a.length; i++) {
|
for (let i = 0; i < a.length; i++) {
|
||||||
if (a[i] !== b[i]) {
|
if (a[i] !== b[i]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -14,10 +14,27 @@ function arrayDiffer(a, b) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shapeDiffer(a, b) {
|
||||||
|
if (a === b) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let key in a) {
|
||||||
|
if (a.hasOwnProperty(key)) {
|
||||||
|
if (key === 'type' && a.type !== b.type) {
|
||||||
|
return true;
|
||||||
|
} else if (a[key].percentage !== b[key].percentage || a[key].value !== b[key].value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function fontAndLinesDiffer(a, b) {
|
function fontAndLinesDiffer(a, b) {
|
||||||
if (a === b) {
|
if (a === b) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.font !== b.font) {
|
if (a.font !== b.font) {
|
||||||
if (a.font === null) {
|
if (a.font === null) {
|
||||||
return true;
|
return true;
|
||||||
@@ -38,7 +55,7 @@ function fontAndLinesDiffer(a, b) {
|
|||||||
return arrayDiffer(a.lines, b.lines);
|
return arrayDiffer(a.lines, b.lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
var NodeAttributes = {
|
const NodeAttributes = {
|
||||||
transform: {
|
transform: {
|
||||||
diff: arrayDiffer
|
diff: arrayDiffer
|
||||||
},
|
},
|
||||||
@@ -49,45 +66,57 @@ var NodeAttributes = {
|
|||||||
clipRule: true
|
clipRule: true
|
||||||
};
|
};
|
||||||
|
|
||||||
var GroupAttributes = Object.assign({
|
const GroupAttributes = {
|
||||||
}, NodeAttributes);
|
...NodeAttributes
|
||||||
|
};
|
||||||
|
|
||||||
var RenderableAttributes = Object.assign({
|
const RenderableAttributes = {
|
||||||
fill: {
|
fill: {
|
||||||
diff: arrayDiffer
|
diff: arrayDiffer
|
||||||
},
|
},
|
||||||
|
fillRule: true,
|
||||||
stroke: {
|
stroke: {
|
||||||
diff: arrayDiffer
|
diff: arrayDiffer
|
||||||
},
|
},
|
||||||
strokeWidth: true,
|
strokeWidth: true,
|
||||||
strokeLinecap: true,
|
strokeLinecap: true,
|
||||||
strokeLinejoin: true,
|
strokeLinejoin: true,
|
||||||
fillRule: true,
|
|
||||||
strokeDasharray: {
|
strokeDasharray: {
|
||||||
diff: arrayDiffer
|
diff: arrayDiffer
|
||||||
},
|
},
|
||||||
strokeDashoffset: true
|
strokeDashoffset: true,
|
||||||
}, NodeAttributes);
|
...NodeAttributes
|
||||||
|
};
|
||||||
|
|
||||||
var PathAttributes = Object.assign({
|
const PathAttributes ={
|
||||||
d: {
|
d: {
|
||||||
diff: arrayDiffer
|
diff: arrayDiffer
|
||||||
}
|
},
|
||||||
}, RenderableAttributes);
|
...RenderableAttributes
|
||||||
|
};
|
||||||
|
|
||||||
var TextAttributes = Object.assign({
|
const TextAttributes = {
|
||||||
alignment: true,
|
alignment: true,
|
||||||
frame: {
|
frame: {
|
||||||
diff: fontAndLinesDiffer
|
diff: fontAndLinesDiffer
|
||||||
},
|
},
|
||||||
path: {
|
path: {
|
||||||
diff: arrayDiffer
|
diff: arrayDiffer
|
||||||
}
|
},
|
||||||
}, RenderableAttributes);
|
...RenderableAttributes
|
||||||
|
};
|
||||||
|
|
||||||
|
const ShapeAttributes = {
|
||||||
|
shape: {
|
||||||
|
diff: shapeDiffer
|
||||||
|
},
|
||||||
|
...RenderableAttributes
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
GroupAttributes,
|
GroupAttributes,
|
||||||
PathAttributes,
|
PathAttributes,
|
||||||
TextAttributes
|
TextAttributes,
|
||||||
|
ShapeAttributes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
import SvgPath from 'svgpath';
|
|
||||||
import BoundingBox from './BoundingBox';
|
|
||||||
|
|
||||||
export default function(d) {
|
|
||||||
let pathDriver = new SvgPath(d);
|
|
||||||
let boundingBox = new BoundingBox();
|
|
||||||
pathDriver
|
|
||||||
.abs()
|
|
||||||
.unarc()
|
|
||||||
.unshort()
|
|
||||||
.iterate((seg, index, x, y) => {
|
|
||||||
switch(seg[0]) {
|
|
||||||
case 'M':
|
|
||||||
case 'L':
|
|
||||||
boundingBox.addPoint(
|
|
||||||
seg[1],
|
|
||||||
seg[2]
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'H':
|
|
||||||
boundingBox.addX(seg[1]);
|
|
||||||
break;
|
|
||||||
case 'V':
|
|
||||||
boundingBox.addY(seg[1]);
|
|
||||||
break;
|
|
||||||
case 'Q':
|
|
||||||
boundingBox.addQuadraticCurve(
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
seg[1],
|
|
||||||
seg[2],
|
|
||||||
seg[3],
|
|
||||||
seg[4]
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case 'C':
|
|
||||||
boundingBox.addBezierCurve(
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
seg[1],
|
|
||||||
seg[2],
|
|
||||||
seg[3],
|
|
||||||
seg[4],
|
|
||||||
seg[5],
|
|
||||||
seg[6]
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
width: boundingBox.width(),
|
|
||||||
height: boundingBox.height(),
|
|
||||||
x1: boundingBox.x1,
|
|
||||||
y1: boundingBox.y1,
|
|
||||||
x2: boundingBox.x2,
|
|
||||||
y2: boundingBox.y2
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -4,41 +4,11 @@ const LINEAR_GRADIENT = 1;
|
|||||||
const RADIAL_GRADIENT = 2;
|
const RADIAL_GRADIENT = 2;
|
||||||
const PATTERN = 3;
|
const PATTERN = 3;
|
||||||
|
|
||||||
function applyBoundingBoxToBrushData(brushData, props) {
|
export default function (colorOrBrush) {
|
||||||
let type = brushData[0];
|
|
||||||
let width = +props.width;
|
|
||||||
let height = +props.height;
|
|
||||||
if (type === LINEAR_GRADIENT) {
|
|
||||||
brushData[1] *= width;
|
|
||||||
brushData[2] *= height;
|
|
||||||
brushData[3] *= width;
|
|
||||||
brushData[4] *= height;
|
|
||||||
} else if (type === RADIAL_GRADIENT) {
|
|
||||||
brushData[1] *= width;
|
|
||||||
brushData[2] *= height;
|
|
||||||
brushData[3] *= width;
|
|
||||||
brushData[4] *= height;
|
|
||||||
brushData[5] *= width;
|
|
||||||
brushData[6] *= height;
|
|
||||||
} else if (type === PATTERN) {
|
|
||||||
// todo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function (colorOrBrush, props) {
|
|
||||||
if (!colorOrBrush) {
|
if (!colorOrBrush) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (colorOrBrush._brush) {
|
if (colorOrBrush._brush) {
|
||||||
if (colorOrBrush._bb) {
|
|
||||||
// The legacy API for Gradients allow for the bounding box to be used
|
|
||||||
// as a convenience for specifying gradient positions. This should be
|
|
||||||
// deprecated. It's not properly implemented in canvas mode. ReactART
|
|
||||||
// doesn't handle update to the bounding box correctly. That's why we
|
|
||||||
// mutate this so that if it's reused, we reuse the same resolved box.
|
|
||||||
applyBoundingBoxToBrushData(colorOrBrush._brush, props);
|
|
||||||
colorOrBrush._bb = false;
|
|
||||||
}
|
|
||||||
return colorOrBrush._brush;
|
return colorOrBrush._brush;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,5 +18,6 @@ export default function (colorOrBrush, props) {
|
|||||||
|
|
||||||
export {
|
export {
|
||||||
LINEAR_GRADIENT,
|
LINEAR_GRADIENT,
|
||||||
RADIAL_GRADIENT
|
RADIAL_GRADIENT,
|
||||||
|
PATTERN
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ const fillRules = {
|
|||||||
nonzero: 1
|
nonzero: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
function fillFilter(props, dimensions) {
|
function fillFilter(props) {
|
||||||
let {fill} = props;
|
let {fill} = props;
|
||||||
|
|
||||||
if (fill === 'none') {
|
if (fill === 'none') {
|
||||||
return null;
|
return null;
|
||||||
} else if (fill) {
|
} else if (fill) {
|
||||||
return patterns(fill, +props.fillOpacity, dimensions, props.svgId);
|
return patterns(fill, +props.fillOpacity, props.svgId);
|
||||||
} else if (props.fill === undefined) {
|
} else if (props.fill === undefined) {
|
||||||
let fillOpacity = +props.fillOpacity;
|
let fillOpacity = +props.fillOpacity;
|
||||||
if (isNaN(fillOpacity)) {
|
if (isNaN(fillOpacity)) {
|
||||||
@@ -25,8 +25,8 @@ function fillFilter(props, dimensions) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function(props, dimensions) {
|
export default function(props) {
|
||||||
let fill = extractBrush(fillFilter(props, dimensions), props);
|
let fill = extractBrush(fillFilter(props), props);
|
||||||
let fillRule = fillRules[props.fillRule] === 0 ? 0 : 1;
|
let fillRule = fillRules[props.fillRule] === 0 ? 0 : 1;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -14,18 +14,16 @@ export default function(props, options = {stroke: true, join: true, transform: t
|
|||||||
opacity: +props.opacity || 1
|
opacity: +props.opacity || 1
|
||||||
};
|
};
|
||||||
|
|
||||||
let dimensions = this.getBoundingBox ? this.getBoundingBox() : null;
|
|
||||||
|
|
||||||
if (props.clipPath) {
|
if (props.clipPath) {
|
||||||
_.assign(extractedProps, extractClipping(props));
|
_.assign(extractedProps, extractClipping(props));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.stroke) {
|
if (options.stroke) {
|
||||||
_.assign(extractedProps, extractStroke(props, dimensions));
|
_.assign(extractedProps, extractStroke(props));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.fill) {
|
if (options.fill) {
|
||||||
_.assign(extractedProps, extractFill(props, dimensions));
|
_.assign(extractedProps, extractFill(props));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.transform) {
|
if (options.transform) {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const joins = {
|
|||||||
round: 1
|
round: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
function strokeFilter(props, dimensions) {
|
function strokeFilter(props) {
|
||||||
let strokeWidth = +props.strokeWidth;
|
let strokeWidth = +props.strokeWidth;
|
||||||
let {stroke} = props;
|
let {stroke} = props;
|
||||||
if (!strokeWidth && !stroke) {
|
if (!strokeWidth && !stroke) {
|
||||||
@@ -39,7 +39,7 @@ function strokeFilter(props, dimensions) {
|
|||||||
|
|
||||||
// TODO: propTypes check
|
// TODO: propTypes check
|
||||||
return {
|
return {
|
||||||
stroke: patterns(stroke, +props.strokeOpacity, dimensions, props.svgId),
|
stroke: patterns(stroke, +props.strokeOpacity, props.svgId),
|
||||||
strokeLinecap: caps[props.strokeLinecap] || 0,
|
strokeLinecap: caps[props.strokeLinecap] || 0,
|
||||||
strokeLinejoin: joins[props.strokeLinejoin] || 0,
|
strokeLinejoin: joins[props.strokeLinejoin] || 0,
|
||||||
strokeDasharray: strokeDasharray || null,
|
strokeDasharray: strokeDasharray || null,
|
||||||
@@ -48,8 +48,8 @@ function strokeFilter(props, dimensions) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function(props, dimensions) {
|
export default function(props) {
|
||||||
let strokeProps = strokeFilter(props, dimensions);
|
let strokeProps = strokeFilter(props);
|
||||||
|
|
||||||
return strokeProps ? {
|
return strokeProps ? {
|
||||||
...strokeProps,
|
...strokeProps,
|
||||||
|
|||||||
@@ -75,15 +75,15 @@ function extractFont(font) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const alignments = {
|
const anchord = {
|
||||||
right: 1,
|
end: 1,
|
||||||
center: 2,
|
middle: 2,
|
||||||
left: 0
|
start: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function(props) {
|
export default function(props) {
|
||||||
return {
|
return {
|
||||||
alignment: alignments[props.textAnchor] || 0,
|
alignment: anchord[props.textAnchor] || 0,
|
||||||
frame: extractFontAndLines(
|
frame: extractFontAndLines(
|
||||||
props,
|
props,
|
||||||
childrenAsString(props.children)
|
childrenAsString(props.children)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export default function({viewbox, width, height, preserveAspectRatio, x: dx, y: dy, dScale, dScaleX, dScaleY, shouldTransform}) {
|
export default function({viewbox, width, height, preserveAspectRatio, x: dx, y: dy, dScale, dScaleX, dScaleY}) {
|
||||||
if (!viewbox || !width || !height) {
|
if (!viewbox || !width || !height) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -47,18 +47,18 @@ export default function({viewbox, width, height, preserveAspectRatio, x: dx, y:
|
|||||||
y = -vy * sy;
|
y = -vy * sy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldTransform) {
|
//if (shouldTransform) {
|
||||||
x += (+dx || 0);
|
x += (+dx || 0);
|
||||||
y += (+dy || 0);
|
y += (+dy || 0);
|
||||||
|
|
||||||
if (dScale) {
|
if (dScale) {
|
||||||
scaleX *= (+dScale || 1);
|
scaleX *= (+dScale || 1);
|
||||||
scaleY *= (+dScale || 1);
|
scaleY *= (+dScale || 1);
|
||||||
} else {
|
} else {
|
||||||
scaleX *= (+dScaleX || 1);
|
scaleX *= (+dScaleX || 1);
|
||||||
scaleY *= (+dScaleY || 1);
|
scaleY *= (+dScaleY || 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
//}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x,
|
x,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export {
|
|||||||
remove
|
remove
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function(patternSting, opacity, dimensions, svgId) {
|
export default function(patternSting, opacity, svgId) {
|
||||||
if (isGradient(patternSting)) {
|
if (isGradient(patternSting)) {
|
||||||
return patternSting;
|
return patternSting;
|
||||||
}
|
}
|
||||||
@@ -40,11 +40,7 @@ export default function(patternSting, opacity, dimensions, svgId) {
|
|||||||
let pattern = patterns[patternName];
|
let pattern = patterns[patternName];
|
||||||
|
|
||||||
if (pattern) {
|
if (pattern) {
|
||||||
if (pattern.length === 2) {
|
return pattern(opacity);
|
||||||
return pattern(dimensions, opacity);
|
|
||||||
} else {
|
|
||||||
return pattern(opacity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ function insertColorIntoArray(color, targetArray, atIndex) {
|
|||||||
|
|
||||||
function insertColorsIntoArray(stops, targetArray, atIndex) {
|
function insertColorsIntoArray(stops, targetArray, atIndex) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
if ('length' in stops) {
|
if ('length' in stops) {
|
||||||
while (i < stops.length) {
|
while (i < stops.length) {
|
||||||
insertColorIntoArray(stops[i], targetArray, atIndex + i * 4);
|
insertColorIntoArray(stops[i], targetArray, atIndex + i * 4);
|
||||||
@@ -28,27 +27,28 @@ function insertColorsIntoArray(stops, targetArray, atIndex) {
|
|||||||
return atIndex + i * 4;
|
return atIndex + i * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertColorStopsIntoArray(stops, targetArray, atIndex, multi = 1) {
|
function insertColorStopsIntoArray(stops, targetArray, atIndex) {
|
||||||
let lastIndex = insertColorsIntoArray(stops, targetArray, atIndex, false);
|
let lastIndex = insertColorsIntoArray(stops, targetArray, atIndex);
|
||||||
insertOffsetsIntoArray(stops, targetArray, lastIndex, multi, false);
|
insertOffsetsIntoArray(stops, targetArray, lastIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertOffsetsIntoArray(stops, targetArray, atIndex, multi) {
|
function insertOffsetsIntoArray(stops, targetArray, atIndex) {
|
||||||
let offsetNumber;
|
let offsetNumber;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let arr = [];
|
let arr = [];
|
||||||
if ('length' in stops) {
|
if ('length' in stops) {
|
||||||
while (i < stops.length) {
|
while (i < stops.length) {
|
||||||
offsetNumber = i / (stops.length - 1) * multi;
|
offsetNumber = i / (stops.length - 1);
|
||||||
targetArray[atIndex + i] = offsetNumber;
|
targetArray[atIndex + i] = offsetNumber;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_.forEach(stops, (stop, offsetString) => {
|
_.forEach(stops, (stop, offsetString) => {
|
||||||
offsetNumber = (+offsetString) * multi;
|
offsetNumber = (+offsetString);
|
||||||
arr.push(offsetNumber);
|
arr.push(offsetNumber);
|
||||||
i++;
|
i++;
|
||||||
});
|
});
|
||||||
|
|
||||||
arr.sort();
|
arr.sort();
|
||||||
targetArray.splice(atIndex, 0, ...arr);
|
targetArray.splice(atIndex, 0, ...arr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
let percentReg = /^(\-?\d+(?:\.\d+)?)(%?)$/;
|
let percentReg = /^(\-?\d+(?:\.\d+)?)(%?)$/;
|
||||||
export default function (percent) {
|
export default function (percent) {
|
||||||
let matched = percent.match(percentReg);
|
let matched = percent.match(percentReg);
|
||||||
|
if (!matched) {
|
||||||
|
console.warn(`\`${percent}\` is not a valid number or percentage string.`);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return matched[2] ? matched[1] / 100 : +matched[1];
|
return matched[2] ? matched[1] / 100 : +matched[1];
|
||||||
}
|
}
|
||||||
|
|||||||
110
lib/props.js
Normal file
110
lib/props.js
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import {
|
||||||
|
PropTypes
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
const numberProp = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
|
||||||
|
|
||||||
|
const fillProps = {
|
||||||
|
fill: PropTypes.string,
|
||||||
|
fillOpacity: numberProp,
|
||||||
|
fillRule: PropTypes.oneOf(['evenodd', 'nonzero'])
|
||||||
|
};
|
||||||
|
|
||||||
|
const clipProps = {
|
||||||
|
clipRule: PropTypes.oneOf(['evenodd', 'nonzero']),
|
||||||
|
clipPath: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
const strokeProps = {
|
||||||
|
stroke: PropTypes.string,
|
||||||
|
strokeWidth: numberProp,
|
||||||
|
strokeOpacity: numberProp,
|
||||||
|
strokeDasharray: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.string]),
|
||||||
|
strokeDashoffset: numberProp,
|
||||||
|
strokeLinecap: PropTypes.oneOf(['butt', 'square', 'round']),
|
||||||
|
strokeLinejoin: PropTypes.oneOf(['miter', 'bevel', 'round'])
|
||||||
|
};
|
||||||
|
|
||||||
|
const textProps = {
|
||||||
|
textAnchor: PropTypes.oneOf(['start', 'middle', 'end']),
|
||||||
|
path: PropTypes.string,
|
||||||
|
fontFamily: PropTypes.string,
|
||||||
|
fontSize: numberProp,
|
||||||
|
fontWeight: PropTypes.string,
|
||||||
|
fontStyle: PropTypes.string,
|
||||||
|
font: PropTypes.object,
|
||||||
|
lines: numberProp
|
||||||
|
};
|
||||||
|
|
||||||
|
const transformProps = {
|
||||||
|
scale: numberProp,
|
||||||
|
scaleX: numberProp,
|
||||||
|
scaleY: numberProp,
|
||||||
|
rotate: numberProp,
|
||||||
|
x: numberProp,
|
||||||
|
y: numberProp,
|
||||||
|
originX: numberProp,
|
||||||
|
originY: numberProp,
|
||||||
|
transform: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
const pathProps = {
|
||||||
|
...fillProps,
|
||||||
|
...strokeProps,
|
||||||
|
...clipProps,
|
||||||
|
...transformProps
|
||||||
|
};
|
||||||
|
|
||||||
|
const circleProps = {
|
||||||
|
cx: numberProp,
|
||||||
|
cy: numberProp,
|
||||||
|
r: numberProp
|
||||||
|
};
|
||||||
|
|
||||||
|
const ellipseProps = {
|
||||||
|
cx: numberProp,
|
||||||
|
cy: numberProp,
|
||||||
|
rx: numberProp,
|
||||||
|
ry: numberProp
|
||||||
|
};
|
||||||
|
|
||||||
|
const lineProps = {
|
||||||
|
x1: numberProp,
|
||||||
|
x2: numberProp,
|
||||||
|
y1: numberProp,
|
||||||
|
y2: numberProp
|
||||||
|
};
|
||||||
|
|
||||||
|
const rectProps = {
|
||||||
|
x: numberProp,
|
||||||
|
y: numberProp,
|
||||||
|
width: numberProp,
|
||||||
|
height: numberProp,
|
||||||
|
rx: numberProp,
|
||||||
|
ry: numberProp
|
||||||
|
};
|
||||||
|
|
||||||
|
const contextProps = {
|
||||||
|
...circleProps,
|
||||||
|
...ellipseProps,
|
||||||
|
...lineProps,
|
||||||
|
...rectProps,
|
||||||
|
...fillProps,
|
||||||
|
...strokeProps,
|
||||||
|
...textProps
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
numberProp,
|
||||||
|
fillProps,
|
||||||
|
clipProps,
|
||||||
|
strokeProps,
|
||||||
|
transformProps,
|
||||||
|
textProps,
|
||||||
|
circleProps,
|
||||||
|
ellipseProps,
|
||||||
|
lineProps,
|
||||||
|
rectProps,
|
||||||
|
contextProps,
|
||||||
|
pathProps
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
export default function (stops, opacity) {
|
export default function (stops, opacity) {
|
||||||
return _.reduce(stops, (ret, color, key) => {
|
return _.reduce(stops, (ret, color, key) => {
|
||||||
ret[key] = color.alpha(opacity).rgbaString();
|
ret[key] = color.alpha(color.alpha() * opacity).rgbaString();
|
||||||
return ret;
|
return ret;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user