mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-20 05:55:10 +00:00
refactor text render
Support G inherit props. Refactor text render. Text glyphs will perfectly draw along the path
This commit is contained in:
@@ -29,8 +29,8 @@ export {
|
|||||||
Text,
|
Text,
|
||||||
Stroking,
|
Stroking,
|
||||||
G,
|
G,
|
||||||
//Use,
|
Use,
|
||||||
//Symbol,
|
Symbol,
|
||||||
Gradients,
|
Gradients,
|
||||||
Clipping,
|
Clipping,
|
||||||
Image,
|
Image,
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ class GTransform extends Component{
|
|||||||
<G
|
<G
|
||||||
rotate="50"
|
rotate="50"
|
||||||
origin="100, 50"
|
origin="100, 50"
|
||||||
|
scale="0.75"
|
||||||
id="group"
|
id="group"
|
||||||
>
|
>
|
||||||
<Line
|
<Line
|
||||||
@@ -96,7 +97,7 @@ class GTransform extends Component{
|
|||||||
>
|
>
|
||||||
Text grouped with shapes</Text>
|
Text grouped with shapes</Text>
|
||||||
</G>
|
</G>
|
||||||
<Use href="url(#group)" x="5" rotate="0" />
|
<Use href="url(#group)" x="5" y="20" rotate="-50" stroke="red" />
|
||||||
</Svg>;
|
</Svg>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,30 +107,35 @@ const icon = <Svg
|
|||||||
width="20"
|
width="20"
|
||||||
>
|
>
|
||||||
<G
|
<G
|
||||||
r="3"
|
|
||||||
fill="purple"
|
fill="purple"
|
||||||
stroke="pink"
|
stroke="pink"
|
||||||
|
strokeWidth="1"
|
||||||
>
|
>
|
||||||
<Circle
|
<Circle
|
||||||
cx="5"
|
cx="5"
|
||||||
cy="5"
|
cy="5"
|
||||||
|
r="3"
|
||||||
/>
|
/>
|
||||||
<Circle
|
<Circle
|
||||||
cx="5"
|
cx="5"
|
||||||
cy="15"
|
cy="15"
|
||||||
|
r="3"
|
||||||
/>
|
/>
|
||||||
<Circle
|
<Circle
|
||||||
cx="10"
|
cx="10"
|
||||||
cy="10"
|
cy="10"
|
||||||
fill="green"
|
fill="green"
|
||||||
|
r="3"
|
||||||
/>
|
/>
|
||||||
<Circle
|
<Circle
|
||||||
cx="15"
|
cx="15"
|
||||||
cy="5"
|
cy="5"
|
||||||
|
r="3"
|
||||||
/>
|
/>
|
||||||
<Circle
|
<Circle
|
||||||
cx="15"
|
cx="15"
|
||||||
cy="15"
|
cy="15"
|
||||||
|
r="3"
|
||||||
/>
|
/>
|
||||||
</G>
|
</G>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class StrokeExample extends Component{
|
|||||||
static title = 'The stroke property defines the color of a line, text or outline of an element';
|
static title = 'The stroke property defines the color of a line, text or outline of an element';
|
||||||
render() {
|
render() {
|
||||||
return <Svg height="80" width="225">
|
return <Svg height="80" width="225">
|
||||||
<G fill="none">
|
<G>
|
||||||
<Path stroke="red" d="M5 20 l215 0" />
|
<Path stroke="red" d="M5 20 l215 0" />
|
||||||
<Path stroke="black" d="M5 40 l215 0" />
|
<Path stroke="black" d="M5 40 l215 0" />
|
||||||
<Path stroke="blue" d="M5 60 l215 0" />
|
<Path stroke="blue" d="M5 60 l215 0" />
|
||||||
@@ -32,10 +32,12 @@ class StrokeLinecap extends Component{
|
|||||||
static title = 'The strokeLinecap property defines different types of endings to an open path';
|
static title = 'The strokeLinecap property defines different types of endings to an open path';
|
||||||
render() {
|
render() {
|
||||||
return <Svg height="80" width="225">
|
return <Svg height="80" width="225">
|
||||||
<G fill="none" stroke="black">
|
<G stroke="red">
|
||||||
<Path strokeLinecap="butt" strokeWidth="8" d="M5 20 l215 0" />
|
<G strokeWidth="8">
|
||||||
<Path strokeLinecap="round" strokeWidth="8" d="M5 40 l215 0" />
|
<Path strokeLinecap="butt" d="M5 20 l215 0" />
|
||||||
<Path strokeLinecap="square" strokeWidth="8" d="M5 60 l215 0" />
|
<Path strokeLinecap="round" d="M5 40 l215 0" />
|
||||||
|
<Path strokeLinecap="square" d="M5 60 l215 0" />
|
||||||
|
</G>
|
||||||
</G>
|
</G>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
}
|
}
|
||||||
@@ -142,7 +144,6 @@ const icon = <Svg
|
|||||||
</Svg>;
|
</Svg>;
|
||||||
|
|
||||||
const samples = [StrokeExample, StrokeLinecap, StrokeDasharray, StrokeDashoffset, StrokePattern];
|
const samples = [StrokeExample, StrokeLinecap, StrokeDasharray, StrokeDashoffset, StrokePattern];
|
||||||
|
|
||||||
export {
|
export {
|
||||||
icon,
|
icon,
|
||||||
samples
|
samples
|
||||||
|
|||||||
@@ -21,19 +21,19 @@ class SymbolExample extends Component{
|
|||||||
</Symbol>
|
</Symbol>
|
||||||
|
|
||||||
<Use
|
<Use
|
||||||
href="#symbol"
|
href="url(#symbol)"
|
||||||
x="0"
|
x="0"
|
||||||
y="0"
|
y="0"
|
||||||
/>
|
/>
|
||||||
<Use
|
<Use
|
||||||
href="#symbol"
|
href="url(#symbol)"
|
||||||
x="0"
|
x="0"
|
||||||
y="50"
|
y="50"
|
||||||
width="75"
|
width="75"
|
||||||
height="38"
|
height="38"
|
||||||
/>
|
/>
|
||||||
<Use
|
<Use
|
||||||
href="#symbol"
|
href="url(#symbol)"
|
||||||
x="0"
|
x="0"
|
||||||
y="100"
|
y="100"
|
||||||
width="50"
|
width="50"
|
||||||
@@ -53,14 +53,14 @@ const icon = <Svg
|
|||||||
</Symbol>
|
</Symbol>
|
||||||
|
|
||||||
<Use
|
<Use
|
||||||
href="#symbol"
|
href="url(#symbol)"
|
||||||
x="0"
|
x="0"
|
||||||
y="0"
|
y="0"
|
||||||
width="20"
|
width="20"
|
||||||
height="10"
|
height="10"
|
||||||
/>
|
/>
|
||||||
<Use
|
<Use
|
||||||
href="#symbol"
|
href="url(#symbol)"
|
||||||
x="0"
|
x="0"
|
||||||
y="12"
|
y="12"
|
||||||
width="20"
|
width="20"
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ import Svg, {
|
|||||||
Text,
|
Text,
|
||||||
LinearGradient,
|
LinearGradient,
|
||||||
Stop,
|
Stop,
|
||||||
Defs
|
Defs,
|
||||||
|
Path,
|
||||||
|
G
|
||||||
} from 'react-native-svg';
|
} from 'react-native-svg';
|
||||||
|
|
||||||
class TextExample extends Component{
|
class TextExample extends Component{
|
||||||
@@ -119,22 +121,33 @@ class TextPath extends Component{
|
|||||||
static title = 'Draw text along path';
|
static title = 'Draw text along path';
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <Svg
|
const path = `
|
||||||
height="60"
|
M 0 20
|
||||||
width="200"
|
h 10
|
||||||
>
|
|
||||||
<Text
|
|
||||||
fill="red"
|
|
||||||
path={`
|
|
||||||
M 10 20
|
|
||||||
C 20 10 30 0 40 10
|
C 20 10 30 0 40 10
|
||||||
C 50 20 60 30 70 20
|
C 50 20 60 30 70 20
|
||||||
C 80 10 90 10 90 10
|
C 80 10 90 10 90 10
|
||||||
C 110 20 120 30 120 20
|
C 110 20 120 30 120 20
|
||||||
C 140 10 150 10 150 10
|
C 140 10 150 10 150 10
|
||||||
`}
|
a 50 50 0 1 1 20 110
|
||||||
y="20"
|
`;
|
||||||
>We go up, then we go down, then up again</Text>
|
|
||||||
|
return <Svg
|
||||||
|
height="60"
|
||||||
|
width="200"
|
||||||
|
>
|
||||||
|
<G y="20">
|
||||||
|
<Text
|
||||||
|
fill="blue"
|
||||||
|
path={path}
|
||||||
|
>We go up, then we go down, then up again</Text>
|
||||||
|
<Path
|
||||||
|
d={path}
|
||||||
|
fill="none"
|
||||||
|
stroke="red"
|
||||||
|
strokeWidth="1"
|
||||||
|
/>
|
||||||
|
</G>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ class UseExample extends Component{
|
|||||||
</G>
|
</G>
|
||||||
</G>
|
</G>
|
||||||
</Defs>
|
</Defs>
|
||||||
<Use href="#shape" x="20" y="0"/>
|
<Use href="url(#shape)" x="20" y="0"/>
|
||||||
<Use href="#shape" x="170"y="0" />
|
<Use href="url(#shape)" x="170"y="0" />
|
||||||
</Svg>;
|
</Svg>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,9 +43,9 @@ class UseShapes extends Component{
|
|||||||
<G id="shape">
|
<G id="shape">
|
||||||
<Rect x="0" y="0" width="50" height="50" />
|
<Rect x="0" y="0" width="50" height="50" />
|
||||||
</G>
|
</G>
|
||||||
<Use href="#shape" x="75" y="50" fill="#0f0"/>
|
<Use href="url(#shape)" x="75" y="50" fill="#0f0"/>
|
||||||
<Use href="#shape" x="110" y="0" stroke="#0ff" fill="#8a3" rotation="45" origin="25, 25"/>
|
<Use href="url(#shape)" x="110" y="0" stroke="#0ff" fill="#8a3" rotation="45" origin="25, 25"/>
|
||||||
<Use href="#shape" x="150" y="50" stroke="#0f0" fill="none"/>
|
<Use href="url(#shape)" x="150" y="50" stroke="#0f0" fill="none"/>
|
||||||
</Svg>;
|
</Svg>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ const icon = <Svg
|
|||||||
stroke="#8a3"
|
stroke="#8a3"
|
||||||
id="line"
|
id="line"
|
||||||
/>
|
/>
|
||||||
<Use href="#line" x="10" stroke="#3a8" />
|
<Use href="url(#line)" x="10" stroke="#3a8" />
|
||||||
</Svg>;
|
</Svg>;
|
||||||
|
|
||||||
const samples = [UseExample, UseShapes];
|
const samples = [UseExample, UseShapes];
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
|
|||||||
|
|
||||||
protected @Nullable Path mClipPath;
|
protected @Nullable Path mClipPath;
|
||||||
protected @Nullable String mClipPathRef;
|
protected @Nullable String mClipPathRef;
|
||||||
private static final int PATH_TYPE_ARC = 4;
|
|
||||||
private static final int PATH_TYPE_CLOSE = 1;
|
private static final int PATH_TYPE_CLOSE = 1;
|
||||||
private static final int PATH_TYPE_CURVETO = 3;
|
private static final int PATH_TYPE_CURVETO = 3;
|
||||||
private static final int PATH_TYPE_LINETO = 2;
|
private static final int PATH_TYPE_LINETO = 2;
|
||||||
@@ -233,30 +232,6 @@ public abstract class RNSVGVirtualNode extends LayoutShadowNode {
|
|||||||
data[i++] * mScale,
|
data[i++] * mScale,
|
||||||
data[i++] * mScale);
|
data[i++] * mScale);
|
||||||
break;
|
break;
|
||||||
case PATH_TYPE_ARC:
|
|
||||||
{
|
|
||||||
float x = data[i++] * mScale;
|
|
||||||
float y = data[i++] * mScale;
|
|
||||||
float r = data[i++] * mScale;
|
|
||||||
float start = (float) Math.toDegrees(data[i++]);
|
|
||||||
float end = (float) Math.toDegrees(data[i++]);
|
|
||||||
|
|
||||||
boolean clockwise = data[i++] == 1f;
|
|
||||||
float sweep = end - start;
|
|
||||||
if (Math.abs(sweep) > 360) {
|
|
||||||
sweep = 360;
|
|
||||||
} else {
|
|
||||||
sweep = modulus(sweep, 360);
|
|
||||||
}
|
|
||||||
if (!clockwise && sweep < 360) {
|
|
||||||
start = end;
|
|
||||||
sweep = 360 - sweep;
|
|
||||||
}
|
|
||||||
|
|
||||||
RectF oval = new RectF(x - r, y - r, x + r, y + r);
|
|
||||||
path.addArc(oval, start, sweep);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
throw new JSApplicationIllegalArgumentException(
|
throw new JSApplicationIllegalArgumentException(
|
||||||
"Unrecognized drawing instruction " + type);
|
"Unrecognized drawing instruction " + type);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import createReactNativeComponentClass from 'react/lib/createReactNativeComponen
|
|||||||
import {transformProps} from '../lib/props';
|
import {transformProps} from '../lib/props';
|
||||||
import {GroupAttributes} from '../lib/attributes';
|
import {GroupAttributes} from '../lib/attributes';
|
||||||
import extractProps from '../lib/extract/extractProps';
|
import extractProps from '../lib/extract/extractProps';
|
||||||
import reusableProps from '../lib/reusableProps';
|
|
||||||
|
|
||||||
class G extends Component{
|
class G extends Component{
|
||||||
static displayName = 'G';
|
static displayName = 'G';
|
||||||
@@ -27,7 +26,6 @@ class G extends Component{
|
|||||||
return <RNSVGGroup
|
return <RNSVGGroup
|
||||||
{...extractedProps}
|
{...extractedProps}
|
||||||
ref={ele => this.root = ele}
|
ref={ele => this.root = ele}
|
||||||
mergeList={reusableProps(extractedProps, props)}
|
|
||||||
>
|
>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</RNSVGGroup>;
|
</RNSVGGroup>;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React, {Component, PropTypes} from 'react';
|
import React, {Component, PropTypes} from 'react';
|
||||||
|
|
||||||
import ViewBox from './ViewBox';
|
import ViewBox from './ViewBox';
|
||||||
import Defs from './Defs';
|
import G from './G';
|
||||||
|
|
||||||
class SymbolElement extends Component{
|
class SymbolElement extends Component{
|
||||||
static displayName = 'Symbol';
|
static displayName = 'Symbol';
|
||||||
static propType = {
|
static propType = {
|
||||||
@@ -9,10 +10,8 @@ class SymbolElement extends Component{
|
|||||||
};
|
};
|
||||||
render() {
|
render() {
|
||||||
let {props} = this;
|
let {props} = this;
|
||||||
return <Defs.Item
|
|
||||||
id={props.id}
|
return <G id={props.id}>
|
||||||
svgId={props.svgId}
|
|
||||||
>
|
|
||||||
<ViewBox
|
<ViewBox
|
||||||
{...props}
|
{...props}
|
||||||
viewbox={props.viewbox}
|
viewbox={props.viewbox}
|
||||||
@@ -20,7 +19,7 @@ class SymbolElement extends Component{
|
|||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</ViewBox>
|
</ViewBox>
|
||||||
</Defs.Item>;
|
</G>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import Shape from './Shape';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import patternReg from '../lib/extract/patternReg';
|
import patternReg from '../lib/extract/patternReg';
|
||||||
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
|
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
|
||||||
import reusableProps from '../lib/reusableProps';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
class Defs extends Shape {
|
class Defs extends Shape {
|
||||||
@@ -44,7 +43,6 @@ class Defs extends Shape {
|
|||||||
return <RNSVGUse
|
return <RNSVGUse
|
||||||
ref={ele => this.root = ele}
|
ref={ele => this.root = ele}
|
||||||
{...extractedProps}
|
{...extractedProps}
|
||||||
mergeList={reusableProps(extractedProps, props)}
|
|
||||||
href={href}
|
href={href}
|
||||||
>{props.children}</RNSVGUse>;
|
>{props.children}</RNSVGUse>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ class ViewBox extends Component{
|
|||||||
y = viewbox.y;
|
y = viewbox.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console.log(viewbox);
|
||||||
return <G
|
return <G
|
||||||
{...this.props}
|
{...this.props}
|
||||||
x={x}
|
x={x}
|
||||||
@@ -26,6 +28,7 @@ class ViewBox extends Component{
|
|||||||
scaleY={scaleY}
|
scaleY={scaleY}
|
||||||
preserveAspectRatio={null}
|
preserveAspectRatio={null}
|
||||||
viewbox={null}
|
viewbox={null}
|
||||||
|
id={null}
|
||||||
>
|
>
|
||||||
{(!scaleX || !scaleY) ? null : this.props.children}
|
{(!scaleX || !scaleY) ? null : this.props.children}
|
||||||
</G>;
|
</G>;
|
||||||
|
|||||||
@@ -11,11 +11,9 @@
|
|||||||
#import "RNSVGContainer.h"
|
#import "RNSVGContainer.h"
|
||||||
#import "RNSVGCGFCRule.h"
|
#import "RNSVGCGFCRule.h"
|
||||||
#import "RNSVGSvgView.h"
|
#import "RNSVGSvgView.h"
|
||||||
#import "RNSVGNode.h"
|
#import "RNSVGRenderable.h"
|
||||||
|
|
||||||
@interface RNSVGGroup : RNSVGNode <RNSVGContainer>
|
@interface RNSVGGroup : RNSVGRenderable <RNSVGContainer>
|
||||||
|
|
||||||
@property (nonatomic, copy) NSArray<NSString *> *mergeList;
|
|
||||||
|
|
||||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
|
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
[self clip:context];
|
[self clip:context];
|
||||||
|
|
||||||
for (RNSVGNode *node in self.subviews) {
|
for (RNSVGNode *node in self.subviews) {
|
||||||
//[node mergeProperties:self mergeList:self.mergeList];
|
[node inheritProperties:self inheritedList:self.propList];
|
||||||
[node renderTo:context];
|
[node renderTo:context];
|
||||||
|
|
||||||
if (node.responsible && !svg.responsible) {
|
if (node.responsible && !svg.responsible) {
|
||||||
@@ -79,4 +79,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)inheritProperties:(__kindof RNSVGNode *)parent inheritedList:(NSArray<NSString *> *)inheritedList;
|
||||||
|
{
|
||||||
|
for (NSString *key in inheritedList) {
|
||||||
|
[self inheritProperty:parent propName:key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.stroke) {
|
if (self.stroke) {
|
||||||
CGContextSetLineWidth(context, self.strokeWidth);
|
CGContextSetLineWidth(context, self.strokeWidth);
|
||||||
CGContextSetLineCap(context, self.strokeLinecap);
|
CGContextSetLineCap(context, self.strokeLinecap);
|
||||||
|
|||||||
@@ -15,6 +15,5 @@
|
|||||||
@interface RNSVGUse : RNSVGRenderable
|
@interface RNSVGUse : RNSVGRenderable
|
||||||
|
|
||||||
@property (nonatomic, strong) NSString *href;
|
@property (nonatomic, strong) NSString *href;
|
||||||
@property (nonatomic, copy) NSArray<NSString *> *mergeList;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -10,22 +10,24 @@
|
|||||||
|
|
||||||
@implementation RNSVGUse
|
@implementation RNSVGUse
|
||||||
|
|
||||||
- (void)setMergeList:(NSArray<NSString *> *)mergeList
|
- (void)setHref:(NSString *)href
|
||||||
{
|
{
|
||||||
if (mergeList == _mergeList) {
|
if (href == _href) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_mergeList = mergeList;
|
|
||||||
[self invalidate];
|
[self invalidate];
|
||||||
|
_href = href;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)renderLayerTo:(CGContextRef)context
|
- (void)renderLayerTo:(CGContextRef)context
|
||||||
{
|
{
|
||||||
RNSVGNode* template = [[self getSvgView] getDefinedTemplate:self.href];
|
RNSVGNode* template = [[self getSvgView] getDefinedTemplate:self.href];
|
||||||
if (template) {
|
if (template) {
|
||||||
[self beginTransparencyLayer:context];
|
[self beginTransparencyLayer:context];
|
||||||
[self clip:context];
|
[self clip:context];
|
||||||
[template mergeProperties:self mergeList:self.mergeList];
|
[template mergeProperties:self mergeList:self.propList];
|
||||||
[template renderTo:context];
|
[template renderTo:context];
|
||||||
[template resetProperties];
|
[template resetProperties];
|
||||||
[self endTransparencyLayer:context];
|
[self endTransparencyLayer:context];
|
||||||
|
|||||||
@@ -15,12 +15,12 @@
|
|||||||
1023B4901D3DF4C40051496D /* RNSVGDefination.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B48F1D3DF4C40051496D /* RNSVGDefination.m */; };
|
1023B4901D3DF4C40051496D /* RNSVGDefination.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B48F1D3DF4C40051496D /* RNSVGDefination.m */; };
|
||||||
1023B4931D3DF5060051496D /* RNSVGUse.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B4921D3DF5060051496D /* RNSVGUse.m */; };
|
1023B4931D3DF5060051496D /* RNSVGUse.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B4921D3DF5060051496D /* RNSVGUse.m */; };
|
||||||
1023B4961D3DF57D0051496D /* RNSVGUseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B4951D3DF57D0051496D /* RNSVGUseManager.m */; };
|
1023B4961D3DF57D0051496D /* RNSVGUseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B4951D3DF57D0051496D /* RNSVGUseManager.m */; };
|
||||||
|
103371321D41C5C90028AF13 /* RNSVGBezierPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 103371311D41C5C90028AF13 /* RNSVGBezierPath.m */; };
|
||||||
1039D2891CE71EB7001E90A8 /* RNSVGGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2821CE71EB7001E90A8 /* RNSVGGroup.m */; };
|
1039D2891CE71EB7001E90A8 /* RNSVGGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2821CE71EB7001E90A8 /* RNSVGGroup.m */; };
|
||||||
1039D28A1CE71EB7001E90A8 /* RNSVGImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2841CE71EB7001E90A8 /* RNSVGImage.m */; };
|
1039D28A1CE71EB7001E90A8 /* RNSVGImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2841CE71EB7001E90A8 /* RNSVGImage.m */; };
|
||||||
1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2861CE71EB7001E90A8 /* RNSVGPath.m */; };
|
1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2861CE71EB7001E90A8 /* RNSVGPath.m */; };
|
||||||
1039D28C1CE71EB7001E90A8 /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2881CE71EB7001E90A8 /* RNSVGSvgView.m */; };
|
1039D28C1CE71EB7001E90A8 /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2881CE71EB7001E90A8 /* RNSVGSvgView.m */; };
|
||||||
1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2901CE71EC2001E90A8 /* RNSVGText.m */; };
|
1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2901CE71EC2001E90A8 /* RNSVGText.m */; };
|
||||||
1039D2961CE71EC2001E90A8 /* UIBezierPath-Points.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2931CE71EC2001E90A8 /* UIBezierPath-Points.m */; };
|
|
||||||
1039D2A01CE72177001E90A8 /* RCTConvert+RNSVG.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D29C1CE72177001E90A8 /* RCTConvert+RNSVG.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 */; };
|
1039D2B01CE72F27001E90A8 /* RNSVGPercentageConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2AF1CE72F27001E90A8 /* RNSVGPercentageConverter.m */; };
|
||||||
10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D1D1CE74E3100887C2B /* RNSVGCircleManager.m */; };
|
10BA0D341CE74E3100887C2B /* RNSVGCircleManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10BA0D1D1CE74E3100887C2B /* RNSVGCircleManager.m */; };
|
||||||
@@ -79,6 +79,8 @@
|
|||||||
1023B4921D3DF5060051496D /* RNSVGUse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGUse.m; path = Elements/RNSVGUse.m; sourceTree = "<group>"; };
|
1023B4921D3DF5060051496D /* RNSVGUse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGUse.m; path = Elements/RNSVGUse.m; sourceTree = "<group>"; };
|
||||||
1023B4941D3DF57D0051496D /* RNSVGUseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGUseManager.h; sourceTree = "<group>"; };
|
1023B4941D3DF57D0051496D /* RNSVGUseManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGUseManager.h; sourceTree = "<group>"; };
|
||||||
1023B4951D3DF57D0051496D /* RNSVGUseManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGUseManager.m; sourceTree = "<group>"; };
|
1023B4951D3DF57D0051496D /* RNSVGUseManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGUseManager.m; sourceTree = "<group>"; };
|
||||||
|
103371311D41C5C90028AF13 /* RNSVGBezierPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGBezierPath.m; path = Text/RNSVGBezierPath.m; sourceTree = "<group>"; };
|
||||||
|
103371331D41D3400028AF13 /* RNSVGBezierPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGBezierPath.h; path = Text/RNSVGBezierPath.h; sourceTree = "<group>"; };
|
||||||
1039D2811CE71EB7001E90A8 /* RNSVGGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGGroup.h; path = Elements/RNSVGGroup.h; sourceTree = "<group>"; };
|
1039D2811CE71EB7001E90A8 /* RNSVGGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGGroup.h; path = Elements/RNSVGGroup.h; sourceTree = "<group>"; };
|
||||||
1039D2821CE71EB7001E90A8 /* RNSVGGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGGroup.m; path = Elements/RNSVGGroup.m; sourceTree = "<group>"; };
|
1039D2821CE71EB7001E90A8 /* RNSVGGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGGroup.m; path = Elements/RNSVGGroup.m; sourceTree = "<group>"; };
|
||||||
1039D2831CE71EB7001E90A8 /* RNSVGImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGImage.h; path = Elements/RNSVGImage.h; sourceTree = "<group>"; };
|
1039D2831CE71EB7001E90A8 /* RNSVGImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGImage.h; path = Elements/RNSVGImage.h; sourceTree = "<group>"; };
|
||||||
@@ -90,8 +92,6 @@
|
|||||||
1039D28F1CE71EC2001E90A8 /* RNSVGText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGText.h; path = Text/RNSVGText.h; sourceTree = "<group>"; };
|
1039D28F1CE71EC2001E90A8 /* RNSVGText.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGText.h; path = Text/RNSVGText.h; sourceTree = "<group>"; };
|
||||||
1039D2901CE71EC2001E90A8 /* RNSVGText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGText.m; path = Text/RNSVGText.m; sourceTree = "<group>"; };
|
1039D2901CE71EC2001E90A8 /* RNSVGText.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGText.m; path = Text/RNSVGText.m; sourceTree = "<group>"; };
|
||||||
1039D2911CE71EC2001E90A8 /* RNSVGTextFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextFrame.h; path = Text/RNSVGTextFrame.h; sourceTree = "<group>"; };
|
1039D2911CE71EC2001E90A8 /* RNSVGTextFrame.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGTextFrame.h; path = Text/RNSVGTextFrame.h; sourceTree = "<group>"; };
|
||||||
1039D2921CE71EC2001E90A8 /* UIBezierPath-Points.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIBezierPath-Points.h"; path = "Text/UIBezierPath-Points.h"; sourceTree = "<group>"; };
|
|
||||||
1039D2931CE71EC2001E90A8 /* UIBezierPath-Points.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIBezierPath-Points.m"; path = "Text/UIBezierPath-Points.m"; 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>"; };
|
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>"; };
|
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>"; };
|
1039D29D1CE72177001E90A8 /* RNSVGCGFCRule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGCGFCRule.h; path = Utils/RNSVGCGFCRule.h; sourceTree = "<group>"; };
|
||||||
@@ -262,11 +262,11 @@
|
|||||||
1039D27F1CE71D9B001E90A8 /* Text */ = {
|
1039D27F1CE71D9B001E90A8 /* Text */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
103371331D41D3400028AF13 /* RNSVGBezierPath.h */,
|
||||||
|
103371311D41C5C90028AF13 /* RNSVGBezierPath.m */,
|
||||||
1039D28F1CE71EC2001E90A8 /* RNSVGText.h */,
|
1039D28F1CE71EC2001E90A8 /* RNSVGText.h */,
|
||||||
1039D2901CE71EC2001E90A8 /* RNSVGText.m */,
|
1039D2901CE71EC2001E90A8 /* RNSVGText.m */,
|
||||||
1039D2911CE71EC2001E90A8 /* RNSVGTextFrame.h */,
|
1039D2911CE71EC2001E90A8 /* RNSVGTextFrame.h */,
|
||||||
1039D2921CE71EC2001E90A8 /* UIBezierPath-Points.h */,
|
|
||||||
1039D2931CE71EC2001E90A8 /* UIBezierPath-Points.m */,
|
|
||||||
);
|
);
|
||||||
name = Text;
|
name = Text;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -386,7 +386,7 @@
|
|||||||
10BA0D3E1CE74E3100887C2B /* RNSVGSvgViewManager.m in Sources */,
|
10BA0D3E1CE74E3100887C2B /* RNSVGSvgViewManager.m in Sources */,
|
||||||
0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColorBrush.m in Sources */,
|
0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColorBrush.m in Sources */,
|
||||||
10BA0D3A1CE74E3100887C2B /* RNSVGPathManager.m in Sources */,
|
10BA0D3A1CE74E3100887C2B /* RNSVGPathManager.m in Sources */,
|
||||||
1039D2961CE71EC2001E90A8 /* UIBezierPath-Points.m in Sources */,
|
103371321D41C5C90028AF13 /* RNSVGBezierPath.m in Sources */,
|
||||||
10BA0D3C1CE74E3100887C2B /* RNSVGRenderableManager.m in Sources */,
|
10BA0D3C1CE74E3100887C2B /* RNSVGRenderableManager.m in Sources */,
|
||||||
10BEC1BD1D3F66F500FDCB19 /* RNSVGRadialGradient.m in Sources */,
|
10BEC1BD1D3F66F500FDCB19 /* RNSVGRadialGradient.m in Sources */,
|
||||||
10BEC1C31D3F680F00FDCB19 /* RNSVGRadialGradientManager.m in Sources */,
|
10BEC1C31D3F680F00FDCB19 /* RNSVGRadialGradientManager.m in Sources */,
|
||||||
|
|||||||
@@ -67,15 +67,22 @@
|
|||||||
- (void)removeDefination;
|
- (void)removeDefination;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Just for template node to merge target node`s properties into owned properties
|
* just for template node to merge target node`s properties into owned properties
|
||||||
*/
|
*/
|
||||||
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList;
|
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Just for template node to reset all owned properties once after rendered.
|
* just for template node to reset all owned properties once after rendered.
|
||||||
*/
|
*/
|
||||||
- (void)resetProperties;
|
- (void)resetProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inherit properties from parent g element
|
||||||
|
*/
|
||||||
|
- (void)inheritProperties:(__kindof RNSVGNode *)parent inheritedList:(NSArray<NSString *> *)inheritedList;
|
||||||
|
|
||||||
|
- (void)inheritProperty:(__kindof RNSVGNode *)parent propName:(NSString *)propName;
|
||||||
|
|
||||||
- (void)beginTransparencyLayer:(CGContextRef)context;
|
- (void)beginTransparencyLayer:(CGContextRef)context;
|
||||||
|
|
||||||
- (void)endTransparencyLayer:(CGContextRef)context;
|
- (void)endTransparencyLayer:(CGContextRef)context;
|
||||||
|
|||||||
@@ -211,6 +211,16 @@
|
|||||||
// abstract
|
// abstract
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)inheritProperty:(__kindof RNSVGNode *)parent propName:(NSString *)propName
|
||||||
|
{
|
||||||
|
// abstract
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)inheritProperties:(__kindof RNSVGNode *)parent inheritedList:(NSArray<NSString *> *)inheritedList;
|
||||||
|
{
|
||||||
|
// abstract
|
||||||
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
CGPathRelease(_clipPath);
|
CGPathRelease(_clipPath);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
@property (nonatomic, assign) RNSVGCGFloatArray strokeDasharray;
|
@property (nonatomic, assign) RNSVGCGFloatArray strokeDasharray;
|
||||||
@property (nonatomic, assign) CGFloat strokeDashoffset;
|
@property (nonatomic, assign) CGFloat strokeDashoffset;
|
||||||
@property (nonatomic, assign) CGMutablePathRef hitArea;
|
@property (nonatomic, assign) CGMutablePathRef hitArea;
|
||||||
|
@property (nonatomic, copy) NSArray<NSString *> *propList;
|
||||||
|
|
||||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
|
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,15 @@
|
|||||||
_strokeMiterlimit = strokeMiterlimit;
|
_strokeMiterlimit = strokeMiterlimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setPropList:(NSArray<NSString *> *)propList
|
||||||
|
{
|
||||||
|
if (propList == _propList) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_propList = propList;
|
||||||
|
[self invalidate];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
CGPathRelease(_hitArea);
|
CGPathRelease(_hitArea);
|
||||||
@@ -131,16 +140,27 @@
|
|||||||
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList
|
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList
|
||||||
{
|
{
|
||||||
|
|
||||||
|
[self mergeProperties:target mergeList:mergeList inherited:NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)mergeProperties:(__kindof RNSVGNode *)target mergeList:(NSArray<NSString *> *)mergeList inherited:(BOOL)inherited
|
||||||
|
{
|
||||||
if (mergeList.count == 0) {
|
if (mergeList.count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
originProperties = [[NSMutableDictionary alloc] init];
|
if (!inherited) {
|
||||||
|
originProperties = [[NSMutableDictionary alloc] init];
|
||||||
|
changedList = mergeList;
|
||||||
|
}
|
||||||
|
|
||||||
changedList = mergeList;
|
|
||||||
for (NSString *key in mergeList) {
|
for (NSString *key in mergeList) {
|
||||||
[originProperties setValue:[self valueForKey:key] forKey:key];
|
if (inherited) {
|
||||||
[self setValue:[target valueForKey:key] forKey:key];
|
[self inheritProperty:target propName:key];
|
||||||
|
} else {
|
||||||
|
[originProperties setValue:[self valueForKey:key] forKey:key];
|
||||||
|
[self setValue:[target valueForKey:key] forKey:key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +175,23 @@
|
|||||||
changedList = nil;
|
changedList = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)inheritProperty:(__kindof RNSVGNode *)parent propName:(NSString *)propName
|
||||||
|
{
|
||||||
|
if (![self.propList containsObject:propName]) {
|
||||||
|
// add prop to propList
|
||||||
|
NSMutableArray *copy = [self.propList mutableCopy];
|
||||||
|
[copy addObject:propName];
|
||||||
|
self.propList = [copy copy];
|
||||||
|
|
||||||
|
[self setValue:[parent valueForKey:propName] forKey:propName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)inheritProperties:(__kindof RNSVGNode *)parent inheritedList:(NSArray<NSString *> *)inheritedList
|
||||||
|
{
|
||||||
|
[self mergeProperties:parent mergeList:inheritedList inherited:YES];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)renderLayerTo:(CGContextRef)context
|
- (void)renderLayerTo:(CGContextRef)context
|
||||||
{
|
{
|
||||||
// abstract
|
// abstract
|
||||||
|
|||||||
17
ios/Text/RNSVGBezierPath.h
Normal file
17
ios/Text/RNSVGBezierPath.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 <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface RNSVGBezierPath : NSObject
|
||||||
|
|
||||||
|
- (instancetype)initWithBezierCurves:(NSArray *)bezierCurves;
|
||||||
|
- (CGAffineTransform)transformAtDistance:(CGFloat)distance;
|
||||||
|
|
||||||
|
@end
|
||||||
147
ios/Text/RNSVGBezierPath.m
Normal file
147
ios/Text/RNSVGBezierPath.m
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* based on
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RNSVGBezierPath.h"
|
||||||
|
#import <QuartzCore/QuartzCore.h>
|
||||||
|
#import <CoreText/CoreText.h>
|
||||||
|
|
||||||
|
@implementation RNSVGBezierPath
|
||||||
|
{
|
||||||
|
NSArray<NSArray *> *_bezierCurves;
|
||||||
|
NSUInteger _bezierIndex;
|
||||||
|
CGFloat _offset;
|
||||||
|
CGFloat _lastX;
|
||||||
|
CGFloat _lastDistance;
|
||||||
|
CGPoint _lastPoint;
|
||||||
|
CGPoint _P0;
|
||||||
|
CGPoint _P1;
|
||||||
|
CGPoint _P2;
|
||||||
|
CGPoint _P3;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithBezierCurves:(NSArray *)bezierCurves
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
_bezierCurves = bezierCurves;
|
||||||
|
_bezierIndex = 0;
|
||||||
|
_offset = 0;
|
||||||
|
_lastX = 0;
|
||||||
|
_lastDistance = 0;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (instancetype)initWithControlPoints:(CGPoint)P0 P1:(CGPoint)P1 P2:(CGPoint)P2 P3:(CGPoint)P3
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
_P0 = P0;
|
||||||
|
_P1 = P1;
|
||||||
|
_P2 = P2;
|
||||||
|
_P3 = P3;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CGFloat Bezier(CGFloat t, CGFloat P0, CGFloat P1, CGFloat P2, CGFloat P3) {
|
||||||
|
return (1-t)*(1-t)*(1-t)*P0+3*(1-t)*(1-t)*t*P1+3*(1-t)*t*t*P2+t*t*t*P3;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGPoint)pointForOffset:(CGFloat)t {
|
||||||
|
CGFloat x = Bezier(t, _P0.x, _P1.x, _P2.x, _P3.x);
|
||||||
|
CGFloat y = Bezier(t, _P0.y, _P1.y, _P2.y, _P3.y);
|
||||||
|
return CGPointMake(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CGFloat BezierPrime(CGFloat t, CGFloat P0, CGFloat P1, CGFloat P2, CGFloat P3) {
|
||||||
|
return -3*(1-t)*(1-t)*P0+(3*(1-t)*(1-t)*P1)-(6*t*(1-t)*P1)-(3*t*t*P2)+(6*t*(1-t)*P2)+3*t*t*P3;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGFloat)angleForOffset:(CGFloat)t {
|
||||||
|
CGFloat dx = BezierPrime(t, _P0.x, _P1.x, _P2.x, _P3.x);
|
||||||
|
CGFloat dy = BezierPrime(t, _P0.y, _P1.y, _P2.y, _P3.y);
|
||||||
|
return atan2(dy, dx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CGFloat Distance(CGPoint a, CGPoint b) {
|
||||||
|
CGFloat dx = a.x - b.x;
|
||||||
|
CGFloat dy = a.y - b.y;
|
||||||
|
return hypot(dx, dy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simplistic routine to find the offset along Bezier that is
|
||||||
|
// `distance` away from `point`. `offset` is the offset used to
|
||||||
|
// generate `point`, and saves us the trouble of recalculating it
|
||||||
|
// This routine just walks forward until it finds a point at least
|
||||||
|
// `distance` away. Good optimizations here would reduce the number
|
||||||
|
// of guesses, but this is tricky since if we go too far out, the
|
||||||
|
// curve might loop back on leading to incorrect results. Tuning
|
||||||
|
// kStep is good start.
|
||||||
|
- (CGFloat)offsetAtDistance:(CGFloat)distance
|
||||||
|
fromPoint:(CGPoint)point
|
||||||
|
offset:(CGFloat)offset {
|
||||||
|
const CGFloat kStep = 0.0005; // 0.0001 - 0.001 work well
|
||||||
|
CGFloat newDistance = 0;
|
||||||
|
CGFloat newOffset = offset + kStep;
|
||||||
|
while (newDistance <= distance && newOffset < 1.0) {
|
||||||
|
newOffset += kStep;
|
||||||
|
newDistance = Distance(point, [self pointForOffset:newOffset]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastDistance = newDistance;
|
||||||
|
return newOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setControlPoints
|
||||||
|
{
|
||||||
|
NSArray *bezier = [_bezierCurves objectAtIndex:_bezierIndex];
|
||||||
|
_bezierIndex++;
|
||||||
|
if (bezier.count == 1) {
|
||||||
|
_lastPoint = _P0 = [(NSValue *)[bezier objectAtIndex:0] CGPointValue];
|
||||||
|
[self setControlPoints];
|
||||||
|
} else if (bezier.count == 3) {
|
||||||
|
_P1 = [(NSValue *)[bezier objectAtIndex:0] CGPointValue];
|
||||||
|
_P2 = [(NSValue *)[bezier objectAtIndex:1] CGPointValue];
|
||||||
|
_P3 = [(NSValue *)[bezier objectAtIndex:2] CGPointValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (CGAffineTransform)transformAtDistance:(CGFloat)distance
|
||||||
|
{
|
||||||
|
if (_offset == 0) {
|
||||||
|
[self setControlPoints];
|
||||||
|
}
|
||||||
|
|
||||||
|
CGFloat offset = [self offsetAtDistance:distance - _lastX
|
||||||
|
fromPoint:_lastPoint
|
||||||
|
offset:_offset];
|
||||||
|
CGPoint glyphPoint = [self pointForOffset:offset];
|
||||||
|
CGFloat angle = [self angleForOffset:offset];
|
||||||
|
|
||||||
|
if (offset < 1) {
|
||||||
|
_offset = offset;
|
||||||
|
_lastPoint = glyphPoint;
|
||||||
|
} else {
|
||||||
|
_offset = 0;
|
||||||
|
_lastPoint = _P0 = _P3;
|
||||||
|
_lastX += _lastDistance;
|
||||||
|
return [self transformAtDistance:distance];
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastX = distance;
|
||||||
|
CGAffineTransform transform = CGAffineTransformMakeTranslation(glyphPoint.x, glyphPoint.y);
|
||||||
|
transform = CGAffineTransformRotate(transform, angle);
|
||||||
|
|
||||||
|
return transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -7,7 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import "UIBezierPath-Points.h"
|
|
||||||
#import "RNSVGPath.h"
|
#import "RNSVGPath.h"
|
||||||
#import "RNSVGTextFrame.h"
|
#import "RNSVGTextFrame.h"
|
||||||
|
|
||||||
@@ -15,6 +14,6 @@
|
|||||||
|
|
||||||
@property (nonatomic, assign) CTTextAlignment alignment;
|
@property (nonatomic, assign) CTTextAlignment alignment;
|
||||||
@property (nonatomic, assign) RNSVGTextFrame textFrame;
|
@property (nonatomic, assign) RNSVGTextFrame textFrame;
|
||||||
@property (nonatomic, assign) CGPathRef path;
|
@property (nonatomic, copy) NSArray<NSArray *> *path;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import "RNSVGText.h"
|
#import "RNSVGText.h"
|
||||||
|
#import "RNSVGBezierPath.h"
|
||||||
#import <CoreText/CoreText.h>
|
#import <CoreText/CoreText.h>
|
||||||
|
|
||||||
@implementation RNSVGText
|
@implementation RNSVGText
|
||||||
@@ -39,19 +39,17 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
|
|||||||
_textFrame = frame;
|
_textFrame = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setPath:(CGPathRef)path
|
- (void)setPath:(NSArray *)path
|
||||||
{
|
{
|
||||||
if (path == _path) {
|
if (path == _path) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
[self invalidate];
|
[self invalidate];
|
||||||
CGPathRelease(_path);
|
_path = path;
|
||||||
_path = CGPathRetain(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
CGPathRelease(_path);
|
|
||||||
RNSVGFreeTextFrame(_textFrame);
|
RNSVGFreeTextFrame(_textFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,59 +93,41 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
|
|||||||
{
|
{
|
||||||
CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0);
|
CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0);
|
||||||
CGMutablePathRef path = CGPathCreateMutable();
|
CGMutablePathRef path = CGPathCreateMutable();
|
||||||
CTLineGetGlyphRuns(line);
|
|
||||||
CFArrayRef glyphRuns = CTLineGetGlyphRuns(line);
|
CFArrayRef glyphRuns = CTLineGetGlyphRuns(line);
|
||||||
CFIndex runCount = CFArrayGetCount(glyphRuns);
|
CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, 0);
|
||||||
CFIndex glyphIndex = 0;
|
|
||||||
|
|
||||||
for(CFIndex i = 0; i < runCount; ++i) {
|
CFIndex runGlyphCount = CTRunGetGlyphCount(run);
|
||||||
// For each run, we need to get the glyphs, their font (to get the path) and their locations.
|
CGPoint positions[runGlyphCount];
|
||||||
CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, i);
|
CGGlyph glyphs[runGlyphCount];
|
||||||
CFIndex runGlyphCount = CTRunGetGlyphCount(run);
|
|
||||||
CGPoint positions[runGlyphCount];
|
|
||||||
CGGlyph glyphs[runGlyphCount];
|
|
||||||
|
|
||||||
// Grab the glyphs, positions, and font
|
// Grab the glyphs, positions, and font
|
||||||
CTRunGetPositions(run, CFRangeMake(0, 0), positions);
|
CTRunGetPositions(run, CFRangeMake(0, 0), positions);
|
||||||
CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs);
|
CTRunGetGlyphs(run, CFRangeMake(0, 0), glyphs);
|
||||||
CFDictionaryRef attributes = CTRunGetAttributes(run);
|
CFDictionaryRef attributes = CTRunGetAttributes(run);
|
||||||
CTFontRef runFont = CFDictionaryGetValue(attributes, kCTFontAttributeName);
|
CTFontRef runFont = CFDictionaryGetValue(attributes, kCTFontAttributeName);
|
||||||
|
|
||||||
for(CFIndex j = 0; j < runGlyphCount; ++j, ++glyphIndex) {
|
RNSVGBezierPath *bezierPath = [[RNSVGBezierPath alloc] initWithBezierCurves:self.path];
|
||||||
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyphs[j], nil);
|
|
||||||
CGPoint point = positions[j];
|
|
||||||
|
|
||||||
if (letter) {
|
for(CFIndex i = 0; i < runGlyphCount; ++i) {
|
||||||
CGAffineTransform transform;
|
CGPathRef letter = CTFontCreatePathForGlyph(runFont, glyphs[i], nil);
|
||||||
|
CGPoint point = positions[i];
|
||||||
|
|
||||||
// draw glyphs along path
|
if (letter) {
|
||||||
if (self.path) {
|
CGAffineTransform transform;
|
||||||
CGPoint slope;
|
|
||||||
CGRect bounding = CGPathGetBoundingBox(letter);
|
|
||||||
UIBezierPath* pathAlong = [UIBezierPath bezierPathWithCGPath:self.path];
|
|
||||||
CGFloat percentConsumed = (point.x + bounding.size.width) / pathAlong.length;
|
|
||||||
if (percentConsumed >= 1.0f) {
|
|
||||||
CGPathRelease(letter);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
CGPoint targetPoint = [pathAlong pointAtPercent:percentConsumed withSlope: &slope];
|
// draw glyphs along path
|
||||||
float angle = atan(slope.y / slope.x); // + M_PI;
|
if (self.path) {
|
||||||
if (slope.x < 0) {
|
transform = [bezierPath transformAtDistance:point.x];
|
||||||
angle += M_PI; // going left, update the angle
|
transform = CGAffineTransformScale(transform, 1.0, -1.0);
|
||||||
}
|
} else {
|
||||||
transform = CGAffineTransformMakeTranslation(targetPoint.x - bounding.size.width, targetPoint.y);
|
transform = CGAffineTransformTranslate(upsideDown, point.x, point.y);
|
||||||
transform = CGAffineTransformRotate(transform, angle);
|
|
||||||
transform = CGAffineTransformScale(transform, 1.0, -1.0);
|
|
||||||
} else {
|
|
||||||
transform = CGAffineTransformTranslate(upsideDown, point.x, point.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
CGPathAddPath(path, &transform, letter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CGPathRelease(letter);
|
CGPathAddPath(path, &transform, letter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CGPathRelease(letter);
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Erica Sadun, http://ericasadun.com
|
|
||||||
iPhone Developer's Cookbook, 6.x Edition
|
|
||||||
BSD License, Use at your own risk
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
@interface UIBezierPath (Points)
|
|
||||||
@property (nonatomic, readonly) NSArray *points;
|
|
||||||
@property (nonatomic, readonly) NSArray *bezierElements;
|
|
||||||
@property (nonatomic, readonly) CGFloat length;
|
|
||||||
|
|
||||||
- (NSArray *) pointPercentArray;
|
|
||||||
- (CGPoint) pointAtPercent: (CGFloat) percent withSlope: (CGPoint *) slope;
|
|
||||||
+ (UIBezierPath *) pathWithPoints: (NSArray *) points;
|
|
||||||
+ (UIBezierPath *) pathWithElements: (NSArray *) elements;
|
|
||||||
@end
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Erica Sadun, http://ericasadun.com
|
|
||||||
iPhone Developer's Cookbook, 6.x Edition
|
|
||||||
BSD License, Use at your own risk
|
|
||||||
*/
|
|
||||||
|
|
||||||
#import "UIBezierPath-Points.h"
|
|
||||||
|
|
||||||
#define POINTSTRING(_CGPOINT_) (NSStringFromCGPoint(_CGPOINT_))
|
|
||||||
#define VALUE(_INDEX_) [NSValue valueWithCGPoint:points[_INDEX_]]
|
|
||||||
#define POINT(_INDEX_) [(NSValue *)[points objectAtIndex:_INDEX_] CGPointValue]
|
|
||||||
|
|
||||||
// Return distance between two points
|
|
||||||
static float distance (CGPoint p1, CGPoint p2)
|
|
||||||
{
|
|
||||||
float dx = p2.x - p1.x;
|
|
||||||
float dy = p2.y - p1.y;
|
|
||||||
|
|
||||||
return sqrt(dx*dx + dy*dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation UIBezierPath (Points)
|
|
||||||
void getPointsFromBezier(void *info, const CGPathElement *element)
|
|
||||||
{
|
|
||||||
NSMutableArray *bezierPoints = (__bridge NSMutableArray *)info;
|
|
||||||
CGPathElementType type = element->type;
|
|
||||||
CGPoint *points = element->points;
|
|
||||||
if (type != kCGPathElementCloseSubpath)
|
|
||||||
{
|
|
||||||
if ((type == kCGPathElementAddLineToPoint) ||
|
|
||||||
(type == kCGPathElementMoveToPoint))
|
|
||||||
[bezierPoints addObject:VALUE(0)];
|
|
||||||
else if (type == kCGPathElementAddQuadCurveToPoint)
|
|
||||||
[bezierPoints addObject:VALUE(1)];
|
|
||||||
else if (type == kCGPathElementAddCurveToPoint)
|
|
||||||
[bezierPoints addObject:VALUE(2)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSArray *)points
|
|
||||||
{
|
|
||||||
NSMutableArray *points = [NSMutableArray array];
|
|
||||||
CGPathApply(self.CGPath, (__bridge void *)points, getPointsFromBezier);
|
|
||||||
return points;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a Bezier path buit with the supplied points
|
|
||||||
+ (UIBezierPath *) pathWithPoints: (NSArray *) points
|
|
||||||
{
|
|
||||||
UIBezierPath *path = [UIBezierPath bezierPath];
|
|
||||||
if (points.count == 0) return path;
|
|
||||||
[path moveToPoint:POINT(0)];
|
|
||||||
for (int i = 1; i < points.count; i++)
|
|
||||||
[path addLineToPoint:POINT(i)];
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat) length
|
|
||||||
{
|
|
||||||
NSArray *points = self.points;
|
|
||||||
float totalPointLength = 0.0f;
|
|
||||||
for (int i = 1; i < points.count; i++)
|
|
||||||
totalPointLength += distance(POINT(i), POINT(i-1));
|
|
||||||
return totalPointLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSArray *) pointPercentArray
|
|
||||||
{
|
|
||||||
// Use total length to calculate the percent of path consumed at each control point
|
|
||||||
NSArray *points = self.points;
|
|
||||||
NSUInteger pointCount = points.count;
|
|
||||||
|
|
||||||
float totalPointLength = self.length;
|
|
||||||
float distanceTravelled = 0.0f;
|
|
||||||
|
|
||||||
NSMutableArray *pointPercentArray = [NSMutableArray array];
|
|
||||||
[pointPercentArray addObject:@(0.0)];
|
|
||||||
|
|
||||||
for (int i = 1; i < pointCount; i++)
|
|
||||||
{
|
|
||||||
distanceTravelled += distance(POINT(i), POINT(i-1));
|
|
||||||
[pointPercentArray addObject:@(distanceTravelled / totalPointLength)];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a final item just to stop with. Probably not needed.
|
|
||||||
[pointPercentArray addObject:[NSNumber numberWithFloat:1.1f]]; // 110%
|
|
||||||
|
|
||||||
return pointPercentArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGPoint) pointAtPercent: (CGFloat) percent withSlope: (CGPoint *) slope
|
|
||||||
{
|
|
||||||
NSArray *points = self.points;
|
|
||||||
NSArray *percentArray = self.pointPercentArray;
|
|
||||||
CFIndex lastPointIndex = points.count - 1;
|
|
||||||
|
|
||||||
if (!points.count) {
|
|
||||||
return CGPointZero;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for 0% and 100%
|
|
||||||
if (percent <= 0.0f) {
|
|
||||||
return POINT(0);
|
|
||||||
}
|
|
||||||
if (percent >= 1.0f) {
|
|
||||||
return POINT(lastPointIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find a corresponding pair of points in the path
|
|
||||||
CFIndex index = 1;
|
|
||||||
while ((index < percentArray.count) &&
|
|
||||||
(percent > ((NSNumber *)percentArray[index]).floatValue)) {
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This should not happen.
|
|
||||||
if (index > lastPointIndex) {
|
|
||||||
return POINT(lastPointIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the intermediate distance between the two points
|
|
||||||
CGPoint point1 = POINT(index -1);
|
|
||||||
CGPoint point2 = POINT(index);
|
|
||||||
|
|
||||||
float percent1 = [[percentArray objectAtIndex:index - 1] floatValue];
|
|
||||||
float percent2 = [[percentArray objectAtIndex:index] floatValue];
|
|
||||||
float percentOffset = (percent - percent1) / (percent2 - percent1);
|
|
||||||
|
|
||||||
float dx = point2.x - point1.x;
|
|
||||||
float dy = point2.y - point1.y;
|
|
||||||
|
|
||||||
// Store dy, dx for retrieving arctan
|
|
||||||
if (slope) {
|
|
||||||
*slope = CGPointMake(dx, dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate new point
|
|
||||||
CGFloat newX = point1.x + (percentOffset * dx);
|
|
||||||
CGFloat newY = point1.y + (percentOffset * dy);
|
|
||||||
CGPoint targetPoint = CGPointMake(newX, newY);
|
|
||||||
|
|
||||||
return targetPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
void getBezierElements(void *info, const CGPathElement *element)
|
|
||||||
{
|
|
||||||
NSMutableArray *bezierElements = (__bridge NSMutableArray *)info;
|
|
||||||
CGPathElementType type = element->type;
|
|
||||||
CGPoint *points = element->points;
|
|
||||||
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case kCGPathElementCloseSubpath:
|
|
||||||
[bezierElements addObject:@[@(type)]];
|
|
||||||
break;
|
|
||||||
case kCGPathElementMoveToPoint:
|
|
||||||
case kCGPathElementAddLineToPoint:
|
|
||||||
[bezierElements addObject:@[@(type), VALUE(0)]];
|
|
||||||
break;
|
|
||||||
case kCGPathElementAddQuadCurveToPoint:
|
|
||||||
[bezierElements addObject:@[@(type), VALUE(0), VALUE(1)]];
|
|
||||||
break;
|
|
||||||
case kCGPathElementAddCurveToPoint:
|
|
||||||
[bezierElements addObject:@[@(type), VALUE(0), VALUE(1), VALUE(2)]];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSArray *) bezierElements
|
|
||||||
{
|
|
||||||
NSMutableArray *elements = [NSMutableArray array];
|
|
||||||
CGPathApply(self.CGPath, (__bridge void *)elements, getBezierElements);
|
|
||||||
return elements;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (UIBezierPath *) pathWithElements: (NSArray *) elements
|
|
||||||
{
|
|
||||||
UIBezierPath *path = [UIBezierPath bezierPath];
|
|
||||||
if (elements.count == 0) return path;
|
|
||||||
|
|
||||||
for (NSArray *points in elements)
|
|
||||||
{
|
|
||||||
if (!points.count) continue;
|
|
||||||
CGPathElementType elementType = [points[0] integerValue];
|
|
||||||
switch (elementType)
|
|
||||||
{
|
|
||||||
case kCGPathElementCloseSubpath:
|
|
||||||
[path closePath];
|
|
||||||
break;
|
|
||||||
case kCGPathElementMoveToPoint:
|
|
||||||
if (points.count == 2)
|
|
||||||
[path moveToPoint:POINT(1)];
|
|
||||||
break;
|
|
||||||
case kCGPathElementAddLineToPoint:
|
|
||||||
if (points.count == 2)
|
|
||||||
[path addLineToPoint:POINT(1)];
|
|
||||||
break;
|
|
||||||
case kCGPathElementAddQuadCurveToPoint:
|
|
||||||
if (points.count == 3)
|
|
||||||
[path addQuadCurveToPoint:POINT(2) controlPoint:POINT(1)];
|
|
||||||
break;
|
|
||||||
case kCGPathElementAddCurveToPoint:
|
|
||||||
if (points.count == 4)
|
|
||||||
[path addCurveToPoint:POINT(3) controlPoint1:POINT(1) controlPoint2:POINT(2)];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
+ (RNSVGCGFloatArray)RNSVGCGFloatArray:(id)json;
|
+ (RNSVGCGFloatArray)RNSVGCGFloatArray:(id)json;
|
||||||
+ (RNSVGBrush *)RNSVGBrush:(id)json;
|
+ (RNSVGBrush *)RNSVGBrush:(id)json;
|
||||||
|
|
||||||
|
+ (NSArray *)RNSVGBezier:(id)json;
|
||||||
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset;
|
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset;
|
||||||
+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset;
|
+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset;
|
||||||
+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset;
|
+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset;
|
||||||
|
|||||||
@@ -44,9 +44,6 @@
|
|||||||
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:
|
|
||||||
CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0);
|
|
||||||
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);
|
||||||
@@ -163,6 +160,63 @@ RCT_ENUM_CONVERTER(RNSVGCGFCRule, (@{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (NSArray *)RNSVGBezier:(id)json
|
||||||
|
{
|
||||||
|
NSArray *arr = [self NSNumberArray:json];
|
||||||
|
|
||||||
|
NSMutableArray<NSArray *> *beziers = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
|
NSUInteger count = [arr count];
|
||||||
|
|
||||||
|
#define NEXT_VALUE [self double:arr[i++]]
|
||||||
|
@try {
|
||||||
|
NSValue *startPoint = [NSValue valueWithCGPoint: CGPointMake(0, 0)];
|
||||||
|
NSUInteger i = 0;
|
||||||
|
while (i < count) {
|
||||||
|
NSUInteger type = [arr[i++] unsignedIntegerValue];
|
||||||
|
switch (type) {
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
startPoint = [NSValue valueWithCGPoint: CGPointMake(NEXT_VALUE, NEXT_VALUE)];
|
||||||
|
[beziers addObject: @[startPoint]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
[beziers addObject: @[]];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
double x = NEXT_VALUE;
|
||||||
|
double y = NEXT_VALUE;
|
||||||
|
NSValue * destination = [NSValue valueWithCGPoint:CGPointMake(x, y)];
|
||||||
|
[beziers addObject: @[
|
||||||
|
destination,
|
||||||
|
startPoint,
|
||||||
|
destination
|
||||||
|
]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
[beziers addObject: @[
|
||||||
|
[NSValue valueWithCGPoint:CGPointMake(NEXT_VALUE, NEXT_VALUE)],
|
||||||
|
[NSValue valueWithCGPoint:CGPointMake(NEXT_VALUE, NEXT_VALUE)],
|
||||||
|
[NSValue valueWithCGPoint:CGPointMake(NEXT_VALUE, NEXT_VALUE)],
|
||||||
|
]];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
RCTLogError(@"Invalid RNSVGBezier type %zd at element %zd of %@", type, i, arr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@catch (NSException *exception) {
|
||||||
|
RCTLogError(@"Invalid RNSVGBezier format: %@", arr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return beziers;
|
||||||
|
}
|
||||||
|
|
||||||
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset
|
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset
|
||||||
{
|
{
|
||||||
NSArray *arr = [self NSArray:json];
|
NSArray *arr = [self NSArray:json];
|
||||||
|
|||||||
@@ -19,6 +19,4 @@ RCT_EXPORT_MODULE()
|
|||||||
return [RNSVGGroup new];
|
return [RNSVGGroup new];
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(mergeList, NSArray<NSString *>)
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -31,5 +31,6 @@ RCT_EXPORT_VIEW_PROPERTY(strokeLinejoin, CGLineJoin)
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(strokeDasharray, RNSVGCGFloatArray)
|
RCT_EXPORT_VIEW_PROPERTY(strokeDasharray, RNSVGCGFloatArray)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(strokeDashoffset, CGFloat)
|
RCT_EXPORT_VIEW_PROPERTY(strokeDashoffset, CGFloat)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(strokeMiterlimit, CGFloat)
|
RCT_EXPORT_VIEW_PROPERTY(strokeMiterlimit, CGFloat)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(propList, NSArray<NSString *>)
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -22,6 +22,6 @@ RCT_EXPORT_MODULE()
|
|||||||
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(alignment, CTTextAlignment)
|
RCT_EXPORT_VIEW_PROPERTY(alignment, CTTextAlignment)
|
||||||
RCT_REMAP_VIEW_PROPERTY(frame, textFrame, RNSVGTextFrame)
|
RCT_REMAP_VIEW_PROPERTY(frame, textFrame, RNSVGTextFrame)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(path, CGPath)
|
RCT_EXPORT_VIEW_PROPERTY(path, RNSVGBezier)
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -19,6 +19,5 @@ RCT_EXPORT_MODULE()
|
|||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(href, NSString)
|
RCT_EXPORT_VIEW_PROPERTY(href, NSString)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(mergeList, NSArray<NSString *>)
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default class SerializablePath {
|
|||||||
case 's': this.curve(p[i++], p[i++], null, null, p[i++], p[i++]); break;
|
case 's': this.curve(p[i++], p[i++], null, null, p[i++], p[i++]); break;
|
||||||
case 'q': this.curve(p[i++], p[i++], p[i++], p[i++]); break;
|
case 'q': this.curve(p[i++], p[i++], p[i++], p[i++]); break;
|
||||||
case 't': this.curve(p[i++], p[i++]); break;
|
case 't': this.curve(p[i++], p[i++]); break;
|
||||||
case 'a': this.arc(p[i + 5], p[i + 6], p[i], p[i + 1], p[i + 3], !+p[i + 4], p[i + 2]); i += 7; break;
|
case 'a': this.arc(p[i + 5], p[i + 6], p[i], p[i + 1], p[i + 3], !+p[i + 4], +p[i + 2]); i += 7; break;
|
||||||
case 'h': this.line(p[i++], 0); break;
|
case 'h': this.line(p[i++], 0); break;
|
||||||
case 'v': this.line(0, p[i++]); break;
|
case 'v': this.line(0, p[i++]); break;
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ export default class SerializablePath {
|
|||||||
case 'S': this.curveTo(p[i++], p[i++], null, null, p[i++], p[i++]); break;
|
case 'S': this.curveTo(p[i++], p[i++], null, null, p[i++], p[i++]); break;
|
||||||
case 'Q': this.curveTo(p[i++], p[i++], p[i++], p[i++]); break;
|
case 'Q': this.curveTo(p[i++], p[i++], p[i++], p[i++]); break;
|
||||||
case 'T': this.curveTo(p[i++], p[i++]); break;
|
case 'T': this.curveTo(p[i++], p[i++]); break;
|
||||||
case 'A': this.arcTo(p[i + 5], p[i + 6], p[i], p[i + 1], p[i + 3], !+p[i + 4], p[i + 2]); i += 7; break;
|
case 'A': this.arcTo(p[i + 5], p[i + 6], p[i], p[i + 1], p[i + 3], !+p[i + 4], +p[i + 2]); i += 7; break;
|
||||||
case 'H': this.lineTo(p[i++], this.penY); break;
|
case 'H': this.lineTo(p[i++], this.penY); break;
|
||||||
case 'V': this.lineTo(this.penX, p[i++]); break;
|
case 'V': this.lineTo(this.penX, p[i++]); break;
|
||||||
|
|
||||||
@@ -276,12 +276,9 @@ export default class SerializablePath {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onArc = (sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation) => {
|
onArc = (sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation) => {
|
||||||
if (rx !== ry || rotation) {
|
return this._arcToBezier(
|
||||||
return this._arcToBezier(
|
sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation
|
||||||
sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
this.path.push(ARC, cx, cy, rx, sa, ea, ccw ? 0 : 1);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
onClose = () => {
|
onClose = () => {
|
||||||
|
|||||||
@@ -52,6 +52,9 @@ const NodeAttributes = {
|
|||||||
clipPath: {
|
clipPath: {
|
||||||
diff: arrayDiffer
|
diff: arrayDiffer
|
||||||
},
|
},
|
||||||
|
propList: {
|
||||||
|
diff: arrayDiffer
|
||||||
|
},
|
||||||
responsible: true
|
responsible: true
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -77,16 +80,9 @@ const RenderableOnlyAttributes = {
|
|||||||
|
|
||||||
const RenderableAttributes = merge({}, NodeAttributes, RenderableOnlyAttributes);
|
const RenderableAttributes = merge({}, NodeAttributes, RenderableOnlyAttributes);
|
||||||
|
|
||||||
const GroupAttributes = merge({
|
const GroupAttributes = RenderableAttributes;
|
||||||
mergeList: {
|
|
||||||
diff: arrayDiffer
|
|
||||||
}
|
|
||||||
}, NodeAttributes);
|
|
||||||
|
|
||||||
const UseAttributes = merge({
|
const UseAttributes = merge({
|
||||||
mergeList: {
|
|
||||||
diff: arrayDiffer
|
|
||||||
},
|
|
||||||
href: true
|
href: true
|
||||||
}, RenderableAttributes);
|
}, RenderableAttributes);
|
||||||
|
|
||||||
|
|||||||
@@ -3,20 +3,22 @@ import _ from 'lodash';
|
|||||||
import patternReg from './patternReg';
|
import patternReg from './patternReg';
|
||||||
|
|
||||||
export default function (colorOrBrush) {
|
export default function (colorOrBrush) {
|
||||||
if (colorOrBrush === 'none') {
|
if (colorOrBrush === 'none' || _.isNil(colorOrBrush)) {
|
||||||
return null;
|
return null;
|
||||||
} else if (_.isNil(colorOrBrush)) {
|
|
||||||
colorOrBrush = '#000';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let matched = colorOrBrush.match(patternReg);
|
try {
|
||||||
|
let matched = colorOrBrush.match(patternReg);
|
||||||
// brush
|
// brush
|
||||||
if (matched) {
|
if (matched) {
|
||||||
return [1, matched[1]];
|
return [1, matched[1]];
|
||||||
//todo:
|
//todo:
|
||||||
} else { // solid color
|
} else { // solid color
|
||||||
let c = new Color(colorOrBrush).rgbaArray();
|
let c = new Color(colorOrBrush).rgbaArray();
|
||||||
return [0, c[0] / 255, c[1] / 255, c[2] / 255, c[3]];
|
return [0, c[0] / 255, c[1] / 255, c[2] / 255, c[3]];
|
||||||
|
}
|
||||||
|
} catch(err) {
|
||||||
|
console.warn(`"${colorOrBrush}" is not a valid color or brush`);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import extractBrush from './extractBrush';
|
import extractBrush from './extractBrush';
|
||||||
import extractOpacity from './extractOpacity';
|
import extractOpacity from './extractOpacity';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
const fillRules = {
|
const fillRules = {
|
||||||
evenodd: 0,
|
evenodd: 0,
|
||||||
@@ -8,7 +9,8 @@ const fillRules = {
|
|||||||
|
|
||||||
export default function(props) {
|
export default function(props) {
|
||||||
return {
|
return {
|
||||||
fill: extractBrush(props.fill),
|
// default fill is black
|
||||||
|
fill: extractBrush(_.isNil(props.fill) ? '#000' : props.fill),
|
||||||
fillOpacity: extractOpacity(props.fillOpacity),
|
fillOpacity: extractOpacity(props.fillOpacity),
|
||||||
fillRule: fillRules[props.fillRule] === 0 ? 0 : 1
|
fillRule: fillRules[props.fillRule] === 0 ? 0 : 1
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,11 +4,29 @@ import extractTransform from './extractTransform';
|
|||||||
import extractClipping from './extractClipping';
|
import extractClipping from './extractClipping';
|
||||||
import extractResponder from './extractResponder';
|
import extractResponder from './extractResponder';
|
||||||
import extractOpacity from './extractOpacity';
|
import extractOpacity from './extractOpacity';
|
||||||
|
import {RenderableOnlyAttributes} from '../attributes';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
export default function(props, options = {stroke: true, transform: true, fill: true, responder: true}) {
|
export default function(props, options = {stroke: true, transform: true, fill: true, responder: true}) {
|
||||||
|
let propList = [];
|
||||||
|
Object.keys(RenderableOnlyAttributes).forEach(name => {
|
||||||
|
if (!_.isNil(props[name])) {
|
||||||
|
// clipPath prop may provide `clipPathRef` as native prop
|
||||||
|
if (name === 'clipPath') {
|
||||||
|
if (extractedProps[name]) {
|
||||||
|
propList.push(name);
|
||||||
|
} else if (extractedProps.clipPathRef) {
|
||||||
|
propList.push('clipPathRef');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
propList.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let extractedProps = {
|
let extractedProps = {
|
||||||
opacity: extractOpacity(props.opacity)
|
opacity: extractOpacity(props.opacity),
|
||||||
|
propList
|
||||||
};
|
};
|
||||||
|
|
||||||
if (props.id) {
|
if (props.id) {
|
||||||
|
|||||||
@@ -17,16 +17,11 @@ const joins = {
|
|||||||
|
|
||||||
export default function(props) {
|
export default function(props) {
|
||||||
let {stroke} = props;
|
let {stroke} = props;
|
||||||
if (!stroke) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let strokeWidth = +props.strokeWidth;
|
let strokeWidth = +props.strokeWidth;
|
||||||
|
|
||||||
if (_.isNil(props.strokeWidth)) {
|
if (_.isNil(props.strokeWidth)) {
|
||||||
strokeWidth = 1;
|
strokeWidth = null;
|
||||||
} else if (!strokeWidth) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let strokeDasharray = props.strokeDasharray;
|
let strokeDasharray = props.strokeDasharray;
|
||||||
@@ -40,10 +35,6 @@ export default function(props) {
|
|||||||
strokeDasharray.push(strokeDasharray[0]);
|
strokeDasharray.push(strokeDasharray[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stroke) {
|
|
||||||
stroke = '#000';
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
stroke: extractBrush(stroke),
|
stroke: extractBrush(stroke),
|
||||||
strokeOpacity: extractOpacity(props.strokeOpacity),
|
strokeOpacity: extractOpacity(props.strokeOpacity),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import SerializablePath from 'react-native/Libraries/ART/ARTSerializablePath';
|
import SerializablePath from '../SerializablePath';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
const newLine = /\n/g;
|
const newLine = /\n/g;
|
||||||
const defaultFontFamily = '"Helvetica Neue", "Helvetica", Arial';
|
const defaultFontFamily = '"Helvetica Neue", "Helvetica", Arial';
|
||||||
@@ -82,6 +82,7 @@ const anchord = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function(props) {
|
export default function(props) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
alignment: anchord[props.textAnchor] || 0,
|
alignment: anchord[props.textAnchor] || 0,
|
||||||
frame: extractFontAndLines(
|
frame: extractFontAndLines(
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
import {RenderableOnlyAttributes} from '../lib/attributes';
|
|
||||||
import _ from 'lodash';
|
|
||||||
|
|
||||||
export default function (extractedProps, originProps) {
|
|
||||||
let reusableProps = [];
|
|
||||||
Object.keys(RenderableOnlyAttributes).forEach(name => {
|
|
||||||
if (!_.isNil(originProps[name])) {
|
|
||||||
// clipPath prop may provide `clipPathRef` as native prop
|
|
||||||
if (name === 'clipPath') {
|
|
||||||
if (extractedProps[name]) {
|
|
||||||
reusableProps.push(name);
|
|
||||||
} else if (extractedProps.clipPathRef) {
|
|
||||||
reusableProps.push('clipPathRef');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reusableProps.push(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return reusableProps;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user