refactor viewBox for svg

This commit is contained in:
Horcrux
2016-07-23 23:44:26 +08:00
parent 7e13b801e1
commit 10b28434bb
26 changed files with 464 additions and 217 deletions

View File

@@ -89,7 +89,6 @@
"no-caller": 1, // disallow use of arguments.caller or arguments.callee
"no-div-regex": 1, // disallow division operators explicitly at beginning of regular expression (off by default)
"no-else-return": 0, // disallow else after a return in an if (off by default)
"no-empty-label": 1, // disallow use of labels for anything other then loops and switches
"no-eq-null": 0, // disallow comparisons to null without a type-checking operator (off by default)
"no-eval": 1, // disallow use of eval()
"no-extend-native": 1, // disallow adding to native types
@@ -184,7 +183,7 @@
"quote-props": 0, // require quotes around object literal property names (off by default)
"semi": 1, // require or disallow use of semicolons instead of ASI
"sort-vars": 0, // sort variables within the same declaration block (off by default)
"space-after-keywords": 1, // require a space after certain keywords (off by default)
"keyword-spacing": 1, // require a space after certain keywords (off by default)
"space-in-brackets": 0, // require or disallow spaces inside brackets (off by default)
"space-in-parens": 0, // require or disallow spaces inside parentheses (off by default)
"space-infix-ops": 1, // require spaces around operators

View File

