mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-23 23:55:27 +00:00
refactor Use and Defs element with native code support(iOS)
This commit is contained in:
@@ -14,24 +14,9 @@ import * as Symbol from './examples/Symbol';
|
||||
import * as Gradients from './examples/Gradients';
|
||||
import * as Clipping from './examples/Clipping';
|
||||
import * as Image from './examples/Image';
|
||||
import * as Definations from './examples/Definations';
|
||||
import * as TouchEvents from './examples/TouchEvents';
|
||||
|
||||
export {
|
||||
Svg,
|
||||
Rect,
|
||||
Circle,
|
||||
Ellipse,
|
||||
Line,
|
||||
Polygon,
|
||||
Polyline,
|
||||
Path,
|
||||
Text,
|
||||
Stroking,
|
||||
G,
|
||||
Use,
|
||||
Symbol,
|
||||
Gradients,
|
||||
Clipping,
|
||||
Image,
|
||||
TouchEvents
|
||||
Definations
|
||||
};
|
||||
|
||||
51
Example/examples/Definations.js
Normal file
51
Example/examples/Definations.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import React, {
|
||||
Component
|
||||
} from 'react';
|
||||
|
||||
import Svg, {
|
||||
Defs,
|
||||
G,
|
||||
Path,
|
||||
Use,
|
||||
Rect
|
||||
} from 'react-native-svg';
|
||||
|
||||
class DefsExample extends Component{
|
||||
static title = 'basic Defs usage';
|
||||
|
||||
render() {
|
||||
return <Svg
|
||||
height="100"
|
||||
width="100"
|
||||
>
|
||||
<Defs>
|
||||
<G id="path" x="5" y="2">
|
||||
<Path id="test" fill='red' d="M38.459,1.66A0.884,0.884,0,0,1,39,2.5a0.7,0.7,0,0,1-.3.575L23.235,16.092,27.58,26.1a1.4,1.4,0,0,1,.148.3,1.3,1.3,0,0,1,0,.377,1.266,1.266,0,0,1-2.078.991L15.526,20.6l-7.58,4.35a1.255,1.255,0,0,1-.485,0,1.267,1.267,0,0,1-1.277-1.258q0-.01,0-0.02a1.429,1.429,0,0,1,0-.446C7.243,20.253,8.6,16.369,8.6,16.29L3.433,13.545A0.743,0.743,0,0,1,2.9,12.822a0.822,0.822,0,0,1,.623-0.773l8.164-2.972,3.018-8.5A0.822,0.822,0,0,1,15.427,0a0.752,0.752,0,0,1,.752.555l2.563,6.936S37.65,1.727,37.792,1.685A1.15,1.15,0,0,1,38.459,1.66Z"/>
|
||||
</G>
|
||||
</Defs>
|
||||
<Use href="url(#path)" x="0" fill="blue" />
|
||||
<Use href="url(#path)" x="10" fill="#3a8" />
|
||||
</Svg>;
|
||||
}
|
||||
}
|
||||
|
||||
const icon = <Svg
|
||||
height="20"
|
||||
width="20"
|
||||
>
|
||||
<Defs>
|
||||
<G id="path">
|
||||
<Path fill='red' d="M38.459,1.66A0.884,0.884,0,0,1,39,2.5a0.7,0.7,0,0,1-.3.575L23.235,16.092,27.58,26.1a1.4,1.4,0,0,1,.148.3,1.3,1.3,0,0,1,0,.377,1.266,1.266,0,0,1-2.078.991L15.526,20.6l-7.58,4.35a1.255,1.255,0,0,1-.485,0,1.267,1.267,0,0,1-1.277-1.258q0-.01,0-0.02a1.429,1.429,0,0,1,0-.446C7.243,20.253,8.6,16.369,8.6,16.29L3.433,13.545A0.743,0.743,0,0,1,2.9,12.822a0.822,0.822,0,0,1,.623-0.773l8.164-2.972,3.018-8.5A0.822,0.822,0,0,1,15.427,0a0.752,0.752,0,0,1,.752.555l2.563,6.936S37.65,1.727,37.792,1.685A1.15,1.15,0,0,1,38.459,1.66Z"/>
|
||||
</G>
|
||||
</Defs>
|
||||
<Use href="url(#path)" x="10" fill="#3a8" />
|
||||
</Svg>;
|
||||
|
||||
|
||||
|
||||
const samples = [DefsExample];
|
||||
|
||||
export {
|
||||
icon,
|
||||
samples
|
||||
};
|
||||
@@ -111,7 +111,8 @@ const styles = StyleSheet.create({
|
||||
}
|
||||
});
|
||||
|
||||
const names = ['Svg', 'Stroking', 'Path', 'Line', 'Rect', 'Polygon', 'Polyline', 'Circle', 'Ellipse', 'G', 'Text', 'Use', 'Symbol', 'Gradients', 'Clipping', 'Image', 'TouchEvents'];
|
||||
//const names = ['Svg', 'Stroking', 'Path', 'Line', 'Rect', 'Polygon', 'Polyline', 'Circle', 'Ellipse', 'G', 'Text', 'Use', 'Symbol', 'Gradients', 'Clipping', 'Image', 'TouchEvents'];
|
||||
const names = ['Definations'];
|
||||
|
||||
class SvgExample extends Component {
|
||||
constructor() {
|
||||
|
||||
@@ -15,9 +15,7 @@ class Circle extends Shape {
|
||||
static contextTypes = {
|
||||
...fillProps,
|
||||
...strokeProps,
|
||||
...circleProps,
|
||||
isInGroup: PropTypes.bool,
|
||||
svgId: numberProp
|
||||
...circleProps
|
||||
};
|
||||
|
||||
setNativeProps = (...args) => {
|
||||
|
||||
105
elements/Defs.js
105
elements/Defs.js
@@ -1,102 +1,19 @@
|
||||
import React, {Children, Component, cloneElement, PropTypes} from 'react';
|
||||
import {NativeGroup} from './G';
|
||||
let map = {};
|
||||
import React, {
|
||||
Component,
|
||||
} from 'react'
|
||||
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
|
||||
|
||||
import LinearGradient from './LinearGradient';
|
||||
import RadialGradient from './RadialGradient';
|
||||
import ClipPath from './ClipPath';
|
||||
let onlyChild = Children.only;
|
||||
|
||||
class DefsItem extends Component{
|
||||
static displayName = 'DefsItem';
|
||||
static propType = {
|
||||
visible: PropTypes.bool
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
visible: false
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.id = this.props.id + ':' + this.props.svgId;
|
||||
map[this.id] = cloneElement(onlyChild(this.props.children), {
|
||||
id: null
|
||||
});
|
||||
}
|
||||
|
||||
componentWillReceiveProps = nextProps => {
|
||||
let id = nextProps.id + ':' + nextProps.svgId;
|
||||
if (id !== this.id) {
|
||||
delete map[this.id];
|
||||
}
|
||||
map[id] = cloneElement(onlyChild(nextProps.children), {
|
||||
id: null
|
||||
});
|
||||
};
|
||||
|
||||
componentWillUnmount = () => {
|
||||
delete map[this.id];
|
||||
};
|
||||
|
||||
render() {
|
||||
return this.props.visible ? onlyChild(this.props.children) : <NativeGroup />;
|
||||
}
|
||||
}
|
||||
|
||||
let idReg = /^#(.+)/;
|
||||
class DefsUse extends Component{
|
||||
static displayName = 'DefsUse';
|
||||
static propType = {
|
||||
href: PropTypes.string
|
||||
};
|
||||
render() {
|
||||
let href = this.props.href;
|
||||
if (href) {
|
||||
let matched = href.match(idReg);
|
||||
if (matched) {
|
||||
let template = map[matched[1] + ':' + this.props.svgId];
|
||||
if (template) {
|
||||
return cloneElement(template, {
|
||||
...this.props,
|
||||
href: null
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.warn(`Invalid href: '${href}' for Use element.\n Please check if '${href}' if defined`);
|
||||
return <NativeGroup />;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: defination scope, global or local?
|
||||
class Defs extends Component{
|
||||
class Defs extends Component {
|
||||
static displayName = 'Defs';
|
||||
static Item = DefsItem;
|
||||
static Use = DefsUse;
|
||||
|
||||
getChildren = () => {
|
||||
return Children.map(this.props.children, child => {
|
||||
let {type} = child;
|
||||
|
||||
if (type === LinearGradient || type === RadialGradient || type === ClipPath) {
|
||||
return cloneElement(child, {
|
||||
svgId: this.props.svgId
|
||||
});
|
||||
}
|
||||
if (child.props.id) {
|
||||
return <DefsItem
|
||||
{...child.props}
|
||||
svgId={this.props.svgId}
|
||||
>{child}</DefsItem>;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return <NativeGroup>{this.getChildren()}</NativeGroup>;
|
||||
return <RNSVGDefination>{this.props.children}</RNSVGDefination>;
|
||||
}
|
||||
}
|
||||
|
||||
const RNSVGDefination = createReactNativeComponentClass({
|
||||
validAttributes: {},
|
||||
uiViewClassName: 'RNSVGDefination'
|
||||
});
|
||||
|
||||
export default Defs;
|
||||
|
||||
@@ -15,9 +15,7 @@ class Ellipse extends Shape{
|
||||
static contextTypes = {
|
||||
...fillProps,
|
||||
...strokeProps,
|
||||
...ellipseProps,
|
||||
isInGroup: PropTypes.bool,
|
||||
svgId: numberProp
|
||||
...ellipseProps
|
||||
};
|
||||
|
||||
setNativeProps = (...args) => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, {Component, PropTypes} from 'react';
|
||||
import Defs from './Defs';
|
||||
import _ from 'lodash';
|
||||
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
|
||||
import {numberProp, contextProps} from '../lib/props';
|
||||
@@ -10,13 +9,10 @@ class G extends Component{
|
||||
static displayName = 'G';
|
||||
|
||||
static contextTypes = {
|
||||
svgId: numberProp,
|
||||
...contextProps
|
||||
};
|
||||
|
||||
static childContextTypes = {
|
||||
svgId: numberProp,
|
||||
isInGroup: PropTypes.bool,
|
||||
...contextProps
|
||||
};
|
||||
|
||||
@@ -26,44 +22,24 @@ class G extends Component{
|
||||
props[key] = this.props[key];
|
||||
}
|
||||
return props;
|
||||
}, {
|
||||
svgId: this.props.svgId || this.context.svgId,
|
||||
isInGroup: true
|
||||
});
|
||||
}, {});
|
||||
|
||||
return _.defaults({}, this.context, context);
|
||||
};
|
||||
|
||||
setNativeProps = (...args) => {
|
||||
this.getNativeElement().setNativeProps(...args);
|
||||
};
|
||||
|
||||
getNativeElement = () => {
|
||||
return this.refs.root || this.root;
|
||||
this.root.setNativeProps(...args);
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.props.id) {
|
||||
return <Defs.Item
|
||||
id={this.props.id}
|
||||
svgId={this.props.svgId}
|
||||
visible={true}
|
||||
>
|
||||
<G
|
||||
{...this.props}
|
||||
ref={ele => this.root = ele.refs.root}
|
||||
id={null}
|
||||
/>
|
||||
</Defs.Item>;
|
||||
} else {
|
||||
return <RNSVGGroup
|
||||
{...extractProps(this.props, {transform: true})}
|
||||
ref="root"
|
||||
ref={ele => this.root = ele}
|
||||
asClipPath={this.props.asClipPath}
|
||||
>
|
||||
{this.props.children}
|
||||
</RNSVGGroup>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const RNSVGGroup = createReactNativeComponentClass({
|
||||
|
||||
@@ -15,9 +15,7 @@ class Line extends Shape {
|
||||
static contextTypes = {
|
||||
...fillProps,
|
||||
...strokeProps,
|
||||
...lineProps,
|
||||
isInGroup: PropTypes.bool,
|
||||
svgId: numberProp
|
||||
...lineProps
|
||||
};
|
||||
|
||||
setNativeProps = (...args) => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, {PropTypes} from 'react';
|
||||
import Defs from './Defs';
|
||||
import SerializablePath from '../lib/SerializablePath';
|
||||
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
|
||||
import {PathAttributes} from '../lib/attributes';
|
||||
@@ -16,9 +15,7 @@ class Path extends Shape {
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
...pathProps,
|
||||
isInGroup: PropTypes.bool,
|
||||
svgId: numberProp
|
||||
...pathProps
|
||||
};
|
||||
|
||||
|
||||
@@ -31,34 +28,16 @@ class Path extends Shape {
|
||||
};
|
||||
|
||||
setNativeProps = (...args) => {
|
||||
this.getNativeElement().setNativeProps(...args);
|
||||
};
|
||||
|
||||
getNativeElement = () => {
|
||||
return this.refs.root || this.root;
|
||||
this.root.setNativeProps(...args);
|
||||
};
|
||||
|
||||
render() {
|
||||
let props = mergeContext(this.props, this.context);
|
||||
|
||||
if (props.id) {
|
||||
return <Defs.Item
|
||||
id={props.id}
|
||||
svgId={props.svgId}
|
||||
visible={true}
|
||||
>
|
||||
<Path
|
||||
ref={ele => this.root = ele.refs.root}
|
||||
{...props}
|
||||
id={null}
|
||||
/>
|
||||
</Defs.Item>;
|
||||
}
|
||||
|
||||
let d = new SerializablePath(props.d).toJSON();
|
||||
return (
|
||||
<RNSVGPath
|
||||
ref="root"
|
||||
ref={ele => this.root = ele}
|
||||
{...this.extractProps(props)}
|
||||
d={d}
|
||||
/>
|
||||
|
||||
@@ -16,9 +16,7 @@ class Rect extends Shape {
|
||||
static contextTypes = {
|
||||
...fillProps,
|
||||
...strokeProps,
|
||||
...rectProps,
|
||||
isInGroup: PropTypes.bool,
|
||||
svgId: numberProp
|
||||
...rectProps
|
||||
};
|
||||
|
||||
setNativeProps = (...args) => {
|
||||
|
||||
@@ -12,8 +12,8 @@ class Shape extends Component {
|
||||
this.state = this.touchableGetInitialState();
|
||||
}
|
||||
|
||||
extractProps = (props) => {
|
||||
let extractedProps = extractProps(props);
|
||||
extractProps = (props, options = {stroke: true, fill: true, responder: true}) => {
|
||||
let extractedProps = extractProps(props, options);
|
||||
if (extractedProps.touchable && !extractedProps.disabled) {
|
||||
_.assign(extractedProps, {
|
||||
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
||||
|
||||
@@ -20,17 +20,11 @@ class Text extends Shape {
|
||||
static contextTypes = {
|
||||
...textProps,
|
||||
...fillProps,
|
||||
...strokeProps,
|
||||
isInGroup: PropTypes.bool,
|
||||
svgId: numberProp
|
||||
...strokeProps
|
||||
};
|
||||
|
||||
setNativeProps = (...args) => {
|
||||
this.getNativeElement().setNativeProps(...args);
|
||||
};
|
||||
|
||||
getNativeElement = () => {
|
||||
return this.refs.root || this.root;
|
||||
this.root.setNativeProps(...args);
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -45,21 +39,9 @@ class Text extends Shape {
|
||||
y = props.dy ? +props.y + (+props.dy) : +props.y;
|
||||
}
|
||||
|
||||
if (this.props.id) {
|
||||
return <Defs.Item
|
||||
ref={ele => this.root = ele.refs.root}
|
||||
id={this.props.id}
|
||||
svgId={this.props.svgId}
|
||||
visible={true}
|
||||
text={true}
|
||||
>
|
||||
<Text {...this.props} id={null} />
|
||||
</Defs.Item>;
|
||||
}
|
||||
|
||||
return (
|
||||
<RNSVGText
|
||||
ref="root"
|
||||
ref={ele => this.root = ele}
|
||||
{...extractProps({...props, x, y})}
|
||||
{...extractText(props)}
|
||||
/>
|
||||
|
||||
@@ -1,14 +1,54 @@
|
||||
import React, {Component, PropTypes} from 'react';
|
||||
import Defs from './Defs';
|
||||
class Use extends Component{
|
||||
import {PropTypes} from 'react';
|
||||
import {pathProps} from '../lib/props';
|
||||
import {UseAttributes} from '../lib/attributes';
|
||||
import Shape from './Shape';
|
||||
import React from 'react';
|
||||
import patternReg from '../lib/extract/patternReg';
|
||||
import createReactNativeComponentClass from 'react/lib/createReactNativeComponentClass';
|
||||
|
||||
class Defs extends Shape {
|
||||
static displayName = 'Use';
|
||||
static propType = {
|
||||
href: PropTypes.string
|
||||
|
||||
static propTypes = {
|
||||
href: PropTypes.string.isRequired,
|
||||
...pathProps
|
||||
};
|
||||
|
||||
setNativeProps = (...args) => {
|
||||
this.root.setNativeProps(...args);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <Defs.Use {...this.props} />;
|
||||
let {props} = this;
|
||||
// 尝试匹配 "url(#pattern)"
|
||||
let matched = props.href.match(patternReg);
|
||||
let href;
|
||||
|
||||
if (matched) {
|
||||
href = matched[1];
|
||||
}
|
||||
|
||||
if (!href) {
|
||||
console.warn('Invalid `href` prop for `Use` element, expected a href like `"url(#id)"`, but got: "' + props.href + '"');
|
||||
}
|
||||
|
||||
return <RNSVGUse
|
||||
ref={ele => this.root = ele}
|
||||
{...this.extractProps(props, {
|
||||
stroke: true,
|
||||
fill: true,
|
||||
responder: true,
|
||||
transform: true
|
||||
})}
|
||||
href={href}
|
||||
>{props.children}</RNSVGUse>;
|
||||
}
|
||||
}
|
||||
|
||||
export default Use;
|
||||
const RNSVGUse = createReactNativeComponentClass({
|
||||
validAttributes: UseAttributes,
|
||||
uiViewClassName: 'RNSVGUse'
|
||||
});
|
||||
|
||||
export default Defs;
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#import "RNSVGSvgView.h"
|
||||
|
||||
@interface RNSVGClipPath : RNSVGGroup
|
||||
@property (nonatomic, strong) NSString *name;
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
|
||||
|
||||
|
||||
@@ -10,16 +10,23 @@
|
||||
|
||||
@implementation RNSVGClipPath
|
||||
|
||||
- (void)renderLayerTo:(CGContextRef)context
|
||||
- (void)saveDefination:(CGContextRef)context
|
||||
{
|
||||
[[self getSvgView] defineClipPath:[self getPath:context] clipPathRef:self.name];
|
||||
}
|
||||
|
||||
// hitTest delagate
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)removeDefination
|
||||
{
|
||||
if (self.name) {
|
||||
[[self getSvgView] removeClipPath: self.name];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
21
ios/Elements/RNSVGDefination.h
Normal file
21
ios/Elements/RNSVGDefination.h
Normal 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 "RNSVGNode.h"
|
||||
|
||||
/**
|
||||
* RNSVG defination are implemented as abstract UIViews for all elements inside Defs.
|
||||
*/
|
||||
|
||||
@interface RNSVGDefination : RNSVGNode
|
||||
|
||||
- (void)renderTo:(CGContextRef)context;
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
|
||||
|
||||
@end
|
||||
27
ios/Elements/RNSVGDefination.m
Normal file
27
ios/Elements/RNSVGDefination.m
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 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 "RNSVGDefination.h"
|
||||
|
||||
@class RNSVGNode;
|
||||
|
||||
@implementation RNSVGDefination
|
||||
|
||||
- (void)renderTo:(CGContextRef)context
|
||||
{
|
||||
for (RNSVGNode *node in self.subviews) {
|
||||
[node saveDefination: context];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#import "RNSVGGroup.h"
|
||||
#import <objc/runtime.h>
|
||||
|
||||
@implementation RNSVGGroup
|
||||
|
||||
@@ -35,17 +36,39 @@
|
||||
}
|
||||
|
||||
// hitTest delagate
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
for (RNSVGNode *node in [self.subviews reverseObjectEnumerator]) {
|
||||
UIView *view = [node hitTest: point withEvent:event];
|
||||
if (view != NULL) {
|
||||
if (view) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)saveDefination:(CGContextRef)context
|
||||
{
|
||||
if (self.name) {
|
||||
RNSVGSvgView* svg = [self getSvgView];
|
||||
[svg defineTemplate:self templateRef:self.name];
|
||||
}
|
||||
|
||||
for (RNSVGNode *node in self.subviews) {
|
||||
[node saveDefination:context];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)willRemoveSubview:(UIView *)subview
|
||||
{
|
||||
[super willRemoveSubview:subview];
|
||||
}
|
||||
|
||||
- (void)mergeProperties:(__kindof RNSVGNode *)target
|
||||
{
|
||||
for (RNSVGNode *node in self.subviews) {
|
||||
[node mergeProperties:target];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
#import "RNSVGContainer.h"
|
||||
|
||||
@class RNSVGNode;
|
||||
|
||||
@interface RNSVGSvgView : UIView <RNSVGContainer>
|
||||
|
||||
@property (nonatomic, assign) BOOL responsible;
|
||||
@@ -17,10 +19,16 @@
|
||||
/**
|
||||
* define <ClipPath></ClipPath> content as clipPath template.
|
||||
*/
|
||||
- (void)defineClipPath:(CGPathRef)clipPath clipPathRef:(NSString *)clipPathId;
|
||||
- (void)defineClipPath:(CGPathRef)clipPath clipPathRef:(NSString *)clipPathRef;
|
||||
|
||||
- (void)removeClipPath:(NSString *)clipPathRef;
|
||||
|
||||
- (CGPathRef)getDefinedClipPath:(NSString *)clipPathRef;
|
||||
|
||||
- (void)defineTemplate:(RNSVGNode *)template templateRef:(NSString *)templateRef;
|
||||
|
||||
- (void)removeTemplate:(NSString *)tempalteRef;
|
||||
|
||||
- (RNSVGNode *)getDefinedTemplate:(NSString *)tempalteRef;
|
||||
|
||||
@end
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
|
||||
@implementation RNSVGSvgView
|
||||
{
|
||||
NSMutableDictionary *clipPaths;
|
||||
NSMutableDictionary<NSString *, NSValue *> *clipPaths;
|
||||
NSMutableDictionary<NSString *, RNSVGNode *> *templates;
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
|
||||
@@ -44,6 +45,7 @@
|
||||
CGContextRef context = UIGraphicsGetCurrentContext();
|
||||
|
||||
for (RNSVGNode *node in self.subviews) {
|
||||
[node saveDefination:context];
|
||||
[node renderTo:context];
|
||||
|
||||
if (node.responsible && !self.responsible) {
|
||||
@@ -64,7 +66,7 @@
|
||||
|
||||
- (void)defineClipPath:(CGPathRef)clipPath clipPathRef:(NSString *)clipPathRef
|
||||
{
|
||||
if (clipPaths == NULL) {
|
||||
if (!clipPaths) {
|
||||
clipPaths = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[clipPaths setValue:[NSValue valueWithPointer:clipPath] forKey:clipPathRef];
|
||||
@@ -72,14 +74,34 @@
|
||||
|
||||
- (void)removeClipPath:(NSString *)clipPathRef
|
||||
{
|
||||
if (clipPaths != NULL) {
|
||||
if (clipPaths) {
|
||||
[clipPaths removeObjectForKey:clipPathRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGPathRef)getDefinedClipPath:(NSString *)clipPathRef
|
||||
{
|
||||
return [[clipPaths valueForKey:clipPathRef] pointerValue];
|
||||
return clipPaths ? [[clipPaths valueForKey:clipPathRef] pointerValue] : nil;
|
||||
}
|
||||
|
||||
- (void)defineTemplate:(RNSVGNode *)template templateRef:(NSString *)templateRef
|
||||
{
|
||||
if (!templates) {
|
||||
templates = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
[templates setObject:template forKey:templateRef];
|
||||
}
|
||||
|
||||
- (void)removeTemplate:(NSString *)tempalteRef
|
||||
{
|
||||
if (templates) {
|
||||
[templates removeObjectForKey:tempalteRef];
|
||||
}
|
||||
}
|
||||
|
||||
- (RNSVGNode *)getDefinedTemplate:(NSString *)tempalteRef
|
||||
{
|
||||
return templates ? [templates objectForKey:tempalteRef] : nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
19
ios/Elements/RNSVGUse.h
Normal file
19
ios/Elements/RNSVGUse.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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 "RNSVGRenderable.h"
|
||||
|
||||
/**
|
||||
* RNSVG defination are implemented as abstract UIViews for all elements inside Defs.
|
||||
*/
|
||||
|
||||
@interface RNSVGUse : RNSVGRenderable
|
||||
|
||||
@property (nonatomic, strong) NSString *href;
|
||||
|
||||
@end
|
||||
26
ios/Elements/RNSVGUse.m
Normal file
26
ios/Elements/RNSVGUse.m
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* 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 "RNSVGUse.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@implementation RNSVGUse
|
||||
|
||||
- (void)renderLayerTo:(CGContextRef)context
|
||||
{
|
||||
RNSVGNode* template = [[self getSvgView] getDefinedTemplate:self.href];
|
||||
if (template) {
|
||||
[template mergeProperties:self];
|
||||
[template renderTo:context];
|
||||
} else if (self.href) {
|
||||
// TODO: calling yellow box here
|
||||
RCTLogWarn(@"`Use` element expected a pre-defined svg template as `href` prop, template named: %@ is not defined.", self.href);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
0CF68B0D1AF0549300FF9E5C /* RNSVGPattern.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF01AF0549300FF9E5C /* RNSVGPattern.m */; };
|
||||
0CF68B0E1AF0549300FF9E5C /* RNSVGRadialGradient.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF21AF0549300FF9E5C /* RNSVGRadialGradient.m */; };
|
||||
0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AF41AF0549300FF9E5C /* RNSVGSolidColor.m */; };
|
||||
1023B48D1D3DDCCE0051496D /* RNSVGDefinationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B48C1D3DDCCE0051496D /* RNSVGDefinationManager.m */; };
|
||||
1023B4901D3DF4C40051496D /* RNSVGDefination.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B48F1D3DF4C40051496D /* RNSVGDefination.m */; };
|
||||
1023B4931D3DF5060051496D /* RNSVGUse.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B4921D3DF5060051496D /* RNSVGUse.m */; };
|
||||
1023B4961D3DF57D0051496D /* RNSVGUseManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1023B4951D3DF57D0051496D /* RNSVGUseManager.m */; };
|
||||
1039D2891CE71EB7001E90A8 /* RNSVGGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2821CE71EB7001E90A8 /* RNSVGGroup.m */; };
|
||||
1039D28A1CE71EB7001E90A8 /* RNSVGImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2841CE71EB7001E90A8 /* RNSVGImage.m */; };
|
||||
1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 1039D2861CE71EB7001E90A8 /* RNSVGPath.m */; };
|
||||
@@ -68,6 +72,14 @@
|
||||
0CF68AF21AF0549300FF9E5C /* RNSVGRadialGradient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGRadialGradient.m; sourceTree = "<group>"; };
|
||||
0CF68AF31AF0549300FF9E5C /* RNSVGSolidColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGSolidColor.h; sourceTree = "<group>"; };
|
||||
0CF68AF41AF0549300FF9E5C /* RNSVGSolidColor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSolidColor.m; sourceTree = "<group>"; };
|
||||
1023B48B1D3DDCCE0051496D /* RNSVGDefinationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGDefinationManager.h; sourceTree = "<group>"; };
|
||||
1023B48C1D3DDCCE0051496D /* RNSVGDefinationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGDefinationManager.m; sourceTree = "<group>"; };
|
||||
1023B48E1D3DF4C40051496D /* RNSVGDefination.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGDefination.h; path = Elements/RNSVGDefination.h; sourceTree = "<group>"; };
|
||||
1023B48F1D3DF4C40051496D /* RNSVGDefination.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNSVGDefination.m; path = Elements/RNSVGDefination.m; sourceTree = "<group>"; };
|
||||
1023B4911D3DF5060051496D /* RNSVGUse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGUse.h; path = Elements/RNSVGUse.h; 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>"; };
|
||||
1023B4951D3DF57D0051496D /* RNSVGUseManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGUseManager.m; 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>"; };
|
||||
1039D2831CE71EB7001E90A8 /* RNSVGImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNSVGImage.h; path = Elements/RNSVGImage.h; sourceTree = "<group>"; };
|
||||
@@ -185,6 +197,10 @@
|
||||
0CF68AF81AF0549300FF9E5C /* ViewManagers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1023B4941D3DF57D0051496D /* RNSVGUseManager.h */,
|
||||
1023B4951D3DF57D0051496D /* RNSVGUseManager.m */,
|
||||
1023B48B1D3DDCCE0051496D /* RNSVGDefinationManager.h */,
|
||||
1023B48C1D3DDCCE0051496D /* RNSVGDefinationManager.m */,
|
||||
10ED4A9C1CF0656A0078BC02 /* RNSVGClipPathManager.h */,
|
||||
10ED4A9D1CF0656A0078BC02 /* RNSVGClipPathManager.m */,
|
||||
10BA0D1C1CE74E3100887C2B /* RNSVGCircleManager.h */,
|
||||
@@ -245,6 +261,10 @@
|
||||
1039D2801CE71DCF001E90A8 /* Elements */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1023B4911D3DF5060051496D /* RNSVGUse.h */,
|
||||
1023B4921D3DF5060051496D /* RNSVGUse.m */,
|
||||
1023B48E1D3DF4C40051496D /* RNSVGDefination.h */,
|
||||
1023B48F1D3DF4C40051496D /* RNSVGDefination.m */,
|
||||
10ED4A991CF065260078BC02 /* RNSVGClipPath.h */,
|
||||
10ED4A9A1CF065260078BC02 /* RNSVGClipPath.m */,
|
||||
1039D2811CE71EB7001E90A8 /* RNSVGGroup.h */,
|
||||
@@ -336,6 +356,7 @@
|
||||
10BA0D491CE74E3D00887C2B /* RNSVGEllipse.m in Sources */,
|
||||
1039D28B1CE71EB7001E90A8 /* RNSVGPath.m in Sources */,
|
||||
0CF68B0D1AF0549300FF9E5C /* RNSVGPattern.m in Sources */,
|
||||
1023B4931D3DF5060051496D /* RNSVGUse.m in Sources */,
|
||||
0CF68B0E1AF0549300FF9E5C /* RNSVGRadialGradient.m in Sources */,
|
||||
1039D2951CE71EC2001E90A8 /* RNSVGText.m in Sources */,
|
||||
10BA0D3B1CE74E3100887C2B /* RNSVGRectManager.m in Sources */,
|
||||
@@ -352,6 +373,7 @@
|
||||
0CF68B0C1AF0549300FF9E5C /* RNSVGLinearGradient.m in Sources */,
|
||||
10BA0D371CE74E3100887C2B /* RNSVGImageManager.m in Sources */,
|
||||
10BA0D391CE74E3100887C2B /* RNSVGNodeManager.m in Sources */,
|
||||
1023B4901D3DF4C40051496D /* RNSVGDefination.m in Sources */,
|
||||
10BA0D381CE74E3100887C2B /* RNSVGLineManager.m in Sources */,
|
||||
10BA0D481CE74E3D00887C2B /* RNSVGCircle.m in Sources */,
|
||||
10BA0D351CE74E3100887C2B /* RNSVGEllipseManager.m in Sources */,
|
||||
@@ -360,6 +382,8 @@
|
||||
10BA0D361CE74E3100887C2B /* RNSVGGroupManager.m in Sources */,
|
||||
10BA0D4A1CE74E3D00887C2B /* RNSVGLine.m in Sources */,
|
||||
1039D28C1CE71EB7001E90A8 /* RNSVGSvgView.m in Sources */,
|
||||
1023B4961D3DF57D0051496D /* RNSVGUseManager.m in Sources */,
|
||||
1023B48D1D3DDCCE0051496D /* RNSVGDefinationManager.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -11,21 +11,22 @@
|
||||
#import "RNSVGSvgView.h"
|
||||
|
||||
/**
|
||||
* RNSVG nodes are implemented as empty UIViews but this is just an implementation detail to fit
|
||||
* into the existing view management. They should also be shadow views and painted on a background
|
||||
* thread.
|
||||
* RNSVG nodes are implemented as base UIViews. They should be implementation for all basic
|
||||
*interfaces for all non-defination nodes.
|
||||
*/
|
||||
|
||||
@interface RNSVGNode : UIView
|
||||
|
||||
@property (nonatomic, assign) CGRect rect;
|
||||
@property (nonatomic, strong) NSString *name;
|
||||
@property (nonatomic, assign) CGFloat opacity;
|
||||
@property (nonatomic, assign) RNSVGCGFCRule clipRule;
|
||||
@property (nonatomic, assign) CGPathRef clipPath; // convert clipPath="M0,0 L0,10 L10,10z" into path
|
||||
@property (nonatomic, strong) NSString *clipPathRef; // use clipPath="url(#clip)" as ClipPath
|
||||
@property (nonatomic, assign) BOOL responsible;
|
||||
|
||||
|
||||
- (void)invalidate;
|
||||
|
||||
- (void)renderTo:(CGContextRef)context;
|
||||
|
||||
/**
|
||||
@@ -36,20 +37,41 @@
|
||||
- (void)renderLayerTo:(CGContextRef)context;
|
||||
|
||||
/**
|
||||
* clip node by clipPath or clipPathId.
|
||||
* clip node by clipPath or clipPathRef.
|
||||
*/
|
||||
- (void)clip:(CGContextRef)context;
|
||||
|
||||
/**
|
||||
* get clip path for current node.
|
||||
*/
|
||||
- (CGPathRef)getClipPath;
|
||||
|
||||
/**
|
||||
* getPath will return the path inside node as a ClipPath.
|
||||
*/
|
||||
- (CGPathRef)getPath: (CGContextRef) context;
|
||||
- (CGPathRef)getPath:(CGContextRef) context;
|
||||
|
||||
|
||||
/**
|
||||
* run hitTest
|
||||
*/
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
|
||||
|
||||
- (RNSVGSvgView *)getSvgView;
|
||||
|
||||
/**
|
||||
* save element`s defination into svg element.
|
||||
*/
|
||||
- (void)saveDefination:(CGContextRef)context;
|
||||
|
||||
/**
|
||||
* remove element`s defination from svg element.
|
||||
*/
|
||||
- (void)removeDefination;
|
||||
|
||||
/**
|
||||
* merge owned properties into target element`s properties
|
||||
*/
|
||||
- (void)mergeProperties:(__kindof RNSVGNode *)target;
|
||||
|
||||
@end
|
||||
|
||||
@@ -30,6 +30,17 @@
|
||||
// Do nothing, as subviews are inserted by insertReactSubview:
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
id<RNSVGContainer> container = (id<RNSVGContainer>)self.superview;
|
||||
[container invalidate];
|
||||
}
|
||||
|
||||
- (void)reactSetInheritedBackgroundColor:(UIColor *)inheritedBackgroundColor
|
||||
{
|
||||
self.backgroundColor = inheritedBackgroundColor;
|
||||
}
|
||||
|
||||
- (void)setOpacity:(CGFloat)opacity
|
||||
{
|
||||
if (opacity == _opacity) {
|
||||
@@ -52,12 +63,6 @@
|
||||
super.transform = transform;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
id<RNSVGContainer> container = (id<RNSVGContainer>)self.superview;
|
||||
[container invalidate];
|
||||
}
|
||||
|
||||
- (void)renderTo:(CGContextRef)context
|
||||
{
|
||||
float opacity = self.opacity;
|
||||
@@ -111,7 +116,7 @@
|
||||
{
|
||||
CGPathRef clipPath = [self getClipPath];
|
||||
|
||||
if (clipPath != NULL) {
|
||||
if (clipPath) {
|
||||
CGContextAddPath(context, [self getClipPath]);
|
||||
if (self.clipRule == kRNSVGCGFCRuleEvenodd) {
|
||||
CGContextEOClip(context);
|
||||
@@ -121,33 +126,68 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)reactSetInheritedBackgroundColor:(UIColor *)inheritedBackgroundColor
|
||||
{
|
||||
self.backgroundColor = inheritedBackgroundColor;
|
||||
}
|
||||
|
||||
- (void)renderLayerTo:(CGContextRef)context
|
||||
{
|
||||
// abstract
|
||||
}
|
||||
|
||||
- (RNSVGSvgView *)getSvgView
|
||||
{
|
||||
UIView *parent = self.superview;
|
||||
while ([parent class] != [RNSVGSvgView class]) {
|
||||
parent = parent.superview;
|
||||
}
|
||||
|
||||
return (RNSVGSvgView *)parent;
|
||||
}
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
|
||||
{
|
||||
// abstract
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (RNSVGSvgView *)getSvgView
|
||||
{
|
||||
UIView *parent = self.superview;
|
||||
while (parent && [parent class] != [RNSVGSvgView class]) {
|
||||
parent = parent.superview;
|
||||
}
|
||||
|
||||
return (RNSVGSvgView *)parent;
|
||||
}
|
||||
|
||||
- (void)willRemoveFromSuperView
|
||||
{
|
||||
if (self.subviews) {
|
||||
for (RNSVGNode *node in self.subviews) {
|
||||
[node willRemoveFromSuperView];
|
||||
}
|
||||
}
|
||||
[self removeDefination];
|
||||
[super removeFromSuperview];
|
||||
}
|
||||
|
||||
/**
|
||||
* reverse removeFromSuperview calling order.
|
||||
* calling it from subviews to superview.
|
||||
*/
|
||||
- (void)removeFromSuperview
|
||||
{
|
||||
[self willRemoveFromSuperView];
|
||||
}
|
||||
|
||||
- (void)saveDefination:(CGContextRef)context
|
||||
{
|
||||
if (self.name) {
|
||||
RNSVGSvgView* svg = [self getSvgView];
|
||||
[svg defineTemplate:self templateRef:self.name];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeDefination
|
||||
{
|
||||
if (self.name) {
|
||||
[[self getSvgView] removeTemplate:self.name];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)mergeProperties:(__kindof RNSVGNode *)target
|
||||
{
|
||||
self.opacity = target.opacity * self.opacity;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
CGPathRelease(_clipPath);
|
||||
|
||||
@@ -101,8 +101,8 @@
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
CGPathRef clipPath = [self getClipPath];
|
||||
if (self.nodeArea != NULL && CGPathContainsPoint(self.nodeArea, nil, point, NO)) {
|
||||
if (clipPath == NULL) {
|
||||
if (self.nodeArea && CGPathContainsPoint(self.nodeArea, nil, point, NO)) {
|
||||
if (!clipPath) {
|
||||
return self;
|
||||
} else {
|
||||
return CGPathContainsPoint(clipPath, nil, point, NO) ? self : nil;
|
||||
@@ -112,6 +112,40 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mergeProperties:(__kindof RNSVGNode *)target
|
||||
{
|
||||
RNSVGRenderable* renderableTarget = target;
|
||||
|
||||
if (renderableTarget.fill) {
|
||||
self.fill = renderableTarget.fill;
|
||||
}
|
||||
if (renderableTarget.fillRule) {
|
||||
self.fillRule = renderableTarget.fillRule;
|
||||
}
|
||||
if (renderableTarget.stroke) {
|
||||
self.stroke = renderableTarget.stroke;
|
||||
}
|
||||
if (renderableTarget.strokeWidth) {
|
||||
self.strokeWidth = renderableTarget.strokeWidth;
|
||||
}
|
||||
if (renderableTarget.strokeLinecap) {
|
||||
self.strokeLinecap = renderableTarget.strokeLinecap;
|
||||
}
|
||||
if (renderableTarget.strokeLinejoin) {
|
||||
self.strokeLinejoin = renderableTarget.strokeLinejoin;
|
||||
}
|
||||
if (renderableTarget.strokeMiterlimit) {
|
||||
self.strokeMiterlimit = renderableTarget.strokeMiterlimit;
|
||||
}
|
||||
if (renderableTarget.strokeDasharray.count != 0) {
|
||||
self.strokeDasharray = renderableTarget.strokeDasharray;
|
||||
}
|
||||
if (renderableTarget.strokeDashoffset) {
|
||||
self.strokeDashoffset = renderableTarget.strokeDashoffset;
|
||||
}
|
||||
[super mergeProperties:target];
|
||||
}
|
||||
|
||||
- (void)renderLayerTo:(CGContextRef)context
|
||||
{
|
||||
// abstract
|
||||
|
||||
@@ -81,7 +81,7 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
|
||||
}
|
||||
// We should consider snapping this shift to device pixels to improve rendering quality
|
||||
// when a line has subpixel width.
|
||||
CGAffineTransform offset = CGAffineTransformMakeTranslation(-shift, frame.baseLine + frame.lineHeight * i + (self.path == NULL ? 0 : -frame.lineHeight));
|
||||
CGAffineTransform offset = CGAffineTransformMakeTranslation(-shift, frame.baseLine + frame.lineHeight * i + (self.path ? -frame.lineHeight : 0));
|
||||
CGPathAddPath(path, &offset, [self setLinePath:frame.lines[i]]);
|
||||
}
|
||||
|
||||
@@ -115,11 +115,11 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
|
||||
for(CFIndex j = 0; j < runGlyphCount; ++j, ++glyphIndex) {
|
||||
CGPathRef letter = [cache pathForGlyph:glyphs[j] fromFont:runFont];
|
||||
CGPoint point = positions[j];
|
||||
if (letter != NULL) {
|
||||
if (letter) {
|
||||
CGAffineTransform transform;
|
||||
|
||||
// draw glyphs along path
|
||||
if (self.path != NULL) {
|
||||
if (self.path) {
|
||||
CGPoint slope;
|
||||
CGRect bounding = CGPathGetBoundingBox(letter);
|
||||
UIBezierPath* path = [UIBezierPath bezierPathWithCGPath:self.path];
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
-(id)init
|
||||
{
|
||||
self = [super init];
|
||||
if(self != nil)
|
||||
if(self)
|
||||
{
|
||||
cache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
{
|
||||
// First we lookup the font to get to its glyph dictionary
|
||||
CFMutableDictionaryRef glyphDict = (CFMutableDictionaryRef)CFDictionaryGetValue(cache, font);
|
||||
if(glyphDict == NULL)
|
||||
if(!glyphDict)
|
||||
{
|
||||
// And if this font hasn't been seen before, we'll create and set the dictionary for it
|
||||
glyphDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
|
||||
@@ -38,11 +38,11 @@
|
||||
}
|
||||
// Next we try to get a path for the given glyph from the glyph dictionary
|
||||
CGPathRef path = (CGPathRef)CFDictionaryGetValue(glyphDict, (const void *)(uintptr_t)glyph);
|
||||
if(path == NULL)
|
||||
if(!path)
|
||||
{
|
||||
// If the path hasn't been seen before, then we'll create the path from the font & glyph and cache it.
|
||||
path = CTFontCreatePathForGlyph(font, glyph, NULL);
|
||||
if(path == NULL)
|
||||
if(!path)
|
||||
{
|
||||
// If a glyph does not have a path, then we need a placeholder to set in the dictionary
|
||||
path = (CGPathRef)kCFNull;
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
|
||||
- (BOOL) isPercentage:(NSString *) string
|
||||
{
|
||||
return [percentageRegularExpression firstMatchInString:string options:0 range:NSMakeRange(0, [string length])] != NULL;
|
||||
return [percentageRegularExpression firstMatchInString:string options:0 range:NSMakeRange(0, [string length])] != nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
15
ios/ViewManagers/RNSVGDefinationManager.h
Normal file
15
ios/ViewManagers/RNSVGDefinationManager.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* 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 "RCTViewManager.h"
|
||||
|
||||
@interface RNSVGDefinationManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
||||
#import "RNSVGNode.h"
|
||||
31
ios/ViewManagers/RNSVGDefinationManager.m
Normal file
31
ios/ViewManagers/RNSVGDefinationManager.m
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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 "RNSVGDefination.h"
|
||||
#import "RNSVGDefinationManager.h"
|
||||
|
||||
@implementation RNSVGDefinationManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (RNSVGDefination *)node
|
||||
{
|
||||
return [RNSVGDefination new];
|
||||
}
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [self node];
|
||||
}
|
||||
|
||||
- (RCTShadowView *)shadowView
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -29,8 +29,9 @@ RCT_EXPORT_MODULE()
|
||||
return nil;
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(name, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat)
|
||||
RCT_EXPORT_VIEW_PROPERTY(trans, CGAffineTransform)
|
||||
RCT_EXPORT_VIEW_PROPERTY(transform, CGAffineTransform)
|
||||
RCT_EXPORT_VIEW_PROPERTY(clipPathRef, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(responsible, BOOL)
|
||||
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
|
||||
#import "RNSVGRenderableManager.h"
|
||||
|
||||
@interface RNSVGShapeManager : RNSVGRenderableManager
|
||||
@interface RNSVGUseManager : RNSVGRenderableManager
|
||||
|
||||
@end
|
||||
@@ -6,20 +6,19 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RNSVGShapeManager.h"
|
||||
#import "RNSVGUseManager.h"
|
||||
#import "RNSVGUse.h"
|
||||
|
||||
#import "RNSVGShape.h"
|
||||
#import "RCTConvert+RNSVG.h"
|
||||
|
||||
@implementation RNSVGShapeManager
|
||||
@implementation RNSVGUseManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (RNSVGRenderable *)node
|
||||
- (RNSVGNode *)node
|
||||
{
|
||||
return [RNSVGShape new];
|
||||
return [RNSVGUse new];
|
||||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(shape, NSDictionary)
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(href, NSString)
|
||||
|
||||
@end
|
||||
@@ -3,7 +3,7 @@ import _ from 'lodash';
|
||||
const merge = _.assign;
|
||||
|
||||
function arrayDiffer(a, b) {
|
||||
if (a == null || b == null) {
|
||||
if (_.isNil(a) || _.isNil(b) ) {
|
||||
return true;
|
||||
}
|
||||
if (a.length !== b.length) {
|
||||
@@ -79,6 +79,10 @@ const GroupAttributes = merge(NodeAttributes, {
|
||||
clipRule: true
|
||||
});
|
||||
|
||||
const UseAttributes = merge(RenderableAttributes, {
|
||||
href: true
|
||||
});
|
||||
|
||||
const PathAttributes = merge(RenderableAttributes, {
|
||||
d: {
|
||||
diff: arrayDiffer
|
||||
@@ -146,5 +150,6 @@ export {
|
||||
EllipseAttributes,
|
||||
ImageAttributes,
|
||||
LineAttributes,
|
||||
RectAttributes
|
||||
RectAttributes,
|
||||
UseAttributes
|
||||
};
|
||||
|
||||
@@ -6,16 +6,14 @@ import extractResponder from './extractResponder';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default function(props, options = {stroke: true, transform: true, fill: true, responder: true}) {
|
||||
if (props.visible === false) {
|
||||
return {
|
||||
opacity: 0
|
||||
};
|
||||
}
|
||||
|
||||
let extractedProps = {
|
||||
opacity: +props.opacity || 1
|
||||
};
|
||||
|
||||
if (props.id) {
|
||||
extractedProps.name = props.id;
|
||||
}
|
||||
|
||||
if (props.clipPath) {
|
||||
_.assign(extractedProps, extractClipping(props));
|
||||
}
|
||||
@@ -30,6 +28,10 @@ export default function(props, options = {stroke: true, transform: true, fill: t
|
||||
|
||||
if (options.transform) {
|
||||
extractedProps.transform = extractTransform(props);
|
||||
} else if (props.transform) {
|
||||
// todo: add support for transform prop like this:
|
||||
// {scale: 1.5, translate: '10 10'}
|
||||
extractedProps.transform = props.transform;
|
||||
}
|
||||
|
||||
if (options.responder) {
|
||||
|
||||
@@ -37,6 +37,10 @@ const clipProps = {
|
||||
clipPath: PropTypes.string
|
||||
};
|
||||
|
||||
const definationProps = {
|
||||
name: PropTypes.string
|
||||
};
|
||||
|
||||
const strokeProps = {
|
||||
stroke: PropTypes.string,
|
||||
strokeWidth: numberProp,
|
||||
@@ -77,7 +81,8 @@ const pathProps = {
|
||||
...clipProps,
|
||||
...transformProps,
|
||||
...responderProps,
|
||||
...touchableProps
|
||||
...touchableProps,
|
||||
...definationProps
|
||||
};
|
||||
|
||||
const circleProps = {
|
||||
|
||||
Reference in New Issue
Block a user