@lugg/maps 0.2.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/LuggMaps.podspec +23 -0
- package/README.md +119 -0
- package/android/build.gradle +78 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/luggmaps/LuggMapsGoogleMapView.kt +438 -0
- package/android/src/main/java/com/luggmaps/LuggMapsGoogleMapViewManager.kt +144 -0
- package/android/src/main/java/com/luggmaps/LuggMapsMarkerView.kt +134 -0
- package/android/src/main/java/com/luggmaps/LuggMapsMarkerViewManager.kt +69 -0
- package/android/src/main/java/com/luggmaps/LuggMapsPackage.kt +13 -0
- package/android/src/main/java/com/luggmaps/LuggMapsPolylineView.kt +79 -0
- package/android/src/main/java/com/luggmaps/LuggMapsPolylineViewManager.kt +71 -0
- package/android/src/main/java/com/luggmaps/LuggMapsWrapperView.kt +39 -0
- package/android/src/main/java/com/luggmaps/LuggMapsWrapperViewManager.kt +25 -0
- package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +180 -0
- package/android/src/main/java/com/luggmaps/events/CameraIdleEvent.kt +21 -0
- package/android/src/main/java/com/luggmaps/events/CameraMoveEvent.kt +28 -0
- package/app.plugin.js +1 -0
- package/ios/LuggMapsAppleMapView.h +16 -0
- package/ios/LuggMapsAppleMapView.mm +544 -0
- package/ios/LuggMapsGoogleMapView.h +13 -0
- package/ios/LuggMapsGoogleMapView.mm +439 -0
- package/ios/LuggMapsMarkerView.h +29 -0
- package/ios/LuggMapsMarkerView.mm +154 -0
- package/ios/LuggMapsPolylineView.h +27 -0
- package/ios/LuggMapsPolylineView.mm +116 -0
- package/ios/LuggMapsWrapperView.h +9 -0
- package/ios/LuggMapsWrapperView.mm +36 -0
- package/ios/core/GMSPolylineAnimator.h +11 -0
- package/ios/core/GMSPolylineAnimator.m +151 -0
- package/ios/core/MKPolylineAnimator.h +12 -0
- package/ios/core/MKPolylineAnimator.m +252 -0
- package/ios/core/PolylineAnimatorBase.h +22 -0
- package/ios/core/PolylineAnimatorBase.m +35 -0
- package/ios/events/CameraIdleEvent.h +24 -0
- package/ios/events/CameraMoveEvent.h +26 -0
- package/ios/extensions/MKMapView+Zoom.h +19 -0
- package/ios/extensions/MKMapView+Zoom.m +45 -0
- package/lib/module/MapView.js +87 -0
- package/lib/module/MapView.js.map +1 -0
- package/lib/module/MapView.types.js +4 -0
- package/lib/module/MapView.types.js.map +1 -0
- package/lib/module/Marker.js +34 -0
- package/lib/module/Marker.js.map +1 -0
- package/lib/module/Marker.types.js +4 -0
- package/lib/module/Marker.types.js.map +1 -0
- package/lib/module/Polyline.js +30 -0
- package/lib/module/Polyline.js.map +1 -0
- package/lib/module/Polyline.types.js +4 -0
- package/lib/module/Polyline.types.js.map +1 -0
- package/lib/module/fabric/LuggMapsAppleMapViewNativeComponent.ts +73 -0
- package/lib/module/fabric/LuggMapsGoogleMapViewNativeComponent.ts +74 -0
- package/lib/module/fabric/LuggMapsMarkerViewNativeComponent.ts +25 -0
- package/lib/module/fabric/LuggMapsPolylineViewNativeComponent.ts +19 -0
- package/lib/module/fabric/LuggMapsWrapperViewNativeComponent.ts +8 -0
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/plugin/src/index.d.ts +16 -0
- package/lib/typescript/plugin/src/index.d.ts.map +1 -0
- package/lib/typescript/plugin/src/withMapsAndroid.d.ts +6 -0
- package/lib/typescript/plugin/src/withMapsAndroid.d.ts.map +1 -0
- package/lib/typescript/plugin/src/withMapsIOS.d.ts +6 -0
- package/lib/typescript/plugin/src/withMapsIOS.d.ts.map +1 -0
- package/lib/typescript/src/MapView.d.ts +12 -0
- package/lib/typescript/src/MapView.d.ts.map +1 -0
- package/lib/typescript/src/MapView.types.d.ts +102 -0
- package/lib/typescript/src/MapView.types.d.ts.map +1 -0
- package/lib/typescript/src/Marker.d.ts +6 -0
- package/lib/typescript/src/Marker.d.ts.map +1 -0
- package/lib/typescript/src/Marker.types.d.ts +32 -0
- package/lib/typescript/src/Marker.types.d.ts.map +1 -0
- package/lib/typescript/src/Polyline.d.ts +6 -0
- package/lib/typescript/src/Polyline.d.ts.map +1 -0
- package/lib/typescript/src/Polyline.types.d.ts +24 -0
- package/lib/typescript/src/Polyline.types.d.ts.map +1 -0
- package/lib/typescript/src/fabric/LuggMapsAppleMapViewNativeComponent.d.ts +47 -0
- package/lib/typescript/src/fabric/LuggMapsAppleMapViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/LuggMapsGoogleMapViewNativeComponent.d.ts +48 -0
- package/lib/typescript/src/fabric/LuggMapsGoogleMapViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/LuggMapsMarkerViewNativeComponent.d.ts +20 -0
- package/lib/typescript/src/fabric/LuggMapsMarkerViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/LuggMapsPolylineViewNativeComponent.d.ts +15 -0
- package/lib/typescript/src/fabric/LuggMapsPolylineViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/LuggMapsWrapperViewNativeComponent.d.ts +6 -0
- package/lib/typescript/src/fabric/LuggMapsWrapperViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +8 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +28 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +200 -0
- package/plugin/build/index.d.ts +15 -0
- package/plugin/build/index.js +13 -0
- package/plugin/build/withMapsAndroid.d.ts +5 -0
- package/plugin/build/withMapsAndroid.js +15 -0
- package/plugin/build/withMapsIOS.d.ts +5 -0
- package/plugin/build/withMapsIOS.js +27 -0
- package/src/MapView.tsx +111 -0
- package/src/MapView.types.ts +110 -0
- package/src/Marker.tsx +31 -0
- package/src/Marker.types.ts +32 -0
- package/src/Polyline.tsx +32 -0
- package/src/Polyline.types.ts +24 -0
- package/src/fabric/LuggMapsAppleMapViewNativeComponent.ts +73 -0
- package/src/fabric/LuggMapsGoogleMapViewNativeComponent.ts +74 -0
- package/src/fabric/LuggMapsMarkerViewNativeComponent.ts +25 -0
- package/src/fabric/LuggMapsPolylineViewNativeComponent.ts +19 -0
- package/src/fabric/LuggMapsWrapperViewNativeComponent.ts +8 -0
- package/src/index.ts +13 -0
- package/src/types.ts +30 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#import "LuggMapsPolylineView.h"
|
|
2
|
+
|
|
3
|
+
#import <react/renderer/components/RNMapsSpec/ComponentDescriptors.h>
|
|
4
|
+
#import <react/renderer/components/RNMapsSpec/EventEmitters.h>
|
|
5
|
+
#import <react/renderer/components/RNMapsSpec/Props.h>
|
|
6
|
+
#import <react/renderer/components/RNMapsSpec/RCTComponentViewHelpers.h>
|
|
7
|
+
|
|
8
|
+
#import "RCTFabricComponentsPlugins.h"
|
|
9
|
+
#import <React/RCTConversions.h>
|
|
10
|
+
|
|
11
|
+
using namespace facebook::react;
|
|
12
|
+
|
|
13
|
+
@interface LuggMapsPolylineView () <RCTLuggMapsPolylineViewViewProtocol>
|
|
14
|
+
@end
|
|
15
|
+
|
|
16
|
+
@implementation LuggMapsPolylineView {
|
|
17
|
+
NSArray<CLLocation *> *_coordinates;
|
|
18
|
+
NSArray<UIColor *> *_strokeColors;
|
|
19
|
+
BOOL _animated;
|
|
20
|
+
CGFloat _strokeWidth;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
24
|
+
return concreteComponentDescriptorProvider<
|
|
25
|
+
LuggMapsPolylineViewComponentDescriptor>();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
29
|
+
if (self = [super initWithFrame:frame]) {
|
|
30
|
+
static const auto defaultProps =
|
|
31
|
+
std::make_shared<const LuggMapsPolylineViewProps>();
|
|
32
|
+
_props = defaultProps;
|
|
33
|
+
|
|
34
|
+
_coordinates = @[];
|
|
35
|
+
_strokeColors = @[ [UIColor blackColor] ];
|
|
36
|
+
_strokeWidth = 1.0;
|
|
37
|
+
|
|
38
|
+
self.hidden = YES;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return self;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
- (void)updateProps:(Props::Shared const &)props
|
|
45
|
+
oldProps:(Props::Shared const &)oldProps {
|
|
46
|
+
[super updateProps:props oldProps:oldProps];
|
|
47
|
+
const auto &newViewProps =
|
|
48
|
+
*std::static_pointer_cast<LuggMapsPolylineViewProps const>(props);
|
|
49
|
+
|
|
50
|
+
NSMutableArray<CLLocation *> *coords = [NSMutableArray array];
|
|
51
|
+
for (const auto &coord : newViewProps.coordinates) {
|
|
52
|
+
CLLocation *location =
|
|
53
|
+
[[CLLocation alloc] initWithLatitude:coord.latitude
|
|
54
|
+
longitude:coord.longitude];
|
|
55
|
+
[coords addObject:location];
|
|
56
|
+
}
|
|
57
|
+
_coordinates = [coords copy];
|
|
58
|
+
|
|
59
|
+
NSMutableArray<UIColor *> *colors = [NSMutableArray array];
|
|
60
|
+
for (const auto &color : newViewProps.strokeColors) {
|
|
61
|
+
UIColor *uiColor = RCTUIColorFromSharedColor(color);
|
|
62
|
+
if (uiColor) {
|
|
63
|
+
[colors addObject:uiColor];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
NSArray<UIColor *> *newColors =
|
|
67
|
+
colors.count > 0 ? [colors copy] : @[ [UIColor blackColor] ];
|
|
68
|
+
if (![newColors isEqualToArray:_strokeColors]) {
|
|
69
|
+
_strokeColors = newColors;
|
|
70
|
+
self.cachedSpans = nil;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_animated = newViewProps.animated;
|
|
74
|
+
|
|
75
|
+
_strokeWidth = newViewProps.strokeWidth > 0 ? newViewProps.strokeWidth : 1.0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
- (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask {
|
|
79
|
+
[super finalizeUpdates:updateMask];
|
|
80
|
+
|
|
81
|
+
if (updateMask & RNComponentViewUpdateMaskProps) {
|
|
82
|
+
if ([self.delegate respondsToSelector:@selector(polylineViewDidUpdate:)]) {
|
|
83
|
+
[self.delegate polylineViewDidUpdate:self];
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
- (NSArray<CLLocation *> *)coordinates {
|
|
89
|
+
return _coordinates;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
- (NSArray<UIColor *> *)strokeColors {
|
|
93
|
+
return _strokeColors;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
- (BOOL)animated {
|
|
97
|
+
return _animated;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
- (CGFloat)strokeWidth {
|
|
101
|
+
return _strokeWidth;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
- (void)prepareForRecycle {
|
|
105
|
+
[super prepareForRecycle];
|
|
106
|
+
self.polyline = nil;
|
|
107
|
+
self.renderer = nil;
|
|
108
|
+
self.cachedSpans = nil;
|
|
109
|
+
self.delegate = nil;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
Class<RCTComponentViewProtocol> LuggMapsPolylineViewCls(void) {
|
|
113
|
+
return LuggMapsPolylineView.class;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#import "LuggMapsWrapperView.h"
|
|
2
|
+
|
|
3
|
+
#import <react/renderer/components/RNMapsSpec/ComponentDescriptors.h>
|
|
4
|
+
#import <react/renderer/components/RNMapsSpec/EventEmitters.h>
|
|
5
|
+
#import <react/renderer/components/RNMapsSpec/Props.h>
|
|
6
|
+
#import <react/renderer/components/RNMapsSpec/RCTComponentViewHelpers.h>
|
|
7
|
+
|
|
8
|
+
#import "RCTFabricComponentsPlugins.h"
|
|
9
|
+
|
|
10
|
+
using namespace facebook::react;
|
|
11
|
+
|
|
12
|
+
@interface LuggMapsWrapperView () <RCTLuggMapsWrapperViewViewProtocol>
|
|
13
|
+
@end
|
|
14
|
+
|
|
15
|
+
@implementation LuggMapsWrapperView
|
|
16
|
+
|
|
17
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
18
|
+
return concreteComponentDescriptorProvider<
|
|
19
|
+
LuggMapsWrapperViewComponentDescriptor>();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
23
|
+
if (self = [super initWithFrame:frame]) {
|
|
24
|
+
static const auto defaultProps =
|
|
25
|
+
std::make_shared<const LuggMapsWrapperViewProps>();
|
|
26
|
+
_props = defaultProps;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return self;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Class<RCTComponentViewProtocol> LuggMapsWrapperViewCls(void) {
|
|
33
|
+
return LuggMapsWrapperView.class;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#import "PolylineAnimatorBase.h"
|
|
2
|
+
#import <GoogleMaps/GoogleMaps.h>
|
|
3
|
+
|
|
4
|
+
@interface GMSPolylineAnimator : PolylineAnimatorBase <PolylineAnimator>
|
|
5
|
+
|
|
6
|
+
@property(nonatomic, weak) GMSPolyline *polyline;
|
|
7
|
+
@property(nonatomic, assign) BOOL animated;
|
|
8
|
+
|
|
9
|
+
- (void)update;
|
|
10
|
+
|
|
11
|
+
@end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#import "GMSPolylineAnimator.h"
|
|
2
|
+
#import <QuartzCore/QuartzCore.h>
|
|
3
|
+
|
|
4
|
+
@implementation GMSPolylineAnimator {
|
|
5
|
+
CADisplayLink *_displayLink;
|
|
6
|
+
CGFloat _animationProgress;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
- (void)dealloc {
|
|
10
|
+
[self stopAnimation];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
- (void)setAnimated:(BOOL)animated {
|
|
14
|
+
if (_animated == animated) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
_animated = animated;
|
|
18
|
+
|
|
19
|
+
if (_animated) {
|
|
20
|
+
[self startAnimation];
|
|
21
|
+
} else {
|
|
22
|
+
[self stopAnimation];
|
|
23
|
+
[self update];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
- (void)startAnimation {
|
|
28
|
+
if (_displayLink) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
_animationProgress = 0;
|
|
32
|
+
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animationTick:)];
|
|
33
|
+
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
- (void)stopAnimation {
|
|
37
|
+
[_displayLink invalidate];
|
|
38
|
+
_displayLink = nil;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
- (void)animationTick:(CADisplayLink *)displayLink {
|
|
42
|
+
CGFloat speed = displayLink.duration / 1.75;
|
|
43
|
+
_animationProgress += speed;
|
|
44
|
+
|
|
45
|
+
if (_animationProgress >= 2.15) {
|
|
46
|
+
_animationProgress = 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
[self updateAnimatedPolyline];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
- (void)update {
|
|
53
|
+
if (_animated) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!_polyline || self.coordinates.count < 2) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
GMSMutablePath *path = [GMSMutablePath path];
|
|
62
|
+
for (CLLocation *location in self.coordinates) {
|
|
63
|
+
[path addCoordinate:location.coordinate];
|
|
64
|
+
}
|
|
65
|
+
_polyline.path = path;
|
|
66
|
+
|
|
67
|
+
if (self.strokeColors.count > 1) {
|
|
68
|
+
_polyline.spans = [self createGradientSpans];
|
|
69
|
+
} else {
|
|
70
|
+
_polyline.strokeColor = self.strokeColors.firstObject ?: [UIColor blackColor];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
- (void)updateAnimatedPolyline {
|
|
75
|
+
if (!_polyline || self.coordinates.count < 2) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
NSUInteger segmentCount = self.coordinates.count - 1;
|
|
80
|
+
CGFloat progress = MIN(_animationProgress, 2.0);
|
|
81
|
+
CGFloat headPos, tailPos;
|
|
82
|
+
|
|
83
|
+
if (progress <= 1.0) {
|
|
84
|
+
tailPos = 0;
|
|
85
|
+
headPos = progress * segmentCount;
|
|
86
|
+
} else {
|
|
87
|
+
CGFloat shrinkProgress = progress - 1.0;
|
|
88
|
+
tailPos = shrinkProgress * segmentCount;
|
|
89
|
+
headPos = segmentCount;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (headPos <= tailPos) {
|
|
93
|
+
_polyline.path = [GMSMutablePath path];
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
NSUInteger startIndex = (NSUInteger)floor(tailPos);
|
|
98
|
+
NSUInteger endIndex = (NSUInteger)ceil(headPos);
|
|
99
|
+
CGFloat visibleLength = headPos - tailPos;
|
|
100
|
+
|
|
101
|
+
GMSMutablePath *path = [GMSMutablePath path];
|
|
102
|
+
NSMutableArray<GMSStyleSpan *> *spans = [NSMutableArray array];
|
|
103
|
+
|
|
104
|
+
for (NSUInteger i = startIndex; i <= endIndex && i < self.coordinates.count; i++) {
|
|
105
|
+
CLLocationCoordinate2D coord = self.coordinates[i].coordinate;
|
|
106
|
+
|
|
107
|
+
if (i == startIndex && tailPos > (CGFloat)startIndex) {
|
|
108
|
+
CGFloat t = tailPos - (CGFloat)startIndex;
|
|
109
|
+
CLLocationCoordinate2D nextCoord = self.coordinates[i + 1].coordinate;
|
|
110
|
+
coord.latitude = coord.latitude + (nextCoord.latitude - coord.latitude) * t;
|
|
111
|
+
coord.longitude = coord.longitude + (nextCoord.longitude - coord.longitude) * t;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (i == endIndex && headPos < (CGFloat)endIndex && i > 0) {
|
|
115
|
+
CGFloat t = headPos - (CGFloat)(endIndex - 1);
|
|
116
|
+
CLLocationCoordinate2D prevCoord = self.coordinates[i - 1].coordinate;
|
|
117
|
+
coord.latitude = prevCoord.latitude + (coord.latitude - prevCoord.latitude) * t;
|
|
118
|
+
coord.longitude = prevCoord.longitude + (coord.longitude - prevCoord.longitude) * t;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
[path addCoordinate:coord];
|
|
122
|
+
|
|
123
|
+
if (i < endIndex && i < segmentCount) {
|
|
124
|
+
CGFloat segStartPos = MAX((CGFloat)i, tailPos);
|
|
125
|
+
CGFloat segEndPos = MIN((CGFloat)(i + 1), headPos);
|
|
126
|
+
CGFloat gradientMid = ((segStartPos + segEndPos) / 2.0 - tailPos) / visibleLength;
|
|
127
|
+
UIColor *color = [self colorAtGradientPosition:gradientMid];
|
|
128
|
+
GMSStrokeStyle *style = [GMSStrokeStyle solidColor:color];
|
|
129
|
+
[spans addObject:[GMSStyleSpan spanWithStyle:style]];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
_polyline.path = path;
|
|
134
|
+
_polyline.spans = spans;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
- (NSArray<GMSStyleSpan *> *)createGradientSpans {
|
|
138
|
+
NSMutableArray<GMSStyleSpan *> *spans = [NSMutableArray array];
|
|
139
|
+
NSUInteger segmentCount = self.coordinates.count - 1;
|
|
140
|
+
|
|
141
|
+
for (NSUInteger i = 0; i < segmentCount; i++) {
|
|
142
|
+
CGFloat position = (CGFloat)i / (CGFloat)segmentCount;
|
|
143
|
+
UIColor *color = [self colorAtGradientPosition:position];
|
|
144
|
+
GMSStrokeStyle *style = [GMSStrokeStyle solidColor:color];
|
|
145
|
+
[spans addObject:[GMSStyleSpan spanWithStyle:style]];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return spans;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#import <MapKit/MapKit.h>
|
|
2
|
+
|
|
3
|
+
@interface MKPolylineAnimator : MKOverlayPathRenderer
|
|
4
|
+
|
|
5
|
+
- (id)initWithPolyline:(MKPolyline *)polyline;
|
|
6
|
+
|
|
7
|
+
@property(nonatomic, strong) NSArray<UIColor *> *strokeColors;
|
|
8
|
+
@property(nonatomic, assign) BOOL animated;
|
|
9
|
+
|
|
10
|
+
- (void)updatePolyline:(MKPolyline *)polyline;
|
|
11
|
+
|
|
12
|
+
@end
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#import "MKPolylineAnimator.h"
|
|
2
|
+
#import <QuartzCore/QuartzCore.h>
|
|
3
|
+
|
|
4
|
+
@implementation MKPolylineAnimator {
|
|
5
|
+
MKPolyline *_polyline;
|
|
6
|
+
CADisplayLink *_displayLink;
|
|
7
|
+
CGFloat _animationProgress; // 0→1 grow, 1→2 shrink
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
- (id)initWithPolyline:(MKPolyline *)polyline {
|
|
11
|
+
self = [super initWithOverlay:polyline];
|
|
12
|
+
if (self) {
|
|
13
|
+
_polyline = polyline;
|
|
14
|
+
_animationProgress = 0;
|
|
15
|
+
[self createPath];
|
|
16
|
+
}
|
|
17
|
+
return self;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
- (void)dealloc {
|
|
21
|
+
[self stopAnimation];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
- (void)setAnimated:(BOOL)animated {
|
|
25
|
+
if (_animated == animated) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
_animated = animated;
|
|
29
|
+
|
|
30
|
+
if (_animated) {
|
|
31
|
+
[self startAnimation];
|
|
32
|
+
} else {
|
|
33
|
+
[self stopAnimation];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
- (void)startAnimation {
|
|
38
|
+
if (_displayLink) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
_animationProgress = 0;
|
|
42
|
+
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(animationTick:)];
|
|
43
|
+
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
- (void)stopAnimation {
|
|
47
|
+
[_displayLink invalidate];
|
|
48
|
+
_displayLink = nil;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
- (void)animationTick:(CADisplayLink *)displayLink {
|
|
52
|
+
// ~1.75s per phase (grow or shrink), matching JS duration
|
|
53
|
+
CGFloat speed = displayLink.duration / 1.75;
|
|
54
|
+
_animationProgress += speed;
|
|
55
|
+
|
|
56
|
+
// 0→1 grow, 1→2 shrink, then reset with small pause
|
|
57
|
+
if (_animationProgress >= 2.15) { // 2.0 + 0.15 pause (~300ms at this speed)
|
|
58
|
+
_animationProgress = 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
MKMapRect bounds = _polyline.boundingMapRect;
|
|
62
|
+
bounds = MKMapRectInset(bounds, -bounds.size.width * 0.1, -bounds.size.height * 0.1);
|
|
63
|
+
[self setNeedsDisplayInMapRect:bounds];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
- (void)updatePolyline:(MKPolyline *)polyline {
|
|
67
|
+
_polyline = polyline;
|
|
68
|
+
[self invalidatePath];
|
|
69
|
+
[self createPath];
|
|
70
|
+
[self setNeedsDisplay];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
- (void)createPath {
|
|
74
|
+
CGMutablePathRef path = CGPathCreateMutable();
|
|
75
|
+
BOOL first = YES;
|
|
76
|
+
for (NSUInteger i = 0; i < _polyline.pointCount; i++) {
|
|
77
|
+
CGPoint point = [self pointForMapPoint:_polyline.points[i]];
|
|
78
|
+
if (first) {
|
|
79
|
+
CGPathMoveToPoint(path, nil, point.x, point.y);
|
|
80
|
+
first = NO;
|
|
81
|
+
} else {
|
|
82
|
+
CGPathAddLineToPoint(path, nil, point.x, point.y);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
self.path = path;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
- (UIColor *)colorAtGradientPosition:(CGFloat)position {
|
|
89
|
+
if (!_strokeColors || _strokeColors.count == 0) {
|
|
90
|
+
return self.strokeColor;
|
|
91
|
+
}
|
|
92
|
+
if (_strokeColors.count == 1) {
|
|
93
|
+
return _strokeColors[0];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
position = MAX(0, MIN(1, position));
|
|
97
|
+
CGFloat scaledPos = position * (_strokeColors.count - 1);
|
|
98
|
+
NSUInteger index = (NSUInteger)floor(scaledPos);
|
|
99
|
+
CGFloat t = scaledPos - index;
|
|
100
|
+
|
|
101
|
+
if (index >= _strokeColors.count - 1) {
|
|
102
|
+
return _strokeColors.lastObject;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
UIColor *c1 = _strokeColors[index];
|
|
106
|
+
UIColor *c2 = _strokeColors[index + 1];
|
|
107
|
+
|
|
108
|
+
CGFloat r1, g1, b1, a1, r2, g2, b2, a2;
|
|
109
|
+
[c1 getRed:&r1 green:&g1 blue:&b1 alpha:&a1];
|
|
110
|
+
[c2 getRed:&r2 green:&g2 blue:&b2 alpha:&a2];
|
|
111
|
+
|
|
112
|
+
return [UIColor colorWithRed:r1 + (r2 - r1) * t
|
|
113
|
+
green:g1 + (g2 - g1) * t
|
|
114
|
+
blue:b1 + (b2 - b1) * t
|
|
115
|
+
alpha:a1 + (a2 - a1) * t];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
- (void)drawMapRect:(MKMapRect)mapRect
|
|
119
|
+
zoomScale:(MKZoomScale)zoomScale
|
|
120
|
+
inContext:(CGContextRef)context {
|
|
121
|
+
CGRect pointsRect = CGPathGetBoundingBox(self.path);
|
|
122
|
+
CGRect mapRectCG = [self rectForMapRect:mapRect];
|
|
123
|
+
if (!CGRectIntersectsRect(pointsRect, mapRectCG)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
CGFloat lineWidth = self.lineWidth / zoomScale;
|
|
128
|
+
CGContextSetLineWidth(context, lineWidth);
|
|
129
|
+
CGContextSetLineCap(context, self.lineCap);
|
|
130
|
+
CGContextSetLineJoin(context, self.lineJoin);
|
|
131
|
+
|
|
132
|
+
NSUInteger segmentCount = _polyline.pointCount - 1;
|
|
133
|
+
if (segmentCount == 0) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Snake animation: grow from start, then shrink from start
|
|
138
|
+
if (_animated && _polyline.pointCount > 1) {
|
|
139
|
+
CGFloat progress = MIN(_animationProgress, 2.0);
|
|
140
|
+
CGFloat headPos, tailPos;
|
|
141
|
+
|
|
142
|
+
if (progress <= 1.0) {
|
|
143
|
+
// Phase 1: grow from start to end
|
|
144
|
+
tailPos = 0;
|
|
145
|
+
headPos = progress * segmentCount;
|
|
146
|
+
} else {
|
|
147
|
+
// Phase 2: shrink from start
|
|
148
|
+
CGFloat shrinkProgress = progress - 1.0;
|
|
149
|
+
tailPos = shrinkProgress * segmentCount;
|
|
150
|
+
headPos = segmentCount;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (headPos <= tailPos) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
NSUInteger startIndex = (NSUInteger)floor(tailPos);
|
|
158
|
+
NSUInteger endIndex = (NSUInteger)ceil(headPos);
|
|
159
|
+
CGFloat visibleLength = headPos - tailPos;
|
|
160
|
+
|
|
161
|
+
for (NSUInteger i = startIndex; i < endIndex && i < segmentCount; i++) {
|
|
162
|
+
CGPoint segStart = [self pointForMapPoint:_polyline.points[i]];
|
|
163
|
+
CGPoint segEnd = [self pointForMapPoint:_polyline.points[i + 1]];
|
|
164
|
+
|
|
165
|
+
CGPoint drawStart = segStart;
|
|
166
|
+
CGPoint drawEnd = segEnd;
|
|
167
|
+
CGFloat segStartPos = (CGFloat)i;
|
|
168
|
+
CGFloat segEndPos = (CGFloat)(i + 1);
|
|
169
|
+
|
|
170
|
+
// Interpolate tail (partial segment at start)
|
|
171
|
+
if (segStartPos < tailPos) {
|
|
172
|
+
CGFloat t = tailPos - segStartPos;
|
|
173
|
+
drawStart.x = segStart.x + (segEnd.x - segStart.x) * t;
|
|
174
|
+
drawStart.y = segStart.y + (segEnd.y - segStart.y) * t;
|
|
175
|
+
segStartPos = tailPos;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Interpolate head (partial segment at end)
|
|
179
|
+
if (segEndPos > headPos) {
|
|
180
|
+
CGFloat t = headPos - (CGFloat)i;
|
|
181
|
+
drawEnd.x = segStart.x + (segEnd.x - segStart.x) * t;
|
|
182
|
+
drawEnd.y = segStart.y + (segEnd.y - segStart.y) * t;
|
|
183
|
+
segEndPos = headPos;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Calculate gradient position (0-1) within visible portion
|
|
187
|
+
CGFloat gradientStart = (segStartPos - tailPos) / visibleLength;
|
|
188
|
+
CGFloat gradientEnd = (segEndPos - tailPos) / visibleLength;
|
|
189
|
+
|
|
190
|
+
UIColor *startColor = [self colorAtGradientPosition:gradientStart];
|
|
191
|
+
UIColor *endColor = [self colorAtGradientPosition:gradientEnd];
|
|
192
|
+
|
|
193
|
+
// Draw gradient line segment
|
|
194
|
+
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
195
|
+
NSArray *colors = @[(__bridge id)startColor.CGColor, (__bridge id)endColor.CGColor];
|
|
196
|
+
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, NULL);
|
|
197
|
+
|
|
198
|
+
CGContextSaveGState(context);
|
|
199
|
+
CGContextBeginPath(context);
|
|
200
|
+
CGContextMoveToPoint(context, drawStart.x, drawStart.y);
|
|
201
|
+
CGContextAddLineToPoint(context, drawEnd.x, drawEnd.y);
|
|
202
|
+
CGContextReplacePathWithStrokedPath(context);
|
|
203
|
+
CGContextClip(context);
|
|
204
|
+
|
|
205
|
+
CGContextDrawLinearGradient(context, gradient, drawStart, drawEnd, 0);
|
|
206
|
+
|
|
207
|
+
CGContextRestoreGState(context);
|
|
208
|
+
CGGradientRelease(gradient);
|
|
209
|
+
CGColorSpaceRelease(colorSpace);
|
|
210
|
+
}
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Static gradient rendering
|
|
215
|
+
if (_strokeColors && _strokeColors.count > 1) {
|
|
216
|
+
for (NSUInteger i = 0; i < segmentCount; i++) {
|
|
217
|
+
CGPoint startPoint = [self pointForMapPoint:_polyline.points[i]];
|
|
218
|
+
CGPoint endPoint = [self pointForMapPoint:_polyline.points[i + 1]];
|
|
219
|
+
|
|
220
|
+
CGFloat gradientStart = (CGFloat)i / segmentCount;
|
|
221
|
+
CGFloat gradientEnd = (CGFloat)(i + 1) / segmentCount;
|
|
222
|
+
|
|
223
|
+
UIColor *startColor = [self colorAtGradientPosition:gradientStart];
|
|
224
|
+
UIColor *endColor = [self colorAtGradientPosition:gradientEnd];
|
|
225
|
+
|
|
226
|
+
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
227
|
+
NSArray *colors = @[(__bridge id)startColor.CGColor, (__bridge id)endColor.CGColor];
|
|
228
|
+
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, NULL);
|
|
229
|
+
|
|
230
|
+
CGContextSaveGState(context);
|
|
231
|
+
CGContextBeginPath(context);
|
|
232
|
+
CGContextMoveToPoint(context, startPoint.x, startPoint.y);
|
|
233
|
+
CGContextAddLineToPoint(context, endPoint.x, endPoint.y);
|
|
234
|
+
CGContextReplacePathWithStrokedPath(context);
|
|
235
|
+
CGContextClip(context);
|
|
236
|
+
|
|
237
|
+
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
|
|
238
|
+
|
|
239
|
+
CGContextRestoreGState(context);
|
|
240
|
+
CGGradientRelease(gradient);
|
|
241
|
+
CGColorSpaceRelease(colorSpace);
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Single color
|
|
247
|
+
CGContextSetStrokeColorWithColor(context, self.strokeColor.CGColor);
|
|
248
|
+
CGContextAddPath(context, self.path);
|
|
249
|
+
CGContextStrokePath(context);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
@end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#import <CoreLocation/CoreLocation.h>
|
|
2
|
+
#import <Foundation/Foundation.h>
|
|
3
|
+
#import <UIKit/UIKit.h>
|
|
4
|
+
|
|
5
|
+
@protocol PolylineAnimator <NSObject>
|
|
6
|
+
|
|
7
|
+
@property(nonatomic, strong) NSArray<CLLocation *> *coordinates;
|
|
8
|
+
@property(nonatomic, strong) NSArray<UIColor *> *strokeColors;
|
|
9
|
+
@property(nonatomic, assign) BOOL animated;
|
|
10
|
+
|
|
11
|
+
- (void)update;
|
|
12
|
+
|
|
13
|
+
@end
|
|
14
|
+
|
|
15
|
+
@interface PolylineAnimatorBase : NSObject
|
|
16
|
+
|
|
17
|
+
@property(nonatomic, strong) NSArray<CLLocation *> *coordinates;
|
|
18
|
+
@property(nonatomic, strong) NSArray<UIColor *> *strokeColors;
|
|
19
|
+
|
|
20
|
+
- (UIColor *)colorAtGradientPosition:(CGFloat)position;
|
|
21
|
+
|
|
22
|
+
@end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#import "PolylineAnimatorBase.h"
|
|
2
|
+
|
|
3
|
+
@implementation PolylineAnimatorBase
|
|
4
|
+
|
|
5
|
+
- (UIColor *)colorAtGradientPosition:(CGFloat)position {
|
|
6
|
+
if (!_strokeColors || _strokeColors.count == 0) {
|
|
7
|
+
return [UIColor blackColor];
|
|
8
|
+
}
|
|
9
|
+
if (_strokeColors.count == 1) {
|
|
10
|
+
return _strokeColors[0];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
position = MAX(0, MIN(1, position));
|
|
14
|
+
CGFloat scaledPos = position * (_strokeColors.count - 1);
|
|
15
|
+
NSUInteger index = (NSUInteger)floor(scaledPos);
|
|
16
|
+
CGFloat t = scaledPos - index;
|
|
17
|
+
|
|
18
|
+
if (index >= _strokeColors.count - 1) {
|
|
19
|
+
return _strokeColors.lastObject;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
UIColor *c1 = _strokeColors[index];
|
|
23
|
+
UIColor *c2 = _strokeColors[index + 1];
|
|
24
|
+
|
|
25
|
+
CGFloat r1, g1, b1, a1, r2, g2, b2, a2;
|
|
26
|
+
[c1 getRed:&r1 green:&g1 blue:&b1 alpha:&a1];
|
|
27
|
+
[c2 getRed:&r2 green:&g2 blue:&b2 alpha:&a2];
|
|
28
|
+
|
|
29
|
+
return [UIColor colorWithRed:r1 + (r2 - r1) * t
|
|
30
|
+
green:g1 + (g2 - g1) * t
|
|
31
|
+
blue:b1 + (b2 - b1) * t
|
|
32
|
+
alpha:a1 + (a2 - a1) * t];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <react/renderer/components/RNMapsSpec/EventEmitters.h>
|
|
4
|
+
|
|
5
|
+
namespace luggmaps {
|
|
6
|
+
namespace events {
|
|
7
|
+
|
|
8
|
+
struct CameraIdleEvent {
|
|
9
|
+
double latitude;
|
|
10
|
+
double longitude;
|
|
11
|
+
double zoom;
|
|
12
|
+
|
|
13
|
+
template <typename Emitter>
|
|
14
|
+
void emit(std::shared_ptr<Emitter const> emitter) const {
|
|
15
|
+
typename Emitter::OnCameraIdle event;
|
|
16
|
+
event.coordinate.latitude = latitude;
|
|
17
|
+
event.coordinate.longitude = longitude;
|
|
18
|
+
event.zoom = zoom;
|
|
19
|
+
emitter->onCameraIdle(event);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
} // namespace events
|
|
24
|
+
} // namespace luggmaps
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#import <react/renderer/components/RNMapsSpec/EventEmitters.h>
|
|
4
|
+
|
|
5
|
+
namespace luggmaps {
|
|
6
|
+
namespace events {
|
|
7
|
+
|
|
8
|
+
struct CameraMoveEvent {
|
|
9
|
+
double latitude;
|
|
10
|
+
double longitude;
|
|
11
|
+
double zoom;
|
|
12
|
+
bool dragging;
|
|
13
|
+
|
|
14
|
+
template <typename Emitter>
|
|
15
|
+
void emit(std::shared_ptr<Emitter const> emitter) const {
|
|
16
|
+
typename Emitter::OnCameraMove event;
|
|
17
|
+
event.coordinate.latitude = latitude;
|
|
18
|
+
event.coordinate.longitude = longitude;
|
|
19
|
+
event.zoom = zoom;
|
|
20
|
+
event.dragging = dragging;
|
|
21
|
+
emitter->onCameraMove(event);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
} // namespace events
|
|
26
|
+
} // namespace luggmaps
|