mirror of
https://github.com/zoriya/react-native-svg.git
synced 2025-12-20 22:05:14 +00:00
Add text path for iOS
Add text path for iOS Add clipPath Text support (iOS)
This commit is contained in:
@@ -124,6 +124,13 @@ class ClipPathElement extends Component{
|
|||||||
<Ellipse cx="60" cy="70" rx="20" ry="10" />
|
<Ellipse cx="60" cy="70" rx="20" ry="10" />
|
||||||
<Rect x="65" y="15" width="30" height="30" />
|
<Rect x="65" y="15" width="30" height="30" />
|
||||||
<Polygon points="20,60 20,80 50,70" />
|
<Polygon points="20,60 20,80 50,70" />
|
||||||
|
<Text
|
||||||
|
x="50"
|
||||||
|
y="30"
|
||||||
|
fontSize="32"
|
||||||
|
fonWeight="bold"
|
||||||
|
textAnchor="middle"
|
||||||
|
>Q</Text>
|
||||||
</ClipPath>
|
</ClipPath>
|
||||||
</Defs>
|
</Defs>
|
||||||
<Rect
|
<Rect
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063011CC7320C0000CEEF /* RNSVGPath.m */; };
|
10A063041CC7320C0000CEEF /* RNSVGPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063011CC7320C0000CEEF /* RNSVGPath.m */; };
|
||||||
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063031CC7320C0000CEEF /* RNSVGSvgView.m */; };
|
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */ = {isa = PBXBuildFile; fileRef = 10A063031CC7320C0000CEEF /* RNSVGSvgView.m */; };
|
||||||
10B898DF1CE45973003CD3D4 /* RNSVGGlyphCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 10B898DE1CE45973003CD3D4 /* RNSVGGlyphCache.m */; };
|
10B898DF1CE45973003CD3D4 /* RNSVGGlyphCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 10B898DE1CE45973003CD3D4 /* RNSVGGlyphCache.m */; };
|
||||||
|
10B898E11CE490FC003CD3D4 /* UIBezierPath-Points.m in Sources */ = {isa = PBXBuildFile; fileRef = 10B898E01CE490FC003CD3D4 /* UIBezierPath-Points.m */; };
|
||||||
10C068671CCF0F87007C6982 /* RNSVGShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 10C068651CCF0F87007C6982 /* RNSVGShape.m */; };
|
10C068671CCF0F87007C6982 /* RNSVGShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 10C068651CCF0F87007C6982 /* RNSVGShape.m */; };
|
||||||
10C0686A1CCF1061007C6982 /* RNSVGShapeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10C068691CCF1061007C6982 /* RNSVGShapeManager.m */; };
|
10C0686A1CCF1061007C6982 /* RNSVGShapeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 10C068691CCF1061007C6982 /* RNSVGShapeManager.m */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@@ -91,6 +92,8 @@
|
|||||||
10A063031CC7320C0000CEEF /* RNSVGSvgView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSvgView.m; sourceTree = "<group>"; };
|
10A063031CC7320C0000CEEF /* RNSVGSvgView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGSvgView.m; sourceTree = "<group>"; };
|
||||||
10B898DD1CE4591F003CD3D4 /* RNSVGGlyphCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGGlyphCache.h; sourceTree = "<group>"; };
|
10B898DD1CE4591F003CD3D4 /* RNSVGGlyphCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGGlyphCache.h; sourceTree = "<group>"; };
|
||||||
10B898DE1CE45973003CD3D4 /* RNSVGGlyphCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGGlyphCache.m; sourceTree = "<group>"; };
|
10B898DE1CE45973003CD3D4 /* RNSVGGlyphCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGGlyphCache.m; sourceTree = "<group>"; };
|
||||||
|
10B898E01CE490FC003CD3D4 /* UIBezierPath-Points.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIBezierPath-Points.m"; sourceTree = "<group>"; };
|
||||||
|
10B898E21CE4910A003CD3D4 /* UIBezierPath-Points.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIBezierPath-Points.h"; sourceTree = "<group>"; };
|
||||||
10C068641CCF0F87007C6982 /* RNSVGShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGShape.h; sourceTree = SOURCE_ROOT; };
|
10C068641CCF0F87007C6982 /* RNSVGShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGShape.h; sourceTree = SOURCE_ROOT; };
|
||||||
10C068651CCF0F87007C6982 /* RNSVGShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGShape.m; sourceTree = SOURCE_ROOT; };
|
10C068651CCF0F87007C6982 /* RNSVGShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNSVGShape.m; sourceTree = SOURCE_ROOT; };
|
||||||
10C068681CCF1061007C6982 /* RNSVGShapeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGShapeManager.h; sourceTree = "<group>"; };
|
10C068681CCF1061007C6982 /* RNSVGShapeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNSVGShapeManager.h; sourceTree = "<group>"; };
|
||||||
@@ -138,6 +141,8 @@
|
|||||||
0CF68AF71AF0549300FF9E5C /* RCTConvert+RNSVG.m */,
|
0CF68AF71AF0549300FF9E5C /* RCTConvert+RNSVG.m */,
|
||||||
10B898DD1CE4591F003CD3D4 /* RNSVGGlyphCache.h */,
|
10B898DD1CE4591F003CD3D4 /* RNSVGGlyphCache.h */,
|
||||||
10B898DE1CE45973003CD3D4 /* RNSVGGlyphCache.m */,
|
10B898DE1CE45973003CD3D4 /* RNSVGGlyphCache.m */,
|
||||||
|
10B898E21CE4910A003CD3D4 /* UIBezierPath-Points.h */,
|
||||||
|
10B898E01CE490FC003CD3D4 /* UIBezierPath-Points.m */,
|
||||||
0CF68AC21AF0540F00FF9E5C /* Products */,
|
0CF68AC21AF0540F00FF9E5C /* Products */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -259,6 +264,7 @@
|
|||||||
0CF68B0E1AF0549300FF9E5C /* RNSVGRadialGradient.m in Sources */,
|
0CF68B0E1AF0549300FF9E5C /* RNSVGRadialGradient.m in Sources */,
|
||||||
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */,
|
10A063051CC7320C0000CEEF /* RNSVGSvgView.m in Sources */,
|
||||||
0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */,
|
0CF68B071AF0549300FF9E5C /* RNSVGRenderable.m in Sources */,
|
||||||
|
10B898E11CE490FC003CD3D4 /* UIBezierPath-Points.m in Sources */,
|
||||||
0CF68B101AF0549300FF9E5C /* RCTConvert+RNSVG.m in Sources */,
|
0CF68B101AF0549300FF9E5C /* RCTConvert+RNSVG.m in Sources */,
|
||||||
10C0686A1CCF1061007C6982 /* RNSVGShapeManager.m in Sources */,
|
10C0686A1CCF1061007C6982 /* RNSVGShapeManager.m in Sources */,
|
||||||
108FD88C1CDAF09B00A65FB3 /* RNSVGImageManager.m in Sources */,
|
108FD88C1CDAF09B00A65FB3 /* RNSVGImageManager.m in Sources */,
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
{
|
{
|
||||||
CGMutablePathRef path = CGPathCreateMutable();
|
CGMutablePathRef path = CGPathCreateMutable();
|
||||||
for (RNSVGNode *node in self.subviews) {
|
for (RNSVGNode *node in self.subviews) {
|
||||||
CGPathAddPath(path, nil, [node getPath:context]);
|
CGAffineTransform transform = node.transform;
|
||||||
|
CGPathAddPath(path, &transform, [node getPath:context]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "UIBezierPath-Points.h"
|
||||||
#import "RNSVGPath.h"
|
#import "RNSVGPath.h"
|
||||||
#import "RNSVGTextFrame.h"
|
#import "RNSVGTextFrame.h"
|
||||||
#import "RNSVGGlyphCache.h"
|
#import "RNSVGGlyphCache.h"
|
||||||
@@ -16,5 +16,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;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -39,15 +39,25 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
|
|||||||
_textFrame = frame;
|
_textFrame = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setPath:(CGPathRef)path
|
||||||
|
{
|
||||||
|
if (path == _path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[self invalidate];
|
||||||
|
CGPathRelease(_path);
|
||||||
|
_path = CGPathRetain(path);
|
||||||
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
|
CGPathRelease(_path);
|
||||||
RNSVGFreeTextFrame(_textFrame);
|
RNSVGFreeTextFrame(_textFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)renderLayerTo:(CGContextRef)context
|
- (void)renderLayerTo:(CGContextRef)context
|
||||||
{
|
{
|
||||||
self.d = [self getPath: context];
|
self.d = [self getPath: context];
|
||||||
|
|
||||||
[super renderLayerTo:context];
|
[super renderLayerTo:context];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,15 +65,15 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
|
|||||||
{
|
{
|
||||||
CGMutablePathRef path = CGPathCreateMutable();
|
CGMutablePathRef path = CGPathCreateMutable();
|
||||||
RNSVGTextFrame frame = self.textFrame;
|
RNSVGTextFrame frame = self.textFrame;
|
||||||
|
|
||||||
for (int i = 0; i < frame.count; i++) {
|
for (int i = 0; i < frame.count; i++) {
|
||||||
CGFloat shift;
|
CGFloat shift;
|
||||||
|
CGFloat width = frame.widths[i];
|
||||||
switch (self.alignment) {
|
switch (self.alignment) {
|
||||||
case kCTTextAlignmentRight:
|
case kCTTextAlignmentRight:
|
||||||
shift = frame.widths[i];
|
shift = width;
|
||||||
break;
|
break;
|
||||||
case kCTTextAlignmentCenter:
|
case kCTTextAlignmentCenter:
|
||||||
shift = (frame.widths[i] / 2);
|
shift = width / 2;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
shift = 0;
|
shift = 0;
|
||||||
@@ -71,7 +81,7 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
|
|||||||
}
|
}
|
||||||
// We should consider snapping this shift to device pixels to improve rendering quality
|
// We should consider snapping this shift to device pixels to improve rendering quality
|
||||||
// when a line has subpixel width.
|
// when a line has subpixel width.
|
||||||
CGAffineTransform offset = CGAffineTransformMakeTranslation(-shift, frame.baseLine + frame.lineHeight * i);
|
CGAffineTransform offset = CGAffineTransformMakeTranslation(-shift, frame.baseLine + frame.lineHeight * i + (self.path == NULL ? 0 : -frame.lineHeight));
|
||||||
CGPathAddPath(path, &offset, [self setLinePath:frame.lines[i]]);
|
CGPathAddPath(path, &offset, [self setLinePath:frame.lines[i]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +90,7 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
|
|||||||
|
|
||||||
- (CGPathRef)setLinePath:(CTLineRef)line
|
- (CGPathRef)setLinePath:(CTLineRef)line
|
||||||
{
|
{
|
||||||
|
|
||||||
CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0);
|
CGAffineTransform upsideDown = CGAffineTransformMakeScale(1.0, -1.0);
|
||||||
CGMutablePathRef path = CGPathCreateMutable();
|
CGMutablePathRef path = CGPathCreateMutable();
|
||||||
RNSVGGlyphCache *cache = [[RNSVGGlyphCache alloc] init];
|
RNSVGGlyphCache *cache = [[RNSVGGlyphCache alloc] init];
|
||||||
@@ -101,12 +112,31 @@ static void RNSVGFreeTextFrame(RNSVGTextFrame frame)
|
|||||||
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) {
|
for(CFIndex j = 0; j < runGlyphCount; ++j, ++glyphIndex) {
|
||||||
CGPathRef letter = [cache pathForGlyph:glyphs[j] fromFont:runFont];
|
CGPathRef letter = [cache pathForGlyph:glyphs[j] fromFont:runFont];
|
||||||
CGPoint point = positions[j];
|
CGPoint point = positions[j];
|
||||||
if (letter != NULL) {
|
if (letter != NULL) {
|
||||||
CGAffineTransform transform = CGAffineTransformTranslate(upsideDown, point.x, point.y);
|
CGAffineTransform transform;
|
||||||
|
|
||||||
|
// draw glyphs along path
|
||||||
|
if (self.path != NULL) {
|
||||||
|
CGPoint slope;
|
||||||
|
CGRect bounding = CGPathGetBoundingBox(letter);
|
||||||
|
UIBezierPath* path = [UIBezierPath bezierPathWithCGPath:self.path];
|
||||||
|
CGFloat percentConsumed = (point.x + bounding.size.width) / path.length;
|
||||||
|
if (percentConsumed >= 1.0f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGPoint targetPoint = [path pointAtPercent:percentConsumed withSlope: &slope];
|
||||||
|
float angle = atan(slope.y / slope.x); // + M_PI;
|
||||||
|
if (slope.x < 0) angle += M_PI; // going left, update the angle
|
||||||
|
transform = CGAffineTransformMakeTranslation(targetPoint.x - bounding.size.width, targetPoint.y);
|
||||||
|
transform = CGAffineTransformRotate(transform, angle);
|
||||||
|
transform = CGAffineTransformScale(transform, 1.0, -1.0);
|
||||||
|
} else {
|
||||||
|
transform = CGAffineTransformTranslate(upsideDown, point.x, point.y);
|
||||||
|
}
|
||||||
CGPathAddPath(path, &transform, letter);
|
CGPathAddPath(path, &transform, letter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
ios/UIBezierPath-Points.h
Normal file
27
ios/UIBezierPath-Points.h
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
||||||
219
ios/UIBezierPath-Points.m
Normal file
219
ios/UIBezierPath-Points.m
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
int 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
|
||||||
@@ -22,5 +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)
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
Reference in New Issue
Block a user