mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-19 21:45:10 +00:00
support stroke in pattern.
support stroke="url(#pattern)"
This commit is contained in:
@@ -185,7 +185,7 @@ class FillGradientWithOpacity extends Component{
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FillGradientInRect extends Component{
|
class FillGradientInRect extends Component{
|
||||||
static title = 'Fill a radial gradient inside a rect';
|
static title = 'Fill a radial gradient inside a rect and stroke it';
|
||||||
render() {
|
render() {
|
||||||
return <Svg
|
return <Svg
|
||||||
height="150"
|
height="150"
|
||||||
@@ -205,7 +205,7 @@ class FillGradientInRect extends Component{
|
|||||||
/>
|
/>
|
||||||
</RadialGradient>
|
</RadialGradient>
|
||||||
</Defs>
|
</Defs>
|
||||||
<Rect x="0" y="0" width="300" height="150" fill="url(#grad)" />
|
<Rect x="5" y="5" width="290" height="130" fill="url(#grad)" stroke="pink" strokeWidth="5" />
|
||||||
</Svg>;
|
</Svg>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class RectExample extends Component{
|
|||||||
fill="rgb(0,0,255)"
|
fill="rgb(0,0,255)"
|
||||||
strokeWidth="3"
|
strokeWidth="3"
|
||||||
stroke="rgb(0,0,0)"
|
stroke="rgb(0,0,0)"
|
||||||
|
strokeDasharray="5,10"
|
||||||
/>
|
/>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
}
|
}
|
||||||
@@ -39,10 +40,10 @@ class RectStrokeFill extends Component{
|
|||||||
width="75"
|
width="75"
|
||||||
height="75"
|
height="75"
|
||||||
fill="blue"
|
fill="blue"
|
||||||
fillOpacity="0.1"
|
fillOpacity="0.5"
|
||||||
stroke="pink"
|
stroke="red"
|
||||||
strokeWidth="5"
|
strokeWidth="5"
|
||||||
strokeOpacity="0.9"
|
strokeOpacity="0.5"
|
||||||
/>
|
/>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import React, {
|
|||||||
|
|
||||||
import Svg, {
|
import Svg, {
|
||||||
Path,
|
Path,
|
||||||
G
|
Rect,
|
||||||
|
G,
|
||||||
|
Defs,
|
||||||
|
Stop,
|
||||||
|
RadialGradient,
|
||||||
|
Polyline
|
||||||
} from 'react-native-svg';
|
} from 'react-native-svg';
|
||||||
|
|
||||||
class StrokeExample extends Component{
|
class StrokeExample extends Component{
|
||||||
@@ -59,6 +64,48 @@ class StrokeDasharray extends Component{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class StrokePattern extends Component{
|
||||||
|
static title = 'Stroke with gradient.';
|
||||||
|
render() {
|
||||||
|
return <Svg height="80" width="200">
|
||||||
|
<Defs>
|
||||||
|
<RadialGradient id="grad" cx="50%" cy="50%" rx="80%" ry="80%" fx="50%" fy="50%">
|
||||||
|
<Stop
|
||||||
|
offset="50%"
|
||||||
|
stopColor="#fff"
|
||||||
|
stopOpacity="1"
|
||||||
|
/>
|
||||||
|
<Stop
|
||||||
|
offset="100%"
|
||||||
|
stopColor="#f00"
|
||||||
|
stopOpacity="1"
|
||||||
|
/>
|
||||||
|
</RadialGradient>
|
||||||
|
</Defs>
|
||||||
|
<Rect
|
||||||
|
x="5"
|
||||||
|
y="5"
|
||||||
|
height="70"
|
||||||
|
width="190"
|
||||||
|
fill="blue"
|
||||||
|
stroke="url(#grad)"
|
||||||
|
strokeWidth="5"
|
||||||
|
strokeDasharray="10"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Polyline
|
||||||
|
strokeDasharray="20,10,5,5,5,10"
|
||||||
|
points="10,10 20,12 30,20 40,60 60,70 90,55"
|
||||||
|
fill="none"
|
||||||
|
stroke="url(#grad)"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeWidth="5"
|
||||||
|
/>
|
||||||
|
</Svg>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const icon = <Svg
|
const icon = <Svg
|
||||||
height="20"
|
height="20"
|
||||||
width="20"
|
width="20"
|
||||||
@@ -70,7 +117,7 @@ const icon = <Svg
|
|||||||
</G>
|
</G>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
|
|
||||||
const samples = [StrokeExample, StrokeWidth, StrokeLinecap, StrokeDasharray];
|
const samples = [StrokeExample, StrokeWidth, StrokeLinecap, StrokeDasharray, StrokePattern];
|
||||||
|
|
||||||
export {
|
export {
|
||||||
icon,
|
icon,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import React, {
|
|||||||
Children
|
Children
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {NativeGroup} from './G';
|
import {NativeGroup} from './G';
|
||||||
import {set, remove} from '../lib/extract/extractFill';
|
import {set, remove} from '../lib/extract/patterns';
|
||||||
import percentFactory from '../lib/percentFactory';
|
import percentFactory from '../lib/percentFactory';
|
||||||
import percentToFloat from '../lib/percentToFloat';
|
import percentToFloat from '../lib/percentToFloat';
|
||||||
import Stop from './Stop';
|
import Stop from './Stop';
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
*/
|
*/
|
||||||
- (BOOL)applyFillColor:(CGContextRef)context;
|
- (BOOL)applyFillColor:(CGContextRef)context;
|
||||||
|
|
||||||
|
- (BOOL)applyStrokeColor:(CGContextRef)context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)applyStrokeColor:(CGContextRef)context
|
||||||
|
{
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)paint:(CGContextRef)context
|
- (void)paint:(CGContextRef)context
|
||||||
{
|
{
|
||||||
// abstract
|
// abstract
|
||||||
|
|||||||
@@ -13,26 +13,32 @@
|
|||||||
|
|
||||||
@implementation RNSVGSolidColor
|
@implementation RNSVGSolidColor
|
||||||
{
|
{
|
||||||
CGColorRef _color;
|
CGColorRef _color;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithArray:(NSArray<NSNumber *> *)array
|
- (instancetype)initWithArray:(NSArray<NSNumber *> *)array
|
||||||
{
|
{
|
||||||
if ((self = [super initWithArray:array])) {
|
if ((self = [super initWithArray:array])) {
|
||||||
_color = CGColorRetain([RCTConvert CGColor:array offset:1]);
|
_color = CGColorRetain([RCTConvert CGColor:array offset:1]);
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
CGColorRelease(_color);
|
CGColorRelease(_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)applyFillColor:(CGContextRef)context
|
- (BOOL)applyFillColor:(CGContextRef)context
|
||||||
{
|
{
|
||||||
CGContextSetFillColorWithColor(context, _color);
|
CGContextSetFillColorWithColor(context, _color);
|
||||||
return YES;
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)applyStrokeColor:(CGContextRef)context
|
||||||
|
{
|
||||||
|
CGContextSetStrokeColorWithColor(context, _color);
|
||||||
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -19,210 +19,211 @@
|
|||||||
|
|
||||||
+ (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) {
|
||||||
NSUInteger type = [arr[i++] unsignedIntegerValue];
|
NSUInteger type = [arr[i++] unsignedIntegerValue];
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 0:
|
case 0:
|
||||||
CGPathMoveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
|
CGPathMoveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
CGPathCloseSubpath(path);
|
CGPathCloseSubpath(path);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
CGPathAddLineToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
|
CGPathAddLineToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
CGPathAddCurveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE);
|
CGPathAddCurveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0);
|
CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
RCTLogError(@"Invalid CGPath type %zd at element %zd of %@", type, i, arr);
|
RCTLogError(@"Invalid CGPath type %zd at element %zd of %@", type, i, arr);
|
||||||
CGPathRelease(path);
|
CGPathRelease(path);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
@catch (NSException *exception) {
|
||||||
@catch (NSException *exception) {
|
RCTLogError(@"Invalid CGPath format: %@", arr);
|
||||||
RCTLogError(@"Invalid CGPath format: %@", arr);
|
CGPathRelease(path);
|
||||||
CGPathRelease(path);
|
return NULL;
|
||||||
return NULL;
|
}
|
||||||
}
|
|
||||||
|
return (CGPathRef)CFAutorelease(path);
|
||||||
return (CGPathRef)CFAutorelease(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_ENUM_CONVERTER(CTTextAlignment, (@{
|
RCT_ENUM_CONVERTER(CTTextAlignment, (@{
|
||||||
@"auto": @(kCTTextAlignmentNatural),
|
@"auto": @(kCTTextAlignmentNatural),
|
||||||
@"left": @(kCTTextAlignmentLeft),
|
@"left": @(kCTTextAlignmentLeft),
|
||||||
@"center": @(kCTTextAlignmentCenter),
|
@"center": @(kCTTextAlignmentCenter),
|
||||||
@"right": @(kCTTextAlignmentRight),
|
@"right": @(kCTTextAlignmentRight),
|
||||||
@"justify": @(kCTTextAlignmentJustified),
|
@"justify": @(kCTTextAlignmentJustified),
|
||||||
}), kCTTextAlignmentNatural, integerValue)
|
}), kCTTextAlignmentNatural, integerValue)
|
||||||
|
|
||||||
RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
|
RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
|
||||||
@"evenodd": @(kRNSVGCGFCRuleEvenodd),
|
@"evenodd": @(kRNSVGCGFCRuleEvenodd),
|
||||||
@"nonzero": @(kRNSVGCGFCRuleNonzero),
|
@"nonzero": @(kRNSVGCGFCRuleNonzero),
|
||||||
}), kRNSVGCGFCRuleNonzero, intValue)
|
}), kRNSVGCGFCRuleNonzero, intValue)
|
||||||
|
|
||||||
// This takes a tuple of text lines and a font to generate a CTLine for each text line.
|
// This takes a tuple of text lines and a font to generate a CTLine for each text line.
|
||||||
// This prepares everything for rendering a frame of text in RNSVGText.
|
// This prepares everything for rendering a frame of text in RNSVGText.
|
||||||
+ (RNSVGTextFrame)RNSVGTextFrame:(id)json
|
+ (RNSVGTextFrame)RNSVGTextFrame:(id)json
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
if (!font) {
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a dictionary for this font
|
||||||
|
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
|
||||||
|
(NSString *)kCTFontAttributeName: (__bridge id)font,
|
||||||
|
(NSString *)kCTForegroundColorFromContextAttributeName: @YES
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set up text frame with font metrics
|
||||||
|
CGFloat size = CTFontGetSize(font);
|
||||||
|
frame.count = lineCount;
|
||||||
|
frame.baseLine = size; // estimate base line
|
||||||
|
frame.lineHeight = size * 1.1; // Base on RNSVG canvas line height estimate
|
||||||
|
frame.lines = malloc(sizeof(CTLineRef) * lineCount);
|
||||||
|
frame.widths = malloc(sizeof(CGFloat) * lineCount);
|
||||||
|
|
||||||
|
[lines enumerateObjectsUsingBlock:^(NSString *text, NSUInteger i, BOOL *stop) {
|
||||||
|
|
||||||
|
CFStringRef string = (__bridge CFStringRef)text;
|
||||||
|
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
|
||||||
|
CTLineRef line = CTLineCreateWithAttributedString(attrString);
|
||||||
|
CFRelease(attrString);
|
||||||
|
|
||||||
|
frame.lines[i] = line;
|
||||||
|
frame.widths[i] = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
|
||||||
|
}];
|
||||||
|
|
||||||
return frame;
|
return frame;
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
if (!font) {
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a dictionary for this font
|
|
||||||
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
|
|
||||||
(NSString *)kCTFontAttributeName: (__bridge id)font,
|
|
||||||
(NSString *)kCTForegroundColorFromContextAttributeName: @YES
|
|
||||||
};
|
|
||||||
|
|
||||||
// Set up text frame with font metrics
|
|
||||||
CGFloat size = CTFontGetSize(font);
|
|
||||||
frame.count = lineCount;
|
|
||||||
frame.baseLine = size; // estimate base line
|
|
||||||
frame.lineHeight = size * 1.1; // Base on RNSVG canvas line height estimate
|
|
||||||
frame.lines = malloc(sizeof(CTLineRef) * lineCount);
|
|
||||||
frame.widths = malloc(sizeof(CGFloat) * lineCount);
|
|
||||||
|
|
||||||
[lines enumerateObjectsUsingBlock:^(NSString *text, NSUInteger i, BOOL *stop) {
|
|
||||||
|
|
||||||
CFStringRef string = (__bridge CFStringRef)text;
|
|
||||||
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
|
|
||||||
CTLineRef line = CTLineCreateWithAttributedString(attrString);
|
|
||||||
CFRelease(attrString);
|
|
||||||
|
|
||||||
frame.lines[i] = line;
|
|
||||||
frame.widths[i] = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
|
|
||||||
}];
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (RNSVGCGFloatArray)RNSVGCGFloatArray:(id)json
|
+ (RNSVGCGFloatArray)RNSVGCGFloatArray:(id)json
|
||||||
{
|
{
|
||||||
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.
|
||||||
array.array = malloc(sizeof(CGFloat) * count);
|
array.array = malloc(sizeof(CGFloat) * count);
|
||||||
for (NSUInteger i = 0; i < count; i++) {
|
for (NSUInteger i = 0; i < count; i++) {
|
||||||
array.array[i] = [arr[i] doubleValue];
|
array.array[i] = [arr[i] doubleValue];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return array;
|
||||||
return array;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (RNSVGBrush *)RNSVGBrush:(id)json
|
+ (RNSVGBrush *)RNSVGBrush:(id)json
|
||||||
{
|
{
|
||||||
NSArray *arr = [self NSArray:json];
|
NSArray *arr = [self NSArray:json];
|
||||||
NSUInteger type = [self NSUInteger:arr.firstObject];
|
NSUInteger type = [self NSUInteger:arr.firstObject];
|
||||||
switch (type) {
|
|
||||||
case 0: // solid color
|
switch (type) {
|
||||||
// These are probably expensive allocations since it's often the same value.
|
case 0: // solid color
|
||||||
// We should memoize colors but look ups may be just as expensive.
|
// These are probably expensive allocations since it's often the same value.
|
||||||
return [[RNSVGSolidColor alloc] initWithArray:arr];
|
// We should memoize colors but look ups may be just as expensive.
|
||||||
case 1: // linear gradient
|
return [[RNSVGSolidColor alloc] initWithArray:arr];
|
||||||
return [[RNSVGLinearGradient alloc] initWithArray:arr];
|
case 1: // linear gradient
|
||||||
case 2: // radial gradient
|
return [[RNSVGLinearGradient alloc] initWithArray:arr];
|
||||||
return [[RNSVGRadialGradient alloc] initWithArray:arr];
|
case 2: // radial gradient
|
||||||
case 3: // pattern
|
return [[RNSVGRadialGradient alloc] initWithArray:arr];
|
||||||
return [[RNSVGPattern alloc] initWithArray:arr];
|
case 3: // pattern
|
||||||
default:
|
return [[RNSVGPattern alloc] initWithArray:arr];
|
||||||
RCTLogError(@"Unknown brush type: %zd", type);
|
default:
|
||||||
return nil;
|
RCTLogError(@"Unknown brush type: %zd", type);
|
||||||
}
|
return nil;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset
|
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset
|
||||||
{
|
{
|
||||||
NSArray *arr = [self NSArray:json];
|
NSArray *arr = [self NSArray:json];
|
||||||
if (arr.count < offset + 2) {
|
if (arr.count < offset + 2) {
|
||||||
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 2 + offset, arr);
|
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 2 + offset, arr);
|
||||||
return CGPointZero;
|
return CGPointZero;
|
||||||
}
|
}
|
||||||
return (CGPoint){
|
return (CGPoint){
|
||||||
[self CGFloat:arr[offset]],
|
[self CGFloat:arr[offset]],
|
||||||
[self CGFloat:arr[offset + 1]],
|
[self CGFloat:arr[offset + 1]],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset
|
+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset
|
||||||
{
|
{
|
||||||
NSArray *arr = [self NSArray:json];
|
NSArray *arr = [self NSArray:json];
|
||||||
if (arr.count < offset + 4) {
|
if (arr.count < offset + 4) {
|
||||||
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr);
|
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr);
|
||||||
return CGRectZero;
|
return CGRectZero;
|
||||||
}
|
}
|
||||||
return (CGRect){
|
return (CGRect){
|
||||||
{[self CGFloat:arr[offset]], [self CGFloat:arr[offset + 1]]},
|
{[self CGFloat:arr[offset]], [self CGFloat:arr[offset + 1]]},
|
||||||
{[self CGFloat:arr[offset + 2]], [self CGFloat:arr[offset + 3]]},
|
{[self CGFloat:arr[offset + 2]], [self CGFloat:arr[offset + 3]]},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset
|
+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset
|
||||||
{
|
{
|
||||||
NSArray *arr = [self NSArray:json];
|
NSArray *arr = [self NSArray:json];
|
||||||
if (arr.count < offset + 4) {
|
if (arr.count < offset + 4) {
|
||||||
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr);
|
RCTLogError(@"Too few elements in array (expected at least %zd): %@", 4 + offset, arr);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return [self CGColor:[arr subarrayWithRange:(NSRange){offset, 4}]];
|
return [self CGColor:[arr subarrayWithRange:(NSRange){offset, 4}]];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset
|
+ (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset
|
||||||
{
|
{
|
||||||
NSArray *arr = [self NSArray:json];
|
NSArray *arr = [self NSArray:json];
|
||||||
if (arr.count < offset) {
|
if (arr.count < offset) {
|
||||||
RCTLogError(@"Too few elements in array (expected at least %zd): %@", offset, arr);
|
RCTLogError(@"Too few elements in array (expected at least %zd): %@", offset, arr);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
arr = [arr subarrayWithRange:(NSRange){offset, arr.count - offset}];
|
arr = [arr subarrayWithRange:(NSRange){offset, arr.count - offset}];
|
||||||
RNSVGCGFloatArray colorsAndOffsets = [self RNSVGCGFloatArray:arr];
|
RNSVGCGFloatArray colorsAndOffsets = [self RNSVGCGFloatArray:arr];
|
||||||
size_t stops = colorsAndOffsets.count / 5;
|
size_t stops = colorsAndOffsets.count / 5;
|
||||||
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
|
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
|
||||||
CGGradientRef gradient = CGGradientCreateWithColorComponents(
|
CGGradientRef gradient = CGGradientCreateWithColorComponents(
|
||||||
rgb,
|
rgb,
|
||||||
colorsAndOffsets.array,
|
colorsAndOffsets.array,
|
||||||
colorsAndOffsets.array + stops * 4,
|
colorsAndOffsets.array + stops * 4,
|
||||||
stops
|
stops
|
||||||
);
|
);
|
||||||
CGColorSpaceRelease(rgb);
|
CGColorSpaceRelease(rgb);
|
||||||
free(colorsAndOffsets.array);
|
free(colorsAndOffsets.array);
|
||||||
return (CGGradientRef)CFAutorelease(gradient);
|
return (CGGradientRef)CFAutorelease(gradient);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -32,10 +32,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
CGPathDrawingMode mode = kCGPathStroke;
|
CGPathDrawingMode mode = kCGPathStroke;
|
||||||
|
BOOL fillColor = YES;
|
||||||
|
|
||||||
if (self.fill) {
|
if (self.fill) {
|
||||||
if ([self.fill applyFillColor:context]) {
|
mode = self.fillRule == kRNSVGCGFCRuleEvenodd ? kCGPathEOFill : kCGPathFill;
|
||||||
mode = self.fillRule == kRNSVGCGFCRuleEvenodd ? kCGPathEOFill : kCGPathFill;
|
fillColor = [self.fill applyFillColor:context];
|
||||||
} else {
|
|
||||||
|
if (!fillColor) {
|
||||||
if (self.clipPath) {
|
if (self.clipPath) {
|
||||||
[self clip:context];
|
[self clip:context];
|
||||||
}
|
}
|
||||||
@@ -51,23 +54,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (self.stroke) {
|
if (self.stroke) {
|
||||||
CGContextSetStrokeColorWithColor(context, self.stroke);
|
|
||||||
CGContextSetLineWidth(context, self.strokeWidth);
|
CGContextSetLineWidth(context, self.strokeWidth);
|
||||||
CGContextSetLineCap(context, self.strokeLinecap);
|
CGContextSetLineCap(context, self.strokeLinecap);
|
||||||
CGContextSetLineJoin(context, self.strokeLinejoin);
|
CGContextSetLineJoin(context, self.strokeLinejoin);
|
||||||
RNSVGCGFloatArray dash = self.strokeDash;
|
RNSVGCGFloatArray dash = self.strokeDash;
|
||||||
|
|
||||||
|
// TODO: render as web svgs do
|
||||||
if (dash.count) {
|
if (dash.count) {
|
||||||
CGContextSetLineDash(context, 0, dash.array, dash.count);
|
CGContextSetLineDash(context, 0, dash.array, dash.count);
|
||||||
}
|
}
|
||||||
if (mode == kCGPathFill) {
|
|
||||||
mode = kCGPathFillStroke;
|
if (!fillColor) {
|
||||||
} else if (mode == kCGPathEOFill) {
|
CGContextAddPath(context, self.d);
|
||||||
mode = kCGPathEOFillStroke;
|
CGContextReplacePathWithStrokedPath(context);
|
||||||
|
CGContextClip(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([self.stroke applyStrokeColor:context]) {
|
||||||
|
|
||||||
|
if (mode == kCGPathFill) {
|
||||||
|
mode = kCGPathFillStroke;
|
||||||
|
} else if (mode == kCGPathEOFill) {
|
||||||
|
mode = kCGPathEOFillStroke;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// draw fill
|
||||||
|
[self clip:context];
|
||||||
|
CGContextAddPath(context, self.d);
|
||||||
|
CGContextDrawPath(context, mode);
|
||||||
|
|
||||||
|
// draw stroke
|
||||||
|
CGContextAddPath(context, self.d);
|
||||||
|
CGContextReplacePathWithStrokedPath(context);
|
||||||
|
CGContextClip(context);
|
||||||
|
[self.stroke paint:context];
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[self clip:context];
|
[self clip:context];
|
||||||
|
|
||||||
CGContextAddPath(context, self.d);
|
CGContextAddPath(context, self.d);
|
||||||
CGContextDrawPath(context, mode);
|
CGContextDrawPath(context, mode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
@property (nonatomic, strong) RNSVGBrush *fill;
|
@property (nonatomic, strong) RNSVGBrush *fill;
|
||||||
@property (nonatomic, assign) RNSVGCGFCRule fillRule;
|
@property (nonatomic, assign) RNSVGCGFCRule fillRule;
|
||||||
@property (nonatomic, assign) CGColorRef stroke;
|
@property (nonatomic, strong) RNSVGBrush *stroke;
|
||||||
@property (nonatomic, assign) CGFloat strokeWidth;
|
@property (nonatomic, assign) CGFloat strokeWidth;
|
||||||
@property (nonatomic, assign) CGLineCap strokeLinecap;
|
@property (nonatomic, assign) CGLineCap strokeLinecap;
|
||||||
@property (nonatomic, assign) CGLineJoin strokeLinejoin;
|
@property (nonatomic, assign) CGLineJoin strokeLinejoin;
|
||||||
|
|||||||
@@ -16,14 +16,10 @@
|
|||||||
_fill = fill;
|
_fill = fill;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setStroke:(CGColorRef)stroke
|
- (void)setStroke:(RNSVGBrush *)stroke
|
||||||
{
|
{
|
||||||
if (stroke == _stroke) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
[self invalidate];
|
[self invalidate];
|
||||||
CGColorRelease(_stroke);
|
_stroke = stroke;
|
||||||
_stroke = CGColorRetain(stroke);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setStrokeWidth:(CGFloat)strokeWidth
|
- (void)setStrokeWidth:(CGFloat)strokeWidth
|
||||||
@@ -58,7 +54,6 @@
|
|||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
CGColorRelease(_stroke);
|
|
||||||
if (_strokeDash.array) {
|
if (_strokeDash.array) {
|
||||||
free(_strokeDash.array);
|
free(_strokeDash.array);
|
||||||
}
|
}
|
||||||
|
|||||||
169
ios/RNSVGText.m
169
ios/RNSVGText.m
@@ -14,114 +14,117 @@
|
|||||||
|
|
||||||
- (void)setAlignment:(CTTextAlignment)alignment
|
- (void)setAlignment:(CTTextAlignment)alignment
|
||||||
{
|
{
|
||||||
[self invalidate];
|
[self invalidate];
|
||||||
_alignment = alignment;
|
_alignment = alignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
|
static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
|
||||||
{
|
{
|
||||||
if (frame.count) {
|
if (frame.count) {
|
||||||
// We must release each line before freeing up this struct
|
// We must release each line before freeing up this struct
|
||||||
for (int i = 0; i < frame.count; i++) {
|
for (int i = 0; i < frame.count; i++) {
|
||||||
CFRelease(frame.lines[i]);
|
CFRelease(frame.lines[i]);
|
||||||
|
}
|
||||||
|
free(frame.lines);
|
||||||
|
free(frame.widths);
|
||||||
}
|
}
|
||||||
free(frame.lines);
|
|
||||||
free(frame.widths);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setTextFrame:(RNSVGTextFrame)frame
|
- (void)setTextFrame:(RNSVGTextFrame)frame
|
||||||
{
|
{
|
||||||
if (frame.lines != _textFrame.lines) {
|
if (frame.lines != _textFrame.lines) {
|
||||||
RNSVGFreeTextFrame(_textFrame);
|
RNSVGFreeTextFrame(_textFrame);
|
||||||
}
|
}
|
||||||
[self invalidate];
|
[self invalidate];
|
||||||
_textFrame = frame;
|
_textFrame = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
RNSVGFreeTextFrame(_textFrame);
|
RNSVGFreeTextFrame(_textFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)renderLayerTo:(CGContextRef)context
|
- (void)renderLayerTo:(CGContextRef)context
|
||||||
{
|
{
|
||||||
RNSVGTextFrame frame = self.textFrame;
|
RNSVGTextFrame frame = self.textFrame;
|
||||||
|
|
||||||
if ((!self.fill && !self.stroke) || !frame.count) {
|
if ((!self.fill && !self.stroke) || !frame.count) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// to-do: draw along a path
|
|
||||||
// to-do: fill-rule
|
|
||||||
// to-do: clip
|
|
||||||
CGTextDrawingMode mode = kCGTextStroke;
|
|
||||||
if (self.fill) {
|
|
||||||
if ([self.fill applyFillColor:context]) {
|
|
||||||
mode = kCGTextFill;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
for (int i = 0; i < frame.count; i++) {
|
|
||||||
CGContextSaveGState(context);
|
|
||||||
// Inverse the coordinate space since CoreText assumes a bottom-up coordinate space
|
|
||||||
CGContextScaleCTM(context, 1.0, -1.0);
|
|
||||||
CGContextSetTextDrawingMode(context, kCGTextClip);
|
|
||||||
[self renderLineTo:context atIndex:i];
|
|
||||||
// Inverse the coordinate space back to the original before filling
|
|
||||||
CGContextScaleCTM(context, 1.0, -1.0);
|
|
||||||
[self.fill paint:context];
|
|
||||||
// Restore the state so that the next line can be clipped separately
|
|
||||||
CGContextRestoreGState(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!self.stroke) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (self.stroke) {
|
// to-do: draw along a path
|
||||||
CGContextSetStrokeColorWithColor(context, self.stroke);
|
// to-do: fill-rule
|
||||||
CGContextSetLineWidth(context, self.strokeWidth);
|
// to-do: clip
|
||||||
CGContextSetLineCap(context, self.strokeLinecap);
|
CGTextDrawingMode mode = kCGTextStroke;
|
||||||
CGContextSetLineJoin(context, self.strokeLinejoin);
|
if (self.fill) {
|
||||||
RNSVGCGFloatArray dash = self.strokeDash;
|
if ([self.fill applyFillColor:context]) {
|
||||||
if (dash.count) {
|
mode = kCGTextFill;
|
||||||
CGContextSetLineDash(context, 0, dash.array, dash.count);
|
} else {
|
||||||
|
|
||||||
|
for (int i = 0; i < frame.count; i++) {
|
||||||
|
CGContextSaveGState(context);
|
||||||
|
// Inverse the coordinate space since CoreText assumes a bottom-up coordinate space
|
||||||
|
CGContextScaleCTM(context, 1.0, -1.0);
|
||||||
|
CGContextSetTextDrawingMode(context, kCGTextClip);
|
||||||
|
[self renderLineTo:context atIndex:i];
|
||||||
|
// Inverse the coordinate space back to the original before filling
|
||||||
|
CGContextScaleCTM(context, 1.0, -1.0);
|
||||||
|
[self.fill paint:context];
|
||||||
|
// Restore the state so that the next line can be clipped separately
|
||||||
|
CGContextRestoreGState(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self.stroke) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mode == kCGTextFill) {
|
if (self.stroke) {
|
||||||
mode = kCGTextFillStroke;
|
if ([self.stroke applyStrokeColor:context] == NO) {
|
||||||
|
[self.stroke paint:context];
|
||||||
|
}
|
||||||
|
|
||||||
|
CGContextSetLineWidth(context, self.strokeWidth);
|
||||||
|
CGContextSetLineCap(context, self.strokeLinecap);
|
||||||
|
CGContextSetLineJoin(context, self.strokeLinejoin);
|
||||||
|
RNSVGCGFloatArray dash = self.strokeDash;
|
||||||
|
if (dash.count) {
|
||||||
|
CGContextSetLineDash(context, 0, dash.array, dash.count);
|
||||||
|
}
|
||||||
|
if (mode == kCGTextFill) {
|
||||||
|
mode = kCGTextFillStroke;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CGContextSetTextDrawingMode(context, mode);
|
||||||
|
|
||||||
|
// Inverse the coordinate space since CoreText assumes a bottom-up coordinate space
|
||||||
|
CGContextScaleCTM(context, 1.0, -1.0);
|
||||||
|
for (int i = 0; i < frame.count; i++) {
|
||||||
|
[self renderLineTo:context atIndex:i];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
CGContextSetTextDrawingMode(context, mode);
|
|
||||||
|
|
||||||
// Inverse the coordinate space since CoreText assumes a bottom-up coordinate space
|
|
||||||
CGContextScaleCTM(context, 1.0, -1.0);
|
|
||||||
for (int i = 0; i < frame.count; i++) {
|
|
||||||
[self renderLineTo:context atIndex:i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)renderLineTo:(CGContextRef)context atIndex:(int)index
|
- (void)renderLineTo:(CGContextRef)context atIndex:(int)index
|
||||||
{
|
{
|
||||||
RNSVGTextFrame frame = self.textFrame;
|
RNSVGTextFrame frame = self.textFrame;
|
||||||
CGFloat shift;
|
CGFloat shift;
|
||||||
switch (self.alignment) {
|
switch (self.alignment) {
|
||||||
case kCTTextAlignmentRight:
|
case kCTTextAlignmentRight:
|
||||||
shift = frame.widths[index];
|
shift = frame.widths[index];
|
||||||
break;
|
break;
|
||||||
case kCTTextAlignmentCenter:
|
case kCTTextAlignmentCenter:
|
||||||
shift = (frame.widths[index] / 2);
|
shift = (frame.widths[index] / 2);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
shift = 0;
|
shift = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// We should consider snapping this shift to device pixels to improve rendering quality
|
// We should consider snapping this shift to device pixels to improve rendering quality
|
||||||
// when a line has subpixel width.
|
// when a line has subpixel width.
|
||||||
CGContextSetTextPosition(context, -shift, -frame.baseLine - frame.lineHeight * index);
|
CGContextSetTextPosition(context, -shift, -frame.baseLine - frame.lineHeight * index);
|
||||||
CTLineRef line = frame.lines[index];
|
CTLineRef line = frame.lines[index];
|
||||||
CTLineDraw(line, context);
|
CTLineDraw(line, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ RCT_EXPORT_MODULE()
|
|||||||
return [RNSVGRenderable new];
|
return [RNSVGRenderable new];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(fill, RNSVGBrush)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(fillRule, RNSVGCGFCRule)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(stroke, RNSVGBrush)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(strokeWidth, CGFloat)
|
RCT_EXPORT_VIEW_PROPERTY(strokeWidth, CGFloat)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(strokeLinecap, CGLineCap)
|
RCT_EXPORT_VIEW_PROPERTY(strokeLinecap, CGLineCap)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(strokeLinejoin, CGLineJoin)
|
RCT_EXPORT_VIEW_PROPERTY(strokeLinejoin, CGLineJoin)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(fill, RNSVGBrush)
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(fillRule, RNSVGCGFCRule)
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(stroke, CGColor)
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(strokeDash, RNSVGCGFloatArray)
|
RCT_EXPORT_VIEW_PROPERTY(strokeDash, RNSVGCGFloatArray)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath)
|
RCT_EXPORT_VIEW_PROPERTY(clipPath, CGPath)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule)
|
RCT_EXPORT_VIEW_PROPERTY(clipRule, RNSVGCGFCRule)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function applyBoundingBoxToBrushData(brushData, props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function (colorOrBrush, props) {
|
export default function (colorOrBrush, props) {
|
||||||
if (colorOrBrush == null) {
|
if (!colorOrBrush) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (colorOrBrush._brush) {
|
if (colorOrBrush._brush) {
|
||||||
|
|||||||
@@ -1,63 +1,19 @@
|
|||||||
import rgba from '../rgba';
|
import rgba from '../rgba';
|
||||||
import {LinearGradientGenerator} from '../../elements/LinearGradient';
|
|
||||||
import {RadialGradientGenerator} from '../../elements/RadialGradient';
|
|
||||||
import extractBrush from './extractBrush';
|
import extractBrush from './extractBrush';
|
||||||
import fillReg from './patternReg';
|
import patterns from './patterns';
|
||||||
|
|
||||||
let fillPatterns = {};
|
|
||||||
|
|
||||||
function isGradient(obj) {
|
|
||||||
return obj instanceof LinearGradientGenerator || obj instanceof RadialGradientGenerator;
|
|
||||||
}
|
|
||||||
|
|
||||||
function set(id, pattern) {
|
|
||||||
fillPatterns[id] = pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove(id) {
|
|
||||||
delete fillPatterns[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
const fillRules = {
|
const fillRules = {
|
||||||
evenodd: 0,
|
evenodd: 0,
|
||||||
nonzero: 1
|
nonzero: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
function fillFilter(props) {
|
function fillFilter(props, dimensions) {
|
||||||
let {fill} = props;
|
let {fill} = props;
|
||||||
|
|
||||||
if (fill === 'none') {
|
if (fill === 'none') {
|
||||||
return null;
|
return null;
|
||||||
} if (fill) {
|
} else if (fill) {
|
||||||
|
return patterns(fill, +props.fillOpacity, dimensions, props.svgId);
|
||||||
if (isGradient(fill)) {
|
|
||||||
return fill;
|
|
||||||
}
|
|
||||||
|
|
||||||
let fillOpacity = +props.fillOpacity;
|
|
||||||
if (isNaN(fillOpacity)) {
|
|
||||||
fillOpacity = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试匹配 fill="url(#pattern)"
|
|
||||||
let matched = fill.match(fillReg);
|
|
||||||
|
|
||||||
if (matched) {
|
|
||||||
let patternName = `${matched[1]}:${props.svgId}`;
|
|
||||||
let pattern = fillPatterns[patternName];
|
|
||||||
|
|
||||||
if (pattern) {
|
|
||||||
if (pattern.length === 2) {
|
|
||||||
let dimensions = this.getBoundingBox();
|
|
||||||
return pattern(dimensions, fillOpacity);
|
|
||||||
} else {
|
|
||||||
return pattern(fillOpacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rgba(props.fill, fillOpacity).rgbaString();
|
|
||||||
} else if (props.fill === undefined) {
|
} else if (props.fill === undefined) {
|
||||||
let fillOpacity = +props.fillOpacity;
|
let fillOpacity = +props.fillOpacity;
|
||||||
if (isNaN(fillOpacity)) {
|
if (isNaN(fillOpacity)) {
|
||||||
@@ -67,20 +23,14 @@ function fillFilter(props) {
|
|||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function(props) {
|
export default function(props, dimensions) {
|
||||||
let fill = fillFilter.call(this, props);
|
let fill = extractBrush(fillFilter(props, dimensions), props);
|
||||||
let fillRule = fillRules[props.fillRule] === 0 ? 0 : 1;
|
let fillRule = fillRules[props.fillRule] === 0 ? 0 : 1;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fill: extractBrush(fill, props),
|
fill,
|
||||||
fillRule
|
fillRule
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
|
||||||
set,
|
|
||||||
remove
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,16 +14,18 @@ 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));
|
_.assign(extractedProps, extractStroke(props, dimensions));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.fill) {
|
if (options.fill) {
|
||||||
_.assign(extractedProps, extractFill.call(this, props));
|
_.assign(extractedProps, extractFill(props, dimensions));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.transform) {
|
if (options.transform) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import rgba from '../rgba';
|
|
||||||
import extractBrush from './extractBrush';
|
import extractBrush from './extractBrush';
|
||||||
|
import patterns from './patterns';
|
||||||
|
|
||||||
let separator = /\s*,\s*/;
|
let separator = /\s*,\s*/;
|
||||||
|
|
||||||
const caps = {
|
const caps = {
|
||||||
@@ -14,9 +15,10 @@ const joins = {
|
|||||||
round: 1
|
round: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
function strokeFilter(props) {
|
function strokeFilter(props, dimensions) {
|
||||||
if (!props.stroke && !props.strokeLinecap && !props.strokeOpacity &&
|
let strokeWidth = +props.strokeWidth;
|
||||||
!props.strokeLinejoin && !props.strokeDasharray && !props.strokeWidth) {
|
let {stroke} = props;
|
||||||
|
if (!strokeWidth && !stroke) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,30 +28,25 @@ function strokeFilter(props) {
|
|||||||
strokeDasharray = strokeDasharray.split(separator).map(dash => +dash);
|
strokeDasharray = strokeDasharray.split(separator).map(dash => +dash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!stroke) {
|
||||||
|
stroke = '#000';
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: propTypes check
|
||||||
return {
|
return {
|
||||||
stroke: rgba(props.stroke, props.strokeOpacity).rgbaArray(),
|
stroke: patterns(stroke, +props.strokeOpacity, dimensions, props.svgId),
|
||||||
strokeLinecap: caps[props.strokeLinecap] || 2,
|
strokeLinecap: caps[props.strokeLinecap] || 2,
|
||||||
strokeLinejoin: joins[props.strokeLinejoin] || 0,
|
strokeLinejoin: joins[props.strokeLinejoin] || 0,
|
||||||
strokeDash: strokeDasharray || null,
|
strokeDash: strokeDasharray || null,
|
||||||
strokeWidth: +props.strokeWidth || 1
|
strokeWidth: strokeWidth || 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function(props) {
|
export default function(props, dimensions) {
|
||||||
let strokeProps = strokeFilter(props);
|
let strokeProps = strokeFilter(props, dimensions);
|
||||||
if (!strokeProps) {
|
return strokeProps ? {
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
let {stroke} = strokeProps;
|
|
||||||
return {
|
|
||||||
...strokeProps,
|
...strokeProps,
|
||||||
stroke: stroke ? [stroke[0] / 255, stroke[1] / 255, stroke[2] / 255, stroke[3]] : null
|
stroke: extractBrush(strokeProps.stroke, props)
|
||||||
};
|
} : null;
|
||||||
|
|
||||||
// TODO: implement brush on stroke prop
|
|
||||||
//return {
|
|
||||||
// ...strokeProps,
|
|
||||||
// stroke: extractBrush(strokeProps.stroke, props)
|
|
||||||
//};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export default /^url\(#(\w+?)\)$/;
|
|
||||||
53
lib/extract/patterns.js
Normal file
53
lib/extract/patterns.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import rgba from '../rgba';
|
||||||
|
let patterns = {};
|
||||||
|
let patternReg = /^url\(#(\w+?)\)$/;
|
||||||
|
|
||||||
|
import {LinearGradientGenerator} from '../../elements/LinearGradient';
|
||||||
|
import {RadialGradientGenerator} from '../../elements/RadialGradient';
|
||||||
|
|
||||||
|
|
||||||
|
function isGradient(obj) {
|
||||||
|
return obj instanceof LinearGradientGenerator || obj instanceof RadialGradientGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set(id, pattern) {
|
||||||
|
patterns[id] = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(id) {
|
||||||
|
delete patterns[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
set,
|
||||||
|
remove
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function(patternSting, opacity, dimensions, svgId) {
|
||||||
|
if (isGradient(patternSting)) {
|
||||||
|
return patternSting;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNaN(opacity)) {
|
||||||
|
opacity = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试匹配 "url(#pattern)"
|
||||||
|
let matched = patternSting.match(patternReg);
|
||||||
|
|
||||||
|
if (matched) {
|
||||||
|
let patternName = `${matched[1]}:${svgId}`;
|
||||||
|
let pattern = patterns[patternName];
|
||||||
|
|
||||||
|
if (pattern) {
|
||||||
|
if (pattern.length === 2) {
|
||||||
|
return pattern(dimensions, opacity);
|
||||||
|
} else {
|
||||||
|
return pattern(opacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rgba(patternSting, opacity).rgbaString();
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"name": "react-native-svg",
|
"name": "react-native-svg",
|
||||||
"description": "react native svg library",
|
"description": "react native svg library",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
Reference in New Issue
Block a user