diff --git a/Example/examples.js b/Example/examples.js index d9992710..e8f46b19 100644 --- a/Example/examples.js +++ b/Example/examples.js @@ -13,6 +13,7 @@ import * as Use from './examples/Use'; import * as Symbol from './examples/Symbol'; import * as Gradients from './examples/Gradients'; import * as Clipping from './examples/Clipping'; +import * as Image from './examples/Image'; export { Svg, @@ -29,5 +30,6 @@ export { Use, Symbol, Gradients, - Clipping + Clipping, + Image }; diff --git a/Example/examples/Image.js b/Example/examples/Image.js new file mode 100644 index 00000000..c8772147 --- /dev/null +++ b/Example/examples/Image.js @@ -0,0 +1,83 @@ +import React, { + Component +} from 'react-native'; + +import Svg, { + Image, + Defs, + Circle, + ClipPath, + Text +} from 'react-native-svg'; + +class ImageExample extends Component{ + static title = 'Image'; + + render() { + return + + ; + } +} + +class ClipImage extends Component{ + static title = 'Clip Image'; + + render() { + return + + + + + + + HOGWARTS + ; + } +} + +const icon = + +; + +const samples = [ImageExample, ClipImage]; + +export { + icon, + samples +}; diff --git a/Example/image.jpg b/Example/image.jpg new file mode 100644 index 00000000..8fddd5c1 Binary files /dev/null and b/Example/image.jpg differ diff --git a/Example/main.js b/Example/main.js index 2d5d1028..86bf18c8 100644 --- a/Example/main.js +++ b/Example/main.js @@ -105,7 +105,7 @@ const styles = StyleSheet.create({ } }); -const names = ['Svg', 'Stroking', 'Path', 'Line', 'Rect', 'Polygon', 'Polyline', 'Circle', 'Ellipse', 'G', 'Text', 'Use', 'Symbol', 'Gradients', 'Clipping']; +const names = ['Svg', 'Stroking', 'Path', 'Line', 'Rect', 'Polygon', 'Polyline', 'Circle', 'Ellipse', 'G', 'Text', 'Use', 'Symbol', 'Gradients', 'Clipping', 'Image']; class ArtSvgExample extends Component { constructor() { diff --git a/elements/Image.js b/elements/Image.js index de76f1ea..99d2c2de 100644 --- a/elements/Image.js +++ b/elements/Image.js @@ -1,25 +1,41 @@ -import { +import React, { Component, PropTypes } from 'react-native'; - -let propType = PropTypes.oneOfType([PropTypes.string, PropTypes.number]); +import {NativeGroup} from './G'; +import extractProps from '../lib/extract/extractProps'; +import {ImageAttributes} from '../lib/attributes'; +import Rect from './Rect'; +import {numberProp} from '../lib/props'; +import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; +import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass'; +import formatPercentageProps from '../lib/formatPercentageProps'; class Image extends Component{ static displayName = 'Image'; static propTypes = { - x1: propType, - x2: propType, - y1: propType, - y2: propType, - href: PropTypes.string + x: numberProp, + y: numberProp, + width: numberProp, + height: numberProp, + href: PropTypes.number.isRequired //preserveAspectRatio: PropTypes.string }; render() { - return null; + return ; } } +let RNSVGImage = createReactNativeComponentClass({ + validAttributes: ImageAttributes, + uiViewClassName: 'RNSVGImage' +}); + + export default Image; diff --git a/elements/Shape.js b/elements/Shape.js index 9a1b7eb3..a3319bba 100644 --- a/elements/Shape.js +++ b/elements/Shape.js @@ -1,11 +1,11 @@ import React, { Component } from 'react-native'; -import './Path'; // must import Path first, don`t know why. without this will throw an `Super expression must either be null or a function, not undefined`, maybe cyclic dependencies issue +import './Path'; // must import Path first, don`t know why. without this will throw an `Super expression must either be null or a function, not undefined` import _ from 'lodash'; import extractProps from '../lib/extract/extractProps'; import {ShapeAttributes} from '../lib/attributes'; -import SerializableShape from '../lib/SerializableShape'; +import formatPercentageProps from '../lib/formatPercentageProps'; import createReactNativeComponentClass from 'react-native/Libraries/ReactNative/createReactNativeComponentClass'; import {circleProps, ellipseProps, lineProps, rectProps} from '../lib/props'; @@ -55,8 +55,6 @@ class Shape extends Component{ }); } - let shape = new SerializableShape(props, COORD_PROPS[this.type]).toJSON(); - return ; diff --git a/ios/RNSVG.xcodeproj/project.pbxproj b/ios/RNSVG.xcodeproj/project.pbxproj index ec2ad54f..43856bf4 100644 --- a/ios/RNSVG.xcodeproj/project.pbxproj +++ b/ios/RNSVG.xcodeproj/project.pbxproj @@ -21,6 +21,8 @@ 0CF68B121AF0549300FF9E5C /* RNSVGNodeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFC1AF0549300FF9E5C /* RNSVGNodeManager.m */; }; 0CF68B131AF0549300FF9E5C /* RNSVGRenderableManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68AFE1AF0549300FF9E5C /* RNSVGRenderableManager.m */; }; 0CF68B161AF0549300FF9E5C /* RNSVGTextManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0CF68B041AF0549300FF9E5C /* RNSVGTextManager.m */; }; + 108FD88C1CDAF09B00A65FB3 /* RNSVGImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 108FD88B1CDAF09B00A65FB3 /* RNSVGImageManager.m */; }; + 108FD88F1CDAF0A300A65FB3 /* RNSVGImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 108FD88E1CDAF0A300A65FB3 /* RNSVGImage.m */; }; 10A062FE1CC732020000CEEF /* RNSVGPathManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A062FB1CC732020000CEEF /* RNSVGPathManager.m */; }; 10A062FF1CC732020000CEEF /* RNSVGSvgViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A062FD1CC732020000CEEF /* RNSVGSvgViewManager.m */; }; 10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063011CC7320C0000CEEF /* RNSVGPath.m */; }; @@ -74,6 +76,10 @@ 0CF68AFE1AF0549300FF9E5C /* RNSVGRenderableManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGRenderableManager.m; sourceTree = ""; }; 0CF68B031AF0549300FF9E5C /* RNSVGTextManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGTextManager.h; sourceTree = ""; }; 0CF68B041AF0549300FF9E5C /* RNSVGTextManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGTextManager.m; sourceTree = ""; }; + 108FD88A1CDAF09B00A65FB3 /* RNSVGImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGImageManager.h; sourceTree = ""; }; + 108FD88B1CDAF09B00A65FB3 /* RNSVGImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGImageManager.m; sourceTree = ""; }; + 108FD88D1CDAF0A300A65FB3 /* RNSVGImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGImage.h; sourceTree = ""; }; + 108FD88E1CDAF0A300A65FB3 /* RNSVGImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGImage.m; sourceTree = ""; }; 10A062FA1CC732020000CEEF /* RNSVGPathManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGPathManager.h; sourceTree = ""; }; 10A062FB1CC732020000CEEF /* RNSVGPathManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGPathManager.m; sourceTree = ""; }; 10A062FC1CC732020000CEEF /* RNSVGSvgViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGSvgViewManager.h; sourceTree = ""; }; @@ -112,6 +118,8 @@ 0CF68ADE1AF0549300FF9E5C /* RNSVGGroup.m */, 10C068641CCF0F87007C6982 /* RNSVGShape.h */, 10C068651CCF0F87007C6982 /* RNSVGShape.m */, + 108FD88D1CDAF0A300A65FB3 /* RNSVGImage.h */, + 108FD88E1CDAF0A300A65FB3 /* RNSVGImage.m */, 10A063001CC7320C0000CEEF /* RNSVGPath.h */, 10A063011CC7320C0000CEEF /* RNSVGPath.m */, 0CF68ADF1AF0549300FF9E5C /* RNSVGNode.h */, @@ -157,6 +165,8 @@ 0CF68AF81AF0549300FF9E5C /* ViewManagers */ = { isa = PBXGroup; children = ( + 108FD88A1CDAF09B00A65FB3 /* RNSVGImageManager.h */, + 108FD88B1CDAF09B00A65FB3 /* RNSVGImageManager.m */, 10C068681CCF1061007C6982 /* RNSVGShapeManager.h */, 10C068691CCF1061007C6982 /* RNSVGShapeManager.m */, 10A062FA1CC732020000CEEF /* RNSVGPathManager.h */, @@ -240,11 +250,13 @@ 0CF68B051AF0549300FF9E5C /* RNSVGGroup.m in Sources */, 10A062FF1CC732020000CEEF /* RNSVGSvgViewManager.m in Sources */, 0CF68B131AF0549300FF9E5C /* RNSVGRenderableManager.m in Sources */, + 108FD88F1CDAF0A300A65FB3 /* RNSVGImage.m in Sources */, 0CF68B0E1AF0549300FF9E5C /* RNSVGRadialGradient.m in Sources */, 10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */, 0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */, 0CF68B101AF0549300FF9E5C /* RCTConvert+RNSVG.m in Sources */, 10C0686A1CCF1061007C6982 /* RNSVGShapeManager.m in Sources */, + 108FD88C1CDAF09B00A65FB3 /* RNSVGImageManager.m in Sources */, 10A062FE1CC732020000CEEF /* RNSVGPathManager.m in Sources */, 0CF68B061AF0549300FF9E5C /* RNSVGNode.m in Sources */, 0CF68B0F1AF0549300FF9E5C /* RNSVGSolidColor.m in Sources */, diff --git a/ios/RNSVGImage.h b/ios/RNSVGImage.h new file mode 100644 index 00000000..6ffb0f7d --- /dev/null +++ b/ios/RNSVGImage.h @@ -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 + +#import "RNSVGRenderable.h" + +@interface RNSVGImage : RNSVGRenderable +@property (nonatomic, strong) NSDictionary* layout; +@property (nonatomic, assign) id src; + +- (CGFloat)getActualProp:(NSString *)name relative:(float)relative; + +@end diff --git a/ios/RNSVGImage.m b/ios/RNSVGImage.m new file mode 100644 index 00000000..2e67f96a --- /dev/null +++ b/ios/RNSVGImage.m @@ -0,0 +1,67 @@ +/** + * 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 "RNSVGImage.h" +#import "RCTConvert+RNSVG.h" +#import "RCTLog.h" + +@implementation RNSVGImage +{ + CGImageRef image; +} +- (void)setSrc:(id)src +{ + if (src == _src) { + return; + } + _src = src; + CGImageRelease(image); + image = CGImageRetain([RCTConvert CGImage:src]); + [self invalidate]; +} + +- (void)setLayout:(NSDictionary *)layout +{ + if (layout == _layout) { + return; + } + _layout = layout; + [self invalidate]; +} + +- (void)dealloc +{ + CGImageRelease(image); +} + +- (void)renderLayerTo:(CGContextRef)context +{ + CGRect box = CGContextGetClipBoundingBox(context); + float height = CGRectGetHeight(box); + float width = CGRectGetWidth(box); + + CGFloat x = [self getActualProp:@"x" relative:width]; + CGFloat y = [self getActualProp:@"y" relative:height]; + CGFloat w = [self getActualProp:@"width" relative:width]; + CGFloat h = [self getActualProp:@"height" relative:height]; + [self clip:context]; + CGContextSaveGState(context); + CGContextTranslateCTM(context, 0, h); + CGContextScaleCTM(context, 1.0, -1.0); + CGContextDrawImage(context, CGRectMake(x, -y, w, h), image); + CGContextRestoreGState(context); +} + +- (CGFloat)getActualProp:(NSString *)name relative:(float)relative +{ + NSDictionary *prop = [self.layout objectForKey:name]; + return [super getActualProp:prop relative:relative]; + +} + +@end diff --git a/ios/RNSVGRenderable.h b/ios/RNSVGRenderable.h index c941e081..490dd122 100644 --- a/ios/RNSVGRenderable.h +++ b/ios/RNSVGRenderable.h @@ -24,4 +24,6 @@ @property (nonatomic, assign) RNSVGCGFloatArray strokeDasharray; @property (nonatomic, assign) CGFloat strokeDashoffset; +- (CGFloat)getActualProp:(NSDictionary *) prop relative:(float)relative; + @end diff --git a/ios/RNSVGRenderable.m b/ios/RNSVGRenderable.m index 1d3b324b..6e1b9431 100644 --- a/ios/RNSVGRenderable.m +++ b/ios/RNSVGRenderable.m @@ -81,6 +81,17 @@ CGContextRestoreGState(context); } +- (CGFloat)getActualProp:(NSDictionary *) prop relative:(float)relative +{ + CGFloat value = [[prop objectForKey:@"value"] floatValue]; + if ([[prop objectForKey:@"percentage"] integerValue] == 1) { + return relative * value; + } else { + return value; + } +} + + - (void)renderLayerTo:(CGContextRef)context { // abstract diff --git a/ios/RNSVGShape.h b/ios/RNSVGShape.h index 17a28727..bdf815fe 100644 --- a/ios/RNSVGShape.h +++ b/ios/RNSVGShape.h @@ -14,4 +14,6 @@ @interface RNSVGShape : RNSVGPath @property (nonatomic, strong) NSDictionary* shape; +- (CGFloat)getActualProp:(NSString *)name relative:(float)relative; + @end diff --git a/ios/RNSVGShape.m b/ios/RNSVGShape.m index 38702d32..1fd30bf2 100644 --- a/ios/RNSVGShape.m +++ b/ios/RNSVGShape.m @@ -112,12 +112,7 @@ - (CGFloat)getActualProp:(NSString *)name relative:(float)relative { NSDictionary *prop = [self.shape objectForKey:name]; - CGFloat value = [[prop objectForKey:@"value"] floatValue]; - if ([[prop objectForKey:@"percentage"] integerValue] == 1) { - return relative * value; - } else { - return value; - } + return [super getActualProp:prop relative:relative]; } @end diff --git a/ios/ViewManagers/RNSVGImageManager.h b/ios/ViewManagers/RNSVGImageManager.h new file mode 100644 index 00000000..c799d3e5 --- /dev/null +++ b/ios/ViewManagers/RNSVGImageManager.h @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2015-present, Horcrux. + * All rights reserved. + * + * This source code is licensed under the MIT-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RNSVGRenderableManager.h" + +@interface RNSVGImageManager : RNSVGRenderableManager + +@end diff --git a/ios/ViewManagers/RNSVGImageManager.m b/ios/ViewManagers/RNSVGImageManager.m new file mode 100644 index 00000000..1284602a --- /dev/null +++ b/ios/ViewManagers/RNSVGImageManager.m @@ -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 "RNSVGImageManager.h" + +#import "RNSVGImage.h" +#import "RCTConvert+RNSVG.h" + +@implementation RNSVGImageManager + +RCT_EXPORT_MODULE() + +- (RNSVGRenderable *)node +{ + return [RNSVGImage new]; +} + +RCT_EXPORT_VIEW_PROPERTY(layout, NSDictionary) +RCT_EXPORT_VIEW_PROPERTY(src, id) + +@end diff --git a/lib/SerializablePath.js b/lib/SerializablePath.js index 0577e84d..b69cc518 100644 --- a/lib/SerializablePath.js +++ b/lib/SerializablePath.js @@ -17,7 +17,6 @@ export default class SerializablePath { } /* parser */ - push = () => { let p = Array.prototype.join.call(arguments, ' ') .replace(/(\.[\d]+)(\-?\.)/ig, '$1,$2') //-.3-.575 => -.3,-.575 @@ -33,65 +32,27 @@ export default class SerializablePath { while (cmd){ switch (cmd){ - case 'm': - this.move(p[i++], p[i++]); - break; - case 'l': - this.line(p[i++], p[i++]); - break; - case 'c': - this.curve(p[i++], p[i++], p[i++], p[i++], 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 '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 'h': - this.line(p[i++], 0); - break; - case 'v': - this.line(0, p[i++]); - break; + case 'm': this.move(p[i++], p[i++]); break; + case 'l': this.line(p[i++], p[i++]); break; + case 'c': this.curve(p[i++], p[i++], p[i++], p[i++], 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 '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 'h': this.line(p[i++], 0); break; + case 'v': this.line(0, p[i++]); break; - case 'M': - this.moveTo(p[i++], p[i++]); - break; - case 'L': - this.lineTo(p[i++], p[i++]); - break; - case 'C': - this.curveTo(p[i++], p[i++], p[i++], p[i++], 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 '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 'H': - this.lineTo(p[i++], this.penY); - break; - case 'V': - this.lineTo(this.penX, p[i++]); - break; - case 'Z': - case 'z': - this.close(); - break; + case 'M': this.moveTo(p[i++], p[i++]); break; + case 'L': this.lineTo(p[i++], p[i++]); break; + case 'C': this.curveTo(p[i++], p[i++], p[i++], p[i++], 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 '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 'H': this.lineTo(p[i++], this.penY); break; + case 'V': this.lineTo(this.penX, p[i++]); break; + + case 'Z': case 'z': this.close(); break; default: cmd = last; i--; diff --git a/lib/attributes.js b/lib/attributes.js index e982e5e9..d5cf4ecb 100644 --- a/lib/attributes.js +++ b/lib/attributes.js @@ -15,7 +15,7 @@ function arrayDiffer(a, b) { return false; } -function shapeDiffer(a, b) { +function percentageDiffer(a, b) { if (a === b) { return false; } @@ -111,15 +111,24 @@ const TextAttributes = { const ShapeAttributes = { shape: { - diff: shapeDiffer + diff: percentageDiffer }, ...RenderableAttributes }; +const ImageAttributes = { + ...NodeAttributes, + layout: { + diff: percentageDiffer + }, + src: true +}; + export { GroupAttributes, PathAttributes, TextAttributes, - ShapeAttributes + ShapeAttributes, + ImageAttributes }; diff --git a/lib/extract/extractProps.js b/lib/extract/extractProps.js index 355ce61f..bd58553a 100644 --- a/lib/extract/extractProps.js +++ b/lib/extract/extractProps.js @@ -4,7 +4,7 @@ import extractTransform from './extractTransform'; import extractClipping from './extractClipping'; import _ from 'lodash'; -export default function(props, options = {stroke: true, join: true, transform: true, fill: true}) { +export default function(props, options = {stroke: true, transform: true, fill: true}) { if (props.visible === false) { return { opacity: 0 diff --git a/lib/SerializableShape.js b/lib/formatPercentageProps.js similarity index 68% rename from lib/SerializableShape.js rename to lib/formatPercentageProps.js index fca9af43..ddc74156 100644 --- a/lib/SerializableShape.js +++ b/lib/formatPercentageProps.js @@ -38,18 +38,12 @@ function percentageTransform(value) { }; } -export default class { - constructor(props, list) { - this.shape = {}; - list.forEach(name => { - if (!_.isNil(props[name])) { - this.shape[name] = percentageTransform(props[name]); - } - }); - } +export default function (props, list) { + return _.reduce(list, (prev, name) => { + if (!_.isNil(props[name])) { + prev[name] = percentageTransform(props[name]); + } - toJSON = () => { - return this.shape; - }; + return prev; + }, {}); } -