Skip to content

Commit

Permalink
Merge pull request #8 from nammayatri/polyline-animation-integ
Browse files Browse the repository at this point in the history
Added polyline animation
  • Loading branch information
kavya-shree-s authored Oct 24, 2024
2 parents d9b5148 + f79ff3f commit fdfc805
Show file tree
Hide file tree
Showing 12 changed files with 368 additions and 5 deletions.
92 changes: 92 additions & 0 deletions android/src/main/java/com/rnmaps/maps/MapPolyline.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import android.os.Handler;
import android.os.Looper;

public class MapPolyline extends MapFeature {

private PolylineOptions polylineOptions;
private Polyline polyline;
private Polyline animatedPolyline;

private List<LatLng> coordinates;
private int color;
Expand All @@ -36,11 +41,95 @@ public class MapPolyline extends MapFeature {
private ReadableArray patternValues;
private List<PatternItem> pattern;
private StyleSpan styleSpan = null;
private int animateColor = Color.parseColor("#FFFFFF");
private Timer animationTimer;
private TimerTask animationTask;
private float drawDone = 0;

public MapPolyline(Context context) {
super(context);
}

public void startPolylineAnimation(final int animateColor, final int animationDuration, final int delay) {
stopPolylineAnimation();
this.animateColor = animateColor;

new Handler(Looper.getMainLooper()).postDelayed(() -> {
animationTimer = new Timer();
drawDone = 0;

animationTask = new TimerTask() {
@Override
public void run() {
if (drawDone <= 28) {
drawDone += 2;
} else if (drawDone <= 66) {
drawDone += 4;
} else if (drawDone <= 98) {
drawDone += 2;
} else if (drawDone <= 200) {
drawDone += 2;
} else {
drawDone = 0;
}

new Handler(Looper.getMainLooper()).post(() -> updatePolyline());
}
};

animationTimer.schedule(animationTask, 200, animationDuration);
}, delay);
}

public void stopPolylineAnimation() {
if (animationTimer != null) {
animationTimer.cancel();
animationTimer = null;
}
if (animationTask != null) {
animationTask.cancel();
animationTask = null;
}
}

private void updatePolyline() {
if (animatedPolyline == null || polyline == null || coordinates == null || coordinates.isEmpty()) return;

if (drawDone >= 0 && drawDone <= 100) {
int pointCount = coordinates.size();
int countToAdd = (int) (pointCount * (drawDone / 100.0f));

List<LatLng> updatedPoints = new ArrayList<>(coordinates.subList(0, countToAdd));

animatedPolyline.setPoints(updatedPoints);
animatedPolyline.setColor(animateColor);
animatedPolyline.setVisible(true);

polyline.setVisible(true);
}
else if (drawDone > 100 && drawDone <= 200) {
float alpha = (drawDone - 100.0f) / 100.0f;
int newColor = fadeColorToStatic(animateColor, color, alpha);
animatedPolyline.setColor(newColor);

if (drawDone == 200) {
polyline.setVisible(true);
animatedPolyline.setVisible(false);
drawDone = 0;
}
}
}

private int fadeColorToStatic(int animateColor, int staticColor, float fraction) {
fraction = Math.max(0, Math.min(fraction, 1));
int fromAlpha = (int) (255 * (1 - fraction));
int fadedAnimateColor = Color.argb(fromAlpha, Color.red(animateColor), Color.green(animateColor), Color.blue(animateColor));
if (fraction >= 1) {
return staticColor;
}
return fadedAnimateColor;
}

public void setCoordinates(ReadableArray coordinates) {
this.coordinates = new ArrayList<>(coordinates.size());
for (int i = 0; i < coordinates.size(); i++) {
Expand Down Expand Up @@ -166,12 +255,15 @@ public Object getFeature() {
public void addToMap(Object collection) {
PolylineManager.Collection polylineCollection = (PolylineManager.Collection) collection;
polyline = polylineCollection.addPolyline(getPolylineOptions());
animatedPolyline = polylineCollection.addPolyline(new PolylineOptions().color(animateColor).width(width));
polyline.setClickable(this.tappable);
}

@Override
public void removeFromMap(Object collection) {
PolylineManager.Collection polylineCollection = (PolylineManager.Collection) collection;
stopPolylineAnimation();
polylineCollection.remove(animatedPolyline);
polylineCollection.remove(polyline);
}
}
24 changes: 24 additions & 0 deletions android/src/main/java/com/rnmaps/maps/MapPolylineManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.util.DisplayMetrics;
import android.view.WindowManager;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.ReactApplicationContext;
Expand Down Expand Up @@ -102,6 +103,29 @@ public void setStrokeColors(MapPolyline view, ReadableArray colors){
view.setStrokeColors(colors);
}

@Override
public void receiveCommand(@NonNull MapPolyline view, String commandId, @Nullable ReadableArray args) {
int animateColor;
int animationDuration;
int delay;

switch (commandId) {
case "startPolylineAnimation":
if (args == null) {
return;
}
animateColor = Color.parseColor(args.getString(0));
animationDuration = args.getInt(1);
delay = args.getInt(2);
view.startPolylineAnimation(animateColor, animationDuration, delay);
break;

case "stopPolylineAnimation":
view.stopPolylineAnimation();
break;
}
}

@Override
@Nullable
public Map getExportedCustomDirectEventTypeConstants() {
Expand Down
2 changes: 2 additions & 0 deletions ios/AirGoogleMaps/AIRGoogleMap.m
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ - (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex
} else if ([subview isKindOfClass:[AIRGoogleMapPolyline class]]) {
AIRGoogleMapPolyline *polyline = (AIRGoogleMapPolyline*)subview;
polyline.polyline.map = self;
polyline.animatedPolyline.map = self;
[self.polylines addObject:polyline];
} else if ([subview isKindOfClass:[AIRGoogleMapCircle class]]) {
AIRGoogleMapCircle *circle = (AIRGoogleMapCircle*)subview;
Expand Down Expand Up @@ -205,6 +206,7 @@ - (void)removeReactSubview:(id<RCTComponent>)subview {
} else if ([subview isKindOfClass:[AIRGoogleMapPolyline class]]) {
AIRGoogleMapPolyline *polyline = (AIRGoogleMapPolyline*)subview;
polyline.polyline.map = nil;
polyline.animatedPolyline.map = nil;
[self.polylines removeObject:polyline];
} else if ([subview isKindOfClass:[AIRGoogleMapCircle class]]) {
AIRGoogleMapCircle *circle = (AIRGoogleMapCircle*)subview;
Expand Down
9 changes: 9 additions & 0 deletions ios/AirGoogleMaps/AIRGoogleMapPolyline.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@
@property (nonatomic, assign) int zIndex;
@property (nonatomic, assign) BOOL tappable;

@property (nonatomic, strong) AIRGMSPolyline *animatedPolyline;
@property (nonatomic, strong) NSTimer *animationTimer;
@property (nonatomic) CGFloat drawDone;
@property (nonatomic, strong) UIColor *animateColor;
@property (nonatomic) BOOL isAnimating;

-(void)startPolylineAnimation:(UIColor *)animateColor animationDuration:(CGFloat)animationDuration delay:(CGFloat)delay;
-(void)stopPolylineAnimation;

@end

#endif
93 changes: 93 additions & 0 deletions ios/AirGoogleMaps/AIRGoogleMapPolyline.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ - (instancetype)init
{
if (self = [super init]) {
_polyline = [[AIRGMSPolyline alloc] init];
_animatedPolyline = [[AIRGMSPolyline alloc] init];
_drawDone = 0;
}
return self;
}
Expand All @@ -33,6 +35,7 @@ -(void)setCoordinates:(NSArray<AIRMapCoordinate *> *)coordinates
if (!coordinates || coordinates.count == 0)
{
_polyline.map = nil; // Remove polyline from the map
[self stopPolylineAnimation];
return;
}

Expand Down Expand Up @@ -84,6 +87,7 @@ -(void)setStrokeWidth:(double)strokeWidth
{
_strokeWidth = strokeWidth;
_polyline.strokeWidth = strokeWidth;
_animatedPolyline.strokeWidth = strokeWidth;
}

-(void)setFillColor:(UIColor *)fillColor
Expand Down Expand Up @@ -144,6 +148,95 @@ - (void)configureStyleSpansIfNeeded {
_polyline.spans = GMSStyleSpans(_polyline.path, styles, _lineDashPattern, kGMSLengthRhumb);
}

-(void)startPolylineAnimation:(UIColor *)animateColor animationDuration:(CGFloat)animationDuration delay:(CGFloat)delay {

[self stopPolylineAnimation];
_animateColor = animateColor;
_drawDone = 0;
_isAnimating = YES;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay / 1000.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationDuration / 1000.0
target:self
selector:@selector(updatePolylineAnimation)
userInfo:nil
repeats:YES];
});

}

-(void)stopPolylineAnimation {
[self.animationTimer invalidate];
self.animationTimer = nil;
_isAnimating = NO;
_animatedPolyline.map = nil;
}

-(void)updatePolylineAnimation {
_animatedPolyline.map = _polyline.map;
if (!_isAnimating || _coordinates.count == 0) return;

if (_drawDone <= 28) {
_drawDone += 2;
} else if (_drawDone <= 66) {
_drawDone += 4;
} else if (_drawDone <= 98) {
_drawDone += 2;
} else if (_drawDone <= 200) {
_drawDone += 2;
} else {
_drawDone = 0;
}

[self updatePolylineWithAnimateColor:_animateColor];
}

-(void)updatePolylineWithAnimateColor:(UIColor *)staticColor {

if (_animatedPolyline == nil || _polyline == nil || _coordinates.count == 0) return;

if (_drawDone >= 0 && _drawDone <= 100) {
NSInteger pointCount = _coordinates.count;
NSInteger countToAdd = (NSInteger)(pointCount * (_drawDone / 100.0));
if (countToAdd > pointCount) countToAdd = pointCount;

GMSMutablePath *path = [GMSMutablePath path];

for (NSInteger i = 0; i < countToAdd; i++) {

[path addCoordinate:_coordinates[i].coordinate];
}

_animatedPolyline.path = path;
_animatedPolyline.strokeColor = _animateColor;


} else if (_drawDone > 100 && _drawDone <= 200) {
float alpha = (_drawDone - 100.0f) / 100.0f;
UIColor *newColor = [self fadeColorToStatic:_animateColor staticColor:_strokeColor fraction:alpha];
_animatedPolyline.strokeColor = newColor;

if (_drawDone == 200) {
_drawDone = 0;
}
}
}

-(UIColor *)fadeColorToStatic:(UIColor *)animateColor staticColor:(UIColor *)staticColor fraction:(float)fraction {
fraction = fmax(0, fmin(fraction, 1));

CGFloat r1, g1, b1, a1;
CGFloat r2, g2, b2, a2;
[animateColor getRed:&r1 green:&g1 blue:&b1 alpha:&a1];
[staticColor getRed:&r2 green:&g2 blue:&b2 alpha:&a2];

CGFloat r = r1 * (1 - fraction) + r2 * fraction;
CGFloat g = g1 * (1 - fraction) + g2 * fraction;
CGFloat b = b1 * (1 - fraction) + b2 * fraction;

return [UIColor colorWithRed:r green:g blue:b alpha:1.0];
}

@end

#endif
45 changes: 45 additions & 0 deletions ios/AirGoogleMaps/AIRGoogleMapPolylineManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#import <React/UIView+React.h>
#import "RCTConvert+AirMap.h"
#import "AIRGoogleMapPolyline.h"
#import <React/RCTUIManager.h>

@interface AIRGoogleMapPolylineManager()

Expand Down Expand Up @@ -43,6 +44,50 @@ - (UIView *)view
RCT_EXPORT_VIEW_PROPERTY(tappable, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)

RCT_EXPORT_VIEW_PROPERTY(animateColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(drawDone, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(isAnimating, BOOL)

RCT_EXPORT_METHOD(startPolylineAnimation:(nonnull NSNumber *)reactTag animateColor:(NSString *)animateColor animationDuration:(CGFloat)duration delay:(CGFloat)delay)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
id view = viewRegistry[reactTag];
if (![view isKindOfClass:[AIRGoogleMapPolyline class]]) {
RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMapPolyline, got: %@", view);
} else {
AIRGoogleMapPolyline *polyline = (AIRGoogleMapPolyline *)view;
UIColor *newColor = UIColorWithHexString(animateColor);
[polyline startPolylineAnimation:newColor animationDuration:duration delay:delay];
}
}];
}

RCT_EXPORT_METHOD(stopPolylineAnimation:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
id view = viewRegistry[reactTag];
if (![view isKindOfClass:[AIRGoogleMapPolyline class]]) {
RCTLogError(@"Invalid view returned from registry, expecting AIRGoogleMapPolyline, got: %@", view);
} else {
AIRGoogleMapPolyline *polyline = (AIRGoogleMapPolyline *)view;

[polyline stopPolylineAnimation];
}
}];
}

static UIColor * UIColorWithHexString(NSString *hex) {
unsigned int rgb = 0;
[[NSScanner scannerWithString:
[[hex uppercaseString] stringByTrimmingCharactersInSet:
[[NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEF"] invertedSet]]]
scanHexInt:&rgb];
return [UIColor colorWithRed:((CGFloat)((rgb & 0xFF0000) >> 16)) / 255.0
green:((CGFloat)((rgb & 0xFF00) >> 8)) / 255.0
blue:((CGFloat)(rgb & 0xFF)) / 255.0
alpha:1.0];
}

@end

#endif
Loading

0 comments on commit fdfc805

Please sign in to comment.