mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-06-01 14:15:03 +00:00
feat: implement support for context-fill and context-stroke color
This commit is contained in:
@@ -42,6 +42,7 @@ abstract public class RenderableView extends VirtualView {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
static RenderableView contextElement;
|
||||
// strokeLinecap
|
||||
private static final int CAP_BUTT = 0;
|
||||
static final int CAP_ROUND = 1;
|
||||
@@ -378,6 +379,7 @@ abstract public class RenderableView extends VirtualView {
|
||||
MarkerView markerMid = (MarkerView)getSvgView().getDefinedMarker(mMarkerMid);
|
||||
MarkerView markerEnd = (MarkerView)getSvgView().getDefinedMarker(mMarkerEnd);
|
||||
if (elements != null && (markerStart != null || markerMid != null || markerEnd != null)) {
|
||||
contextElement = this;
|
||||
ArrayList<RNSVGMarkerPosition> positions = RNSVGMarkerPosition.fromPath(elements);
|
||||
float width = (float)(this.strokeWidth != null ? relativeOnOther(this.strokeWidth) : 1);
|
||||
for (RNSVGMarkerPosition position : positions) {
|
||||
@@ -399,8 +401,10 @@ abstract public class RenderableView extends VirtualView {
|
||||
break;
|
||||
}
|
||||
}
|
||||
contextElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up paint according to the props set on a view. Returns {@code true}
|
||||
* if the fill should be drawn, {@code false} if not.
|
||||
@@ -477,6 +481,18 @@ abstract public class RenderableView extends VirtualView {
|
||||
paint.setColor(brush);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
if (contextElement != null && contextElement.fill != null) {
|
||||
setupPaint(paint, opacity, contextElement.fill);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (contextElement != null && contextElement.stroke != null) {
|
||||
setupPaint(paint, opacity, contextElement.stroke);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, react-native-community.
|
||||
* 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 "RNSVGBrush.h"
|
||||
|
||||
@interface RNSVGContextBrush : RNSVGBrush
|
||||
|
||||
- (instancetype)initFill;
|
||||
- (instancetype)initStroke;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, react-native-community.
|
||||
* 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 "RNSVGContextBrush.h"
|
||||
#import "RNSVGRenderable.h"
|
||||
#import "RNSVGNode.h"
|
||||
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
@implementation RNSVGContextBrush
|
||||
{
|
||||
BOOL _isStroke;
|
||||
}
|
||||
|
||||
- (instancetype)initFill
|
||||
{
|
||||
if ((self = [super initWithArray:nil])) {
|
||||
_isStroke = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initStroke
|
||||
{
|
||||
if ((self = [super initWithArray:nil])) {
|
||||
_isStroke = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
}
|
||||
|
||||
- (BOOL)applyFillColor:(CGContextRef)context opacity:(CGFloat)opacity
|
||||
{
|
||||
RNSVGRenderable *element = RNSVGRenderable.contextElement;
|
||||
if (!element) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
RNSVGBrush *brush = _isStroke ? element.stroke : element.fill;
|
||||
|
||||
BOOL fillColor;
|
||||
|
||||
if (brush.class == RNSVGBrush.class) {
|
||||
CGContextSetFillColorWithColor(context, [element.tintColor CGColor]);
|
||||
fillColor = YES;
|
||||
} else {
|
||||
fillColor = [brush applyFillColor:context opacity:opacity];
|
||||
}
|
||||
|
||||
return fillColor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (BOOL)applyStrokeColor:(CGContextRef)context opacity:(CGFloat)opacity
|
||||
{
|
||||
RNSVGRenderable *element = RNSVGRenderable.contextElement;
|
||||
if (!element) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
RNSVGBrush *brush = _isStroke ? element.stroke : element.fill;
|
||||
|
||||
BOOL strokeColor;
|
||||
|
||||
if (brush.class == RNSVGBrush.class) {
|
||||
CGContextSetStrokeColorWithColor(context, [element.tintColor CGColor]);
|
||||
strokeColor = YES;
|
||||
} else {
|
||||
strokeColor = [brush applyStrokeColor:context opacity:opacity];
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -65,6 +65,8 @@
|
||||
947F380C214810DC00677F2A /* RNSVGMask.m in Sources */ = {isa = PBXBuildFile; fileRef = 947F380A214810DC00677F2A /* RNSVGMask.m */; };
|
||||
947F380F2148119A00677F2A /* RNSVGMaskManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 947F380E2148119A00677F2A /* RNSVGMaskManager.m */; };
|
||||
947F38102148119A00677F2A /* RNSVGMaskManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 947F380E2148119A00677F2A /* RNSVGMaskManager.m */; };
|
||||
9482DEFA23460EC800FC486E /* RNSVGContextBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 9482DEF823460EC700FC486E /* RNSVGContextBrush.m */; };
|
||||
9482DEFB23460EC800FC486E /* RNSVGContextBrush.m in Sources */ = {isa = PBXBuildFile; fileRef = 9482DEF823460EC700FC486E /* RNSVGContextBrush.m */; };
|
||||
9494C4D81F473BA700D5BCFD /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9494C4D71F473BA700D5BCFD /* QuartzCore.framework */; };
|
||||
9494C4DA1F473BCB00D5BCFD /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9494C4D91F473BCB00D5BCFD /* CoreText.framework */; };
|
||||
9494C4DC1F473BD900D5BCFD /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9494C4DB1F473BD900D5BCFD /* CoreGraphics.framework */; };
|
||||
@@ -257,6 +259,8 @@
|
||||
947F380A214810DC00677F2A /* RNSVGMask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGMask.m; path = Elements/RNSVGMask.m; sourceTree = "<group>"; };
|
||||
947F380D2148118300677F2A /* RNSVGMaskManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGMaskManager.h; sourceTree = "<group>"; };
|
||||
947F380E2148119A00677F2A /* RNSVGMaskManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGMaskManager.m; sourceTree = "<group>"; };
|
||||
9482DEF823460EC700FC486E /* RNSVGContextBrush.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGContextBrush.m; sourceTree = "<group>"; };
|
||||
9482DEF923460EC800FC486E /* RNSVGContextBrush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGContextBrush.h; sourceTree = "<group>"; };
|
||||
9494C4D71F473BA700D5BCFD /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
||||
9494C4D91F473BCB00D5BCFD /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
|
||||
9494C4DB1F473BD900D5BCFD /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
|
||||
@@ -340,6 +344,8 @@
|
||||
0CF68AEA1AF0549300FF9E5C /* Brushes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9482DEF923460EC800FC486E /* RNSVGContextBrush.h */,
|
||||
9482DEF823460EC700FC486E /* RNSVGContextBrush.m */,
|
||||
10FDEEB31D3FBED400A5C46C /* RNSVGBrushType.h */,
|
||||
10FDEEB01D3FB60500A5C46C /* RNSVGPainterBrush.h */,
|
||||
10FDEEB11D3FB60500A5C46C /* RNSVGPainterBrush.m */,
|
||||
@@ -599,6 +605,7 @@
|
||||
10BEC1C21D3F680F00FDCB19 /* RNSVGLinearGradientManager.m in Sources */,
|
||||
1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */,
|
||||
10BA0D3B1CE74E3100887C2B /* RNSVGRectManager.m in Sources */,
|
||||
9482DEFA23460EC800FC486E /* RNSVGContextBrush.m in Sources */,
|
||||
0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */,
|
||||
1039D2891CE71EB7001E90A8 /* RNSVGGroup.m in Sources */,
|
||||
10ED4A9E1CF0656A0078BC02 /* RNSVGClipPathManager.m in Sources */,
|
||||
@@ -664,6 +671,7 @@
|
||||
A361E77A1EB0C33D00646005 /* RNSVGText.m in Sources */,
|
||||
A361E77B1EB0C33D00646005 /* RNSVGRectManager.m in Sources */,
|
||||
A361E77C1EB0C33D00646005 /* RNSVGRenderable.m in Sources */,
|
||||
9482DEFB23460EC800FC486E /* RNSVGContextBrush.m in Sources */,
|
||||
A361E77D1EB0C33D00646005 /* RNSVGGroup.m in Sources */,
|
||||
A361E77E1EB0C33D00646005 /* RNSVGClipPathManager.m in Sources */,
|
||||
A361E77F1EB0C33D00646005 /* RNSVGPainter.m in Sources */,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
@interface RNSVGRenderable : RNSVGNode
|
||||
|
||||
@property (class) RNSVGRenderable *contextElement;
|
||||
@property (nonatomic, strong) RNSVGBrush *fill;
|
||||
@property (nonatomic, assign) CGFloat fillOpacity;
|
||||
@property (nonatomic, assign) RNSVGCGFCRule fillRule;
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
CGPathRef _hitArea;
|
||||
}
|
||||
|
||||
static RNSVGRenderable * _contextElement;
|
||||
+ (RNSVGRenderable *)contextElement { return _contextElement; }
|
||||
+ (void)setContextElement:(RNSVGRenderable *)contextElement { _contextElement = contextElement; }
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
@@ -304,6 +308,7 @@ UInt32 saturate(CGFloat value) {
|
||||
RNSVGMarker *markerMid = (RNSVGMarker*)[self.svgView getDefinedMarker:self.markerMid];
|
||||
RNSVGMarker *markerEnd = (RNSVGMarker*)[self.svgView getDefinedMarker:self.markerEnd];
|
||||
if (markerStart || markerMid || markerEnd) {
|
||||
_contextElement = self;
|
||||
NSArray<RNSVGMarkerPosition*>* positions = [RNSVGMarkerPosition fromCGPath:path];
|
||||
CGFloat width = self.strokeWidth ? [self relativeOnOther:self.strokeWidth] : 1;
|
||||
for (RNSVGMarkerPosition* position in positions) {
|
||||
@@ -325,6 +330,7 @@ UInt32 saturate(CGFloat value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_contextElement = nil;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#import "RNSVGPainterBrush.h"
|
||||
#import "RNSVGSolidColorBrush.h"
|
||||
#import "RNSVGContextBrush.h"
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTFont.h>
|
||||
|
||||
@@ -65,6 +66,10 @@ RCT_ENUM_CONVERTER(RNSVGUnits, (@{
|
||||
return [[RNSVGPainterBrush alloc] initWithArray:arr];
|
||||
case 2: // currentColor
|
||||
return [[RNSVGBrush alloc] initWithArray:nil];
|
||||
case 3: // context-fill
|
||||
return [[RNSVGContextBrush alloc] initFill];
|
||||
case 4: // context-stroke
|
||||
return [[RNSVGContextBrush alloc] initStroke];
|
||||
default:
|
||||
RCTLogError(@"Unknown brush type: %zd", (unsigned long)type);
|
||||
return nil;
|
||||
|
||||
@@ -4,6 +4,8 @@ import { Color } from './types';
|
||||
const urlIdPattern = /^url\(#(.+)\)$/;
|
||||
|
||||
const currentColorBrush = [2];
|
||||
const contextFillBrush = [3];
|
||||
const contextStrokeBrush = [4];
|
||||
|
||||
export default function extractBrush(color?: Color) {
|
||||
if (typeof color === 'number') {
|
||||
@@ -20,6 +22,14 @@ export default function extractBrush(color?: Color) {
|
||||
return currentColorBrush;
|
||||
}
|
||||
|
||||
if (color === 'context-fill') {
|
||||
return contextFillBrush;
|
||||
}
|
||||
|
||||
if (color === 'context-stroke') {
|
||||
return contextStrokeBrush;
|
||||
}
|
||||
|
||||
const brush = typeof color === 'string' && color.match(urlIdPattern);
|
||||
if (brush) {
|
||||
return [1, brush[1]];
|
||||
|
||||
Reference in New Issue
Block a user