@@ -78,7 +78,6 @@ const icon = <Svg
</Svg>;
const samples = [CircleExample, StrokeCircle, StrokeOpacityCircle];
export {
icon,
samples

View File

@@ -23,7 +23,8 @@ import Svg, {
Circle,
Rect,
Path,
Line
Line,
G
} from 'react-native-svg';
class SvgExample extends Component{
@@ -84,19 +85,21 @@ class SvgOpacity extends Component{
}
class SvgViewbox extends Component{
static title = 'SVG with `viewbox="40 20 100 40"`';
static title = 'SVG with `viewBox="40 20 100 40" and preserveAspectRatio="none"`';
render() {
return <Svg
height="100"
width="100"
viewbox="40 20 100 40"
viewBox="40 20 100 40"
preserveAspectRatio="none"
>
<Rect x="0" y="0" width="100" height="100" fill="black" />
<G>
<Rect x="0" y="0" width="100" height="100" fill="red" />
<Circle cx="50" cy="50" r="30" fill="yellow" />
<Circle cx="40" cy="40" r="4" fill="black" />
<Circle cx="60" cy="40" r="4" fill="black" />
<Path d="M 40 60 A 10 10 0 0 0 60 60" stroke="black" />
</G>
</Svg>;
}
}

View File

@@ -18,11 +18,9 @@ class Svg extends Component{
opacity: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
viewbox: PropTypes.string,
// TODO: complete other values of preserveAspectRatio
// http://www.justinmccandless.com/demos/viewbox/index.html
// http://tutorials.jenkov.com/svg/svg-viewport-view-box.html
preserveAspectRatio: PropTypes.string // preserveAspectRatio only supports 'none' for now
// more detail https://svgwg.org/svg2-draft/coords.html#ViewBoxAttribute
viewBox: PropTypes.string,
preserveAspectRatio: PropTypes.string
};
constructor() {
@@ -51,13 +49,25 @@ class Svg extends Component{
let opacity = +props.opacity;
let width = +props.width;
let height = +props.height;
let flexLayout = isNaN(width) || isNaN(height);
let viewBox = props.viewBox;
let dimensions;
let content = (props.viewbox && !flexLayout) ? <ViewBox
viewbox={props.viewbox}
if (width && height) {
dimensions = {
width,
height,
flex: 0
}
}
if (props.viewbox) {
viewBox = props.viewbox;
console.warn('Prop `viewbox` is deprecated. please use `viewBox` instead.');
}
let content = viewBox ? <ViewBox
viewBox={viewBox}
preserveAspectRatio={props.preserveAspectRatio}
width={props.width}
height={props.height}
>{props.children}</ViewBox> : props.children;
return (
@@ -66,7 +76,7 @@ class Svg extends Component{
opacity={null}
width={null}
height={null}
viewbox={null}
viewBox={null}
preserveAspectRatio={null}
ref={ele => this.root = ele}
style={[
@@ -75,11 +85,7 @@ class Svg extends Component{
!isNaN(opacity) && {
opacity
},
!flexLayout && {
width,
height,
flex: 0
}
dimensions
]}
>
{content}

View File

@@ -1,38 +1,73 @@
import React, {Component} from 'react';
import React, {Component, PropTypes} from 'react';
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
import {ViewBoxAttributes} from '../lib/attributes';
import G from './G';
import extractViewbox from '../lib/extract/extractViewbox';
import _ from 'lodash';
const meetOrSliceTypes = {
meet: 0,
slice: 1,
none: 2
};
const alignEnum = _.reduce([
'xMinMin', 'xMidYMin', 'xMaxYMin',
'xMinYMid', 'xMidYMid', 'xMaxYMid',
'xMinYMax', 'xMidYMax', 'xMaxYMax',
'none'
], (prev, name) => {
prev[name] = name;
return prev;
}, {});
const numberRegExp = /^\d*\.?\d*%?$/;
const spacesRegExp = /\s+/;
class ViewBox extends Component{
static displayName = 'ViewBox';
static propTypes = {
viewBox: PropTypes.string.isRequired,
preserveAspectRatio: PropTypes.string
};
static defaultProps = {
preserveAspectRatio: 'xMidYMid meet'
};
render() {
let viewbox = extractViewbox(this.props);
let scaleX = 1;
let scaleY = 1;
let x = 0;
let y = 0;
if (viewbox) {
scaleX = viewbox.scaleX;
scaleY = viewbox.scaleY;
x = viewbox.x;
y = viewbox.y;
let {viewBox, preserveAspectRatio} = this.props;
let params = viewBox.trim().split(spacesRegExp);
if (params.length !== 4 || !_.some(params, param => param && numberRegExp.test(param))) {
console.warn('`viewBox` expected a string like `minX minY width height`, but got:' + viewBox);
return <G>
{this.props.children}
</G>
}
let modes = preserveAspectRatio.trim().split(spacesRegExp);
console.log(viewbox);
return <G
{...this.props}
x={x}
y={y}
scaleX={scaleX}
scaleY={scaleY}
preserveAspectRatio={null}
viewbox={null}
id={null}
let meetOrSlice = meetOrSliceTypes[modes[1]] || 0;
let align = alignEnum[modes[0]] || 'xMidYMid';
return <RNSVGViewBox
minX={params[0]}
minY={params[1]}
vbWidth={params[2]}
vbHeight={params[3]}
align={align}
meetOrSlice={meetOrSlice}
>
{(!scaleX || !scaleY) ? null : this.props.children}
</G>;
{this.props.children}
</RNSVGViewBox>
}
}
const RNSVGViewBox = createReactNativeComponentClass({
validAttributes: ViewBoxAttributes,
uiViewClassName: 'RNSVGViewBox'
});
export default ViewBox;

View File

@@ -7,7 +7,6 @@
*/
#import "RNSVGGroup.h"
#import <objc/runtime.h>
@implementation RNSVGGroup

View File

@@ -69,33 +69,37 @@
- (void)renderLayerTo:(CGContextRef)context
{
CGRect box = CGContextGetClipBoundingBox(context);
float height = CGRectGetHeight(box);
float width = CGRectGetWidth(box);
RNSVGPercentageConverter* convert = [[RNSVGPercentageConverter alloc] init];
CGFloat x = [convert stringToFloat:self.x relative:width offset:0];
CGFloat y = [convert stringToFloat:self.y relative:height offset:0];
CGFloat w = [convert stringToFloat:self.width relative:width offset:0];
CGFloat h = [convert stringToFloat:self.height relative:height offset:0];
CGRect rect = [self getRect:context];
// add hit area
self.hitArea = CGPathCreateMutable();
CGPathRef rect = CGPathCreateWithRect(CGRectMake(x, y, w, h), nil);
CGPathAddPath(self.hitArea, nil, rect);
CGPathRelease(rect);
if (self.opacity == 0) {
return;
}
CGPathRef path = CGPathCreateWithRect(rect, nil);
CGPathAddPath(self.hitArea, nil, path);
CGPathRelease(path);
[self clip:context];
CGContextSaveGState(context);
CGContextTranslateCTM(context, 0, h);
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(x, -y, w, h), image);
CGContextDrawImage(context, rect, image);
CGContextRestoreGState(context);
}
- (CGRect)getRect:(CGContextRef)context
{
[self setBoundingBox:context];
CGFloat x = [self getWidthRelatedValue:self.x];
CGFloat y = [self getHeightRelatedValue:self.y];
CGFloat width = [self getWidthRelatedValue:self.width];
CGFloat height = [self getHeightRelatedValue:self.height];
return CGRectMake(x, y, width, height);
}
- (CGPathRef)getPath:(CGContextRef)context
{
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, nil, [self getRect:context]);
return (CGPathRef)CFAutorelease(path);
}
@end

View File

@@ -23,6 +23,8 @@
1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2901CE71EC2001E90A8 /* RNSVGText.m */; };
1039D2A01CE72177001E90A8 /* RCTConvert+RNSVG.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D29C1CE72177001E90A8 /* RCTConvert+RNSVG.m */; };
1039D2B01CE72F27001E90A8 /* RNSVGPercentageConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2AF1CE72F27001E90A8 /* RNSVGPercentageConverter.m */; };
10ABC7331D435915006CCF6E /* RNSVGViewBoxManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ABC7321D435915006CCF6E /* RNSVGViewBoxManager.m */; };
10ABC7361D43595E006CCF6E /* RNSVGViewBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 10ABC7351D43595E006CCF6E /* RNSVGViewBox.m */; };
10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D1D1CE74E3100887C2B /* RNSVGCircleManager.m */; };
10BA0D351CE74E3100887C2B /* RNSVGEllipseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D1F1CE74E3100887C2B /* RNSVGEllipseManager.m */; };
10BA0D361CE74E3100887C2B /* RNSVGGroupManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D211CE74E3100887C2B /* RNSVGGroupManager.m */; };
@@ -94,11 +96,16 @@
1039D2911CE71EC2001E90A8 /* RNSVGTextFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextFrame.h; path = Text/RNSVGTextFrame.h; sourceTree = "<group>"; };
1039D29B1CE72177001E90A8 /* RCTConvert+RNSVG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "RCTConvert+RNSVG.h"; path = "Utils/RCTConvert+RNSVG.h"; sourceTree = "<group>"; };
1039D29C1CE72177001E90A8 /* RCTConvert+RNSVG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "RCTConvert+RNSVG.m"; path = "Utils/RCTConvert+RNSVG.m"; sourceTree = "<group>"; };
1039D29D1CE72177001E90A8 /* RNSVGCGFCRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGCGFCRule.h; path = Utils/RNSVGCGFCRule.h; sourceTree = "<group>"; };
1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGCGFloatArray.h; path = Utils/RNSVGCGFloatArray.h; sourceTree = "<group>"; };
1039D2A11CE721A7001E90A8 /* RNSVGContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGContainer.h; sourceTree = "<group>"; };
1039D2AE1CE72F27001E90A8 /* RNSVGPercentageConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGPercentageConverter.h; path = Utils/RNSVGPercentageConverter.h; sourceTree = "<group>"; };
1039D2AF1CE72F27001E90A8 /* RNSVGPercentageConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGPercentageConverter.m; path = Utils/RNSVGPercentageConverter.m; sourceTree = "<group>"; };
10ABC7311D435915006CCF6E /* RNSVGViewBoxManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGViewBoxManager.h; sourceTree = "<group>"; };
10ABC7321D435915006CCF6E /* RNSVGViewBoxManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGViewBoxManager.m; sourceTree = "<group>"; };
10ABC7341D43595E006CCF6E /* RNSVGViewBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGViewBox.h; sourceTree = "<group>"; };
10ABC7351D43595E006CCF6E /* RNSVGViewBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGViewBox.m; sourceTree = "<group>"; };
10ABC7371D439779006CCF6E /* RNSVGCGFCRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGCGFCRule.h; path = Utils/RNSVGCGFCRule.h; sourceTree = "<group>"; };
10ABC7381D43982B006CCF6E /* RNSVGVBMOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGVBMOS.h; path = Utils/RNSVGVBMOS.h; sourceTree = "<group>"; };
10BA0D1C1CE74E3100887C2B /* RNSVGCircleManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGCircleManager.h; sourceTree = "<group>"; };
10BA0D1D1CE74E3100887C2B /* RNSVGCircleManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGCircleManager.m; sourceTree = "<group>"; };
10BA0D1E1CE74E3100887C2B /* RNSVGEllipseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGEllipseManager.h; sourceTree = "<group>"; };
@@ -175,6 +182,8 @@
10ED4AA11CF078830078BC02 /* RNSVGNode.m */,
0CF68AE11AF0549300FF9E5C /* RNSVGRenderable.h */,
0CF68AE21AF0549300FF9E5C /* RNSVGRenderable.m */,
10ABC7341D43595E006CCF6E /* RNSVGViewBox.h */,
10ABC7351D43595E006CCF6E /* RNSVGViewBox.m */,
0CF68AC21AF0540F00FF9E5C /* Products */,
);
sourceTree = "<group>";
@@ -208,6 +217,8 @@
0CF68AF81AF0549300FF9E5C /* ViewManagers */ = {
isa = PBXGroup;
children = (
10ABC7311D435915006CCF6E /* RNSVGViewBoxManager.h */,
10ABC7321D435915006CCF6E /* RNSVGViewBoxManager.m */,
10BEC1BE1D3F680F00FDCB19 /* RNSVGLinearGradientManager.h */,
10BEC1BF1D3F680F00FDCB19 /* RNSVGLinearGradientManager.m */,
10BEC1C01D3F680F00FDCB19 /* RNSVGRadialGradientManager.h */,
@@ -299,11 +310,12 @@
1039D29A1CE7212C001E90A8 /* Utils */ = {
isa = PBXGroup;
children = (
10ABC7381D43982B006CCF6E /* RNSVGVBMOS.h */,
10ABC7371D439779006CCF6E /* RNSVGCGFCRule.h */,
1039D2AE1CE72F27001E90A8 /* RNSVGPercentageConverter.h */,
1039D2AF1CE72F27001E90A8 /* RNSVGPercentageConverter.m */,
1039D29B1CE72177001E90A8 /* RCTConvert+RNSVG.h */,
1039D29C1CE72177001E90A8 /* RCTConvert+RNSVG.m */,
1039D29D1CE72177001E90A8 /* RNSVGCGFCRule.h */,
1039D29E1CE72177001E90A8 /* RNSVGCGFloatArray.h */,
);
name = Utils;
@@ -365,12 +377,14 @@
buildActionMask = 2147483647;
files = (
10BA0D3F1CE74E3100887C2B /* RNSVGTextManager.m in Sources */,
10ABC7361D43595E006CCF6E /* RNSVGViewBox.m in Sources */,
1039D28A1CE71EB7001E90A8 /* RNSVGImage.m in Sources */,
10BA0D4B1CE74E3D00887C2B /* RNSVGRect.m in Sources */,
10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */,
10BEC1BC1D3F66F500FDCB19 /* RNSVGLinearGradient.m in Sources */,
1039D2B01CE72F27001E90A8 /* RNSVGPercentageConverter.m in Sources */,
10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */,
10ABC7331D435915006CCF6E /* RNSVGViewBoxManager.m in Sources */,
1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */,
0CF68B0D1AF0549300FF9E5C /* RNSVGPattern.m in Sources */,
1023B4931D3DF5060051496D /* RNSVGUse.m in Sources */,

View File

@@ -29,6 +29,19 @@
@property (nonatomic, assign) CGMutablePathRef hitArea;
@property (nonatomic, copy) NSArray<NSString *> *propList;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (void)setBoundingBox:(CGContextRef)context;
- (CGFloat)getWidthRelatedValue:(NSString *)string;
- (CGFloat)getHeightRelatedValue:(NSString *)string;
- (CGFloat)getContextWidth;
- (CGFloat)getContextHeight;
- (CGFloat)getContextX;
- (CGFloat)getContextY;
@end

View File

@@ -7,11 +7,17 @@
*/
#import "RNSVGRenderable.h"
#import "RNSVGPercentageConverter.h"
@implementation RNSVGRenderable
{
NSMutableDictionary *originProperties;
NSArray *changedList;
NSMutableDictionary *_originProperties;
NSArray *_changedList;
RNSVGPercentageConverter *_widthConverter;
RNSVGPercentageConverter *_heightConverter;
CGFloat _contextWidth;
CGFloat _contextHeight;
CGRect _boundingBox;
}
- (id)init
@@ -137,6 +143,44 @@
}
}
- (void)setBoundingBox:(CGContextRef)context
{
_boundingBox = CGContextGetClipBoundingBox(context);
_widthConverter = [[RNSVGPercentageConverter alloc] initWithRelativeAndOffset:CGRectGetWidth(_boundingBox) offset:0];
_heightConverter = [[RNSVGPercentageConverter alloc] initWithRelativeAndOffset:CGRectGetHeight(_boundingBox) offset:0];
}
- (CGFloat)getWidthRelatedValue:(NSString *)string
{
return [_widthConverter stringToFloat:string];
}
- (CGFloat)getHeightRelatedValue:(NSString *)string
{
return [_heightConverter stringToFloat:string];
}
- (CGFloat)getContextWidth
{
return CGRectGetWidth(_boundingBox);
}
- (CGFloat)getContextHeight
{
return CGRectGetHeight(_boundingBox);
}
- (CGFloat)getContextX
{
return CGRectGetMinX(_boundingBox);
}
- (CGFloat)getContextY
{
return CGRectGetMinY(_boundingBox);
}
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList
{
@@ -150,15 +194,15 @@
}
if (!inherited) {
originProperties = [[NSMutableDictionary alloc] init];
changedList = mergeList;
_originProperties = [[NSMutableDictionary alloc] init];
_changedList = mergeList;
}
for (NSString *key in mergeList) {
if (inherited) {
[self inheritProperty:target propName:key];
} else {
[originProperties setValue:[self valueForKey:key] forKey:key];
[_originProperties setValue:[self valueForKey:key] forKey:key];
[self setValue:[target valueForKey:key] forKey:key];
}
}
@@ -166,13 +210,13 @@
- (void)resetProperties
{
if (changedList) {
for (NSString *key in changedList) {
[self setValue:[originProperties valueForKey:key] forKey:key];
if (_changedList) {
for (NSString *key in _changedList) {
[self setValue:[_originProperties valueForKey:key] forKey:key];
}
}
[super resetProperties];
changedList = nil;
_changedList = nil;
}
- (void)inheritProperty:(__kindof RNSVGNode *)parent propName:(NSString *)propName

21
ios/RNSVGViewBox.h Normal file
View File

@@ -0,0 +1,21 @@
/**
* 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 "RNSVGGroup.h"
#import "RNSVGVBMOS.h"
@interface RNSVGViewBox : RNSVGGroup
@property (nonatomic, strong) NSString *minX;
@property (nonatomic, strong) NSString *minY;
@property (nonatomic, strong) NSString *vbWidth;
@property (nonatomic, strong) NSString *vbHeight;
@property (nonatomic, strong) NSString *align;
@property (nonatomic, assign) RNSVGVBMOS meetOrSlice;
@end

102
ios/RNSVGViewBox.m Normal file
View File

@@ -0,0 +1,102 @@
/**
* 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 <math.h>
#import "RNSVGViewBox.h"
@implementation RNSVGViewBox
- (void)renderTo:(CGContextRef)context
{
// based on https://svgwg.org/svg2-draft/coords.html#ComputingAViewportsTransform
[self setBoundingBox:context];
// Let vb-x, vb-y, vb-width, vb-height be the min-x, min-y, width and height values of the viewBox attribute respectively.
CGFloat vbX = [self getWidthRelatedValue:self.minX];
CGFloat vbY = [self getHeightRelatedValue:self.minY];
CGFloat vbWidth = [self getWidthRelatedValue:self.vbWidth];
CGFloat vbHeight = [self getHeightRelatedValue:self.vbHeight];
// Let e-x, e-y, e-width, e-height be the position and size of the element respectively.
CGFloat eX = [self getContextX];
CGFloat eY = [self getContextY];
CGFloat eWidth = [self getContextWidth];
CGFloat eHeight = [self getContextHeight];
// Let align be the align value of preserveAspectRatio, or 'xMidyMid' if preserveAspectRatio is not defined.
NSString *align = self.align;
// Let meetOrSlice be the meetOrSlice value of preserveAspectRatio, or 'meet' if preserveAspectRatio is not defined or if meetOrSlice is missing from this value.
RNSVGVBMOS meetOrSlice = self.meetOrSlice;
// Initialize scale-x to e-width/vb-width.
CGFloat scaleX = eWidth / vbWidth;
// Initialize scale-y to e-height/vb-height.
CGFloat scaleY = eHeight / vbHeight;
// Initialize translate-x to vb-x - e-x.
// Initialize translate-y to vb-y - e-y.
CGFloat translateX = vbX - eX;
CGFloat translateY = vbY - eY;
// If align is 'none'
if (meetOrSlice == kRNSVGVBMOSNone) {
// Let scale be set the smaller value of scale-x and scale-y.
// Assign scale-x and scale-y to scale.
CGFloat scale = scaleX = scaleY = fmin(scaleX, scaleY);
// If scale is greater than 1
if (scale > 1) {
// Minus translateX by (eWidth / scale - vbWidth) / 2
// Minus translateY by (eHeight / scale - vbHeight) / 2
translateX -= (eWidth / scale - vbWidth) / 2;
translateY -= (eHeight / scale - vbHeight) / 2;
} else {
translateX -= (eWidth - vbWidth * scale) / 2;
translateY -= (eHeight - vbHeight * scale) / 2;
}
} else {
// If align is not 'none' and meetOrSlice is 'meet', set the larger of scale-x and scale-y to the smaller.
// Otherwise, if align is not 'none' and meetOrSlice is 'slice', set the smaller of scale-x and scale-y to the larger.
if (![align isEqualToString: @"none"] && meetOrSlice == kRNSVGVBMOSMeet) {
scaleX = scaleY = fmin(scaleX, scaleY);
} else if (![align isEqualToString: @"none"] && meetOrSlice == kRNSVGVBMOSSlice) {
scaleX = scaleY = fmax(scaleX, scaleY);
}
// If align contains 'xMid', minus (e-width / scale-x - vb-width) / 2 from transform-x.
if ([align containsString:@"xMid"]) {
translateX -= (eWidth / scaleX - vbWidth) / 2;
}
// If align contains 'xMax', minus (e-width / scale-x - vb-width) from transform-x.
if ([align containsString:@"xMax"]) {
translateX -= eWidth / scaleX - vbWidth;
}
// If align contains 'yMid', minus (e-height / scale-y - vb-height) / 2 from transform-y.
if ([align containsString:@"YMid"]) {
translateY -= (eHeight / scaleY - vbHeight) / 2;
}
// If align contains 'yMax', minus (e-height / scale-y - vb-height) from transform-y.
if ([align containsString:@"YMax"]) {
translateY -= eHeight / scaleY - vbHeight;
}
}
CGAffineTransform transform = CGAffineTransformMakeScale(scaleX, scaleY);
transform = CGAffineTransformTranslate(transform, -translateX, -translateY);
CGContextConcatCTM(context, transform);
[super renderTo:context];
}
@end

View File

@@ -46,22 +46,18 @@
- (CGPathRef)getPath:(CGContextRef)context
{
[self setBoundingBox:context];
CGMutablePathRef path = CGPathCreateMutable();
CGRect box = CGContextGetClipBoundingBox(context);
float height = CGRectGetHeight(box);
float width = CGRectGetWidth(box);
RNSVGPercentageConverter* convert = [[RNSVGPercentageConverter alloc] init];
CGFloat cx = [convert stringToFloat:self.cx relative:width offset:0];
CGFloat cy = [convert stringToFloat:self.cy relative:height offset:0];
CGFloat cx = [self getWidthRelatedValue:self.cx];
CGFloat cy = [self getHeightRelatedValue:self.cy];
CGFloat r;
// radius percentage calculate formula:
// radius = sqrt(pow((width*percent), 2) + pow((height*percent), 2)) / sqrt(2)
if ([convert isPercentage:self.r]) {
CGFloat radiusPercent = [convert percentageToFloat:self.r relative:1 offset:0];
r = sqrt(pow((width * radiusPercent), 2) + pow((height * radiusPercent), 2)) / sqrt(2);
r = sqrt(pow(([self getContextWidth] * radiusPercent), 2) + pow(([self getContextHeight] * radiusPercent), 2)) / sqrt(2);
} else {
r = [self.r floatValue];
}

View File

@@ -55,17 +55,12 @@
- (CGPathRef)getPath:(CGContextRef)context
{
[self setBoundingBox:context];
CGMutablePathRef path = CGPathCreateMutable();
CGRect box = CGContextGetClipBoundingBox(context);
float height = CGRectGetHeight(box);
float width = CGRectGetWidth(box);
RNSVGPercentageConverter* convert = [[RNSVGPercentageConverter alloc] init];
CGFloat cx = [convert stringToFloat:self.cx relative:width offset:0];
CGFloat cy = [convert stringToFloat:self.cy relative:height offset:0];
CGFloat rx = [convert stringToFloat:self.rx relative:width offset:0];
CGFloat ry = [convert stringToFloat:self.ry relative:height offset:0];
CGFloat cx = [self getWidthRelatedValue:self.cx];
CGFloat cy = [self getHeightRelatedValue:self.cy];
CGFloat rx = [self getWidthRelatedValue:self.rx];
CGFloat ry = [self getHeightRelatedValue:self.ry];
CGPathAddEllipseInRect(path, nil, CGRectMake(cx - rx, cy - ry, rx * 2, ry * 2));
return (CGPathRef)CFAutorelease(path);
}

View File

@@ -55,17 +55,12 @@
- (CGPathRef)getPath:(CGContextRef)context
{
[self setBoundingBox:context];
CGMutablePathRef path = CGPathCreateMutable();
CGRect box = CGContextGetClipBoundingBox(context);
float height = CGRectGetHeight(box);
float width = CGRectGetWidth(box);
RNSVGPercentageConverter* convert = [[RNSVGPercentageConverter alloc] init];
CGFloat x1 = [convert stringToFloat:self.x1 relative:width offset:0];
CGFloat y1 = [convert stringToFloat:self.y1 relative:height offset:0];
CGFloat x2 = [convert stringToFloat:self.x2 relative:width offset:0];
CGFloat y2 = [convert stringToFloat:self.y2 relative:height offset:0];
CGFloat x1 = [self getWidthRelatedValue:self.x1];
CGFloat y1 = [self getHeightRelatedValue:self.y1];
CGFloat x2 = [self getWidthRelatedValue:self.x2];
CGFloat y2 = [self getHeightRelatedValue:self.y2];
CGPathMoveToPoint(path, nil, x1, y1);
CGPathAddLineToPoint(path, nil, x2, y2);

View File

@@ -73,21 +73,14 @@
- (CGPathRef)getPath:(CGContextRef)context
{
[self setBoundingBox:context];
CGMutablePathRef path = CGPathCreateMutable();
CGRect box = CGContextGetClipBoundingBox(context);
float height = CGRectGetHeight(box);
float width = CGRectGetWidth(box);
RNSVGPercentageConverter* convert = [[RNSVGPercentageConverter alloc] init];
CGFloat x = [convert stringToFloat:self.x relative:width offset:0];
CGFloat y = [convert stringToFloat:self.y relative:height offset:0];
CGFloat w = [convert stringToFloat:self.width relative:width offset:0];
CGFloat h = [convert stringToFloat:self.height relative:height offset:0];
CGFloat rx = [convert stringToFloat:self.rx relative:width offset:0];
CGFloat ry = [convert stringToFloat:self.ry relative:height offset:0];
CGFloat x = [self getWidthRelatedValue:self.x];
CGFloat y = [self getHeightRelatedValue:self.y];
CGFloat width = [self getWidthRelatedValue:self.width];
CGFloat height = [self getHeightRelatedValue:self.height];
CGFloat rx = [self getWidthRelatedValue:self.rx];
CGFloat ry = [self getHeightRelatedValue:self.ry];
if (rx != 0 || ry != 0) {
if (rx == 0) {
@@ -96,17 +89,17 @@
ry = rx;
}
if (rx > w / 2) {
rx = w / 2;
if (rx > width / 2) {
rx = width / 2;
}
if (ry > h / 2) {
ry = h / 2;
if (ry > height / 2) {
ry = height / 2;
}
CGPathAddRoundedRect(path, nil, CGRectMake(x, y, w, h), rx, ry);
CGPathAddRoundedRect(path, nil, CGRectMake(x, y, width, height), rx, ry);
} else {
CGPathAddRect(path, nil, CGRectMake(x, y, w, h));
CGPathAddRect(path, nil, CGRectMake(x, y, width, height));
}
return (CGPathRef)CFAutorelease(path);

View File

@@ -7,7 +7,7 @@
*/
/**
* based on
* based on CurvyText by iosptl: https://github.com/iosptl/ios7ptl/blob/master/ch21-Text/CurvyText/CurvyText/CurvyTextView.m
*/
#import "RNSVGBezierPath.h"

View File

@@ -13,6 +13,7 @@
#import "RNSVGSolidColorBrush.h"
#import "RCTLog.h"
#import "RNSVGCGFCRule.h"
#import "RNSVGVBMOS.h"
@implementation RCTConvert (RNSVG)
@@ -73,6 +74,13 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
@"nonzero": @(kRNSVGCGFCRuleNonzero),
}), kRNSVGCGFCRuleNonzero, intValue)
RCT_ENUM_CONVERTER(RNSVGVBMOS, (@{
@"meet": @(kRNSVGVBMOSMeet),
@"slice": @(kRNSVGVBMOSSlice),
@"none": @(kRNSVGVBMOSNone)
}), kRNSVGVBMOSMeet, intValue)
// 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.
+ (RNSVGTextFrame)RNSVGTextFrame:(id)json

View File

@@ -7,14 +7,21 @@
*/
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
@interface RNSVGPercentageConverter : NSObject
- (NSRegularExpression *) getPercentageRegularExpression;
- (float) percentageToFloat:(NSString *)percentage relative:(float)relative offset:(float)offset;
- (instancetype) initWithRelativeAndOffset:(CGFloat)relative offset:(CGFloat)offset;
- (float) stringToFloat:(NSString *)string relative:(float)relative offset:(float)offset;
- (CGFloat) percentageToFloat:(NSString *)percentage relative:(CGFloat)relative offset:(CGFloat)offset;
- (CGFloat) percentageToFloat:(NSString *)percentage;
- (CGFloat) stringToFloat:(NSString *)string relative:(CGFloat)relative offset:(CGFloat)offset;
- (CGFloat) stringToFloat:(NSString *)string;
- (BOOL) isPercentage:(NSString *) string;

View File

@@ -10,13 +10,24 @@
@implementation RNSVGPercentageConverter
{
CGFloat _relative;
CGFloat _offset;
NSRegularExpression *percentageRegularExpression;
}
- (instancetype) initWithRelativeAndOffset:(CGFloat)relative offset:(CGFloat)offset
{
if (self = [super init]) {
_relative = relative;
_offset = offset;
percentageRegularExpression = [[NSRegularExpression alloc] initWithPattern:@"^(\\-?\\d+(?:\\.\\d+)?)%$" options:0 error:nil];
}
return self;
}
- (id)init
{
self = [super init];
if (self) {
if (self = [super init]) {
percentageRegularExpression = [[NSRegularExpression alloc] initWithPattern:@"^(\\-?\\d+(?:\\.\\d+)?)%$" options:0 error:nil];
}
return self;
@@ -27,20 +38,29 @@
return percentageRegularExpression;
}
- (float) stringToFloat:(NSString *)percentage relative:(float)relative offset:(float)offset
- (CGFloat) stringToFloat:(NSString *)string
{
if ([self isPercentage:percentage] == NO) {
return [percentage floatValue];
return [self stringToFloat:string relative:_relative offset:_offset];
}
- (CGFloat) stringToFloat:(NSString *)string relative:(CGFloat)relative offset:(CGFloat)offset
{
if ([self isPercentage:string] == NO) {
return [string floatValue];
} else {
return [self percentageToFloat:percentage relative:relative offset:offset];
return [self percentageToFloat:string relative:relative offset:offset];
}
}
- (float) percentageToFloat:(NSString *)percentage relative:(float)relative offset:(float)offset
- (CGFloat) percentageToFloat:(NSString *)percentage
{
return [self percentageToFloat:percentage relative:_relative offset:_offset];
}
- (CGFloat) percentageToFloat:(NSString *)percentage relative:(CGFloat)relative offset:(CGFloat)offset
{
__block CGFloat matched;
__block float matched;
[percentageRegularExpression enumerateMatchesInString:percentage
options:0
range:NSMakeRange(0, percentage.length)

13
ios/Utils/RNSVGVBMOS.h Normal file
View 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.
*/
typedef CF_ENUM(int32_t, RNSVGVBMOS) {
kRNSVGVBMOSMeet,
kRNSVGVBMOSSlice,
kRNSVGVBMOSNone
};

View 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 "RNSVGGroupManager.h"
@interface RNSVGViewBoxManager : RNSVGGroupManager
@end

View File

@@ -0,0 +1,29 @@
/**
* 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 "RNSVGViewBoxManager.h"
#import "RNSVGViewBox.h"
#import "RNSVGVBMOS.h"
@implementation RNSVGViewBoxManager
RCT_EXPORT_MODULE()
- (RNSVGViewBox *)node
{
return [RNSVGViewBox new];
}
RCT_EXPORT_VIEW_PROPERTY(minX, NSString)
RCT_EXPORT_VIEW_PROPERTY(minY, NSString)
RCT_EXPORT_VIEW_PROPERTY(vbWidth, NSString)
RCT_EXPORT_VIEW_PROPERTY(vbHeight, NSString)
RCT_EXPORT_VIEW_PROPERTY(align, NSString)
RCT_EXPORT_VIEW_PROPERTY(meetOrSlice, RNSVGVBMOS)
@end

View File

@@ -41,6 +41,15 @@ function fontAndLinesDiffer(a, b) {
return arrayDiffer(a.lines, b.lines);
}
const ViewBoxAttributes = {
minX: true,
minY: true,
vbWidth: true,
vbHeight: true,
align: true,
meetOrSlice: true
};
const NodeAttributes = {
name: true,
transform: {
@@ -180,5 +189,6 @@ export {
UseAttributes,
RenderableOnlyAttributes,
LinearGradientAttributes,
RadialGradientAttributes
RadialGradientAttributes,
ViewBoxAttributes
};

View File

@@ -1,4 +1,4 @@
import SerializablePath from 'react-native/Libraries/ART/ARTSerializablePath';
import SerializablePath from '../SerializablePath';
import clipReg from './patternReg';
const clipRules = {

View File

@@ -1,71 +0,0 @@
export default function({viewbox, width, height, preserveAspectRatio, x: dx, y: dy, dScale, dScaleX, dScaleY}) {
if (!viewbox || !width || !height) {
return false;
}
if (typeof viewbox === 'string') {
let parts = viewbox.trim().split(/\s+/);
let vw = +parts[2];
let vh = +parts[3];
// width or height can`t be negative
if (vw < 0 || vh < 0 || parts.length !== 4) {
return false;
}
// width or height equals zero disable render
if (!vw || !vh) {
return {
x: 0,
y: 0,
scaleX: 0,
scaleY: 0
};
}
let vx = +parts[0] || 0;
let vy = +parts[1] || 0;
let preserve = preserveAspectRatio !== 'none';
let scaleX = 1;
let scaleY = 1;
let x = 0;
let y = 0;
let sx = width / vw;
let sy = height / vh;
if (preserve) {
scaleX = scaleY = Math.min(sx, sy);
x = width / 2 - Math.min(vw, vh) * scaleX / 2 - vx * scaleX;
y = 0 - vy * scaleX;
if (sx < sy) {
[x, y] = [y, x];
}
} else {
scaleX = sx;
scaleY = sy;
x = -vx * sx;
y = -vy * sy;
}
//if (shouldTransform) {
x += (+dx || 0);
y += (+dy || 0);
if (dScale) {
scaleX *= (+dScale || 1);
scaleY *= (+dScale || 1);
} else {
scaleX *= (+dScaleX || 1);
scaleY *= (+dScaleY || 1);
}
//}
return {
x,
y,
scaleX,
scaleY
};
}
return false;
}