Providing Location Aware User Feedback with Animated Map Markers

Part 2 of Creating a Hot or Cold Game as a Location Aware Proof of Concept

Background

In a previous post here I built the user interface for a hotter / colder game mobile application. In this app, the user places location markers on a map and the application displays feedback as to whether the user is getting hotter or colder (closer or further away from) the makers they placed.

This post details the process of expanding the “hotter or colder” application built in the first post to include the visual feedback mentioned above.

In addition to reading my previous post, I recommend checking out the github repo here. You can also run the application by installing the Expo Client (which is downloadable from the Google or Apple App Stores) and clicking on this link: https://exp.host/@reggie3/geofencing-proof-of-concept.

Design

The desired functionality is deceptively simple:

  1. Periodically check the device’s current location
  2. Calculate distance from currently location to marker location(s)
  3. Provide feedback based on the calculated distance. The feedback will consist of markers that appear to be more “excited” the closer the device is to it.

This sounds easy enough. However, I’m old enough to be wary of the idea that coding any new technique will be easy.

Its only a few functions, right?

Implementation

The program is already writes its new location to the redux store by dispatching an event whenever its location changes. I just needed to add the ability to repeatedly and periodically calculate and store the distance between the current location and the markers. JavaScript’s setInterval function seemed like a good place to start for doing that.

I wrote the following function that is initialized in the App.js component’s componentWillMount function in order to measure and record distances as soon as the device’s current location is known.

Periodically check the current location and measure the distance from each marker

I then wrote the following reducer code that concatenates the array of previously measured distances to the current distance. This results in an array with the newest measurement as item zero. The resulting array is then sized based on a user defined setting on the settings page. Then the values in the resulting array are averaged to get a final distance. This is done so that one stray measurement doesn’t affect the what is displayed to the user.

Write distance information to the store

Custom Animated Map Markers

Animated map makers proved to be the most difficult problem in this project because of this documented issue with Android maps and animations:

“Issue: Since android needs to render its marker views as a bitmap, the animations APIs may not be compatible with the Marker views. Not sure if this can be worked around yet or not.”

This meant that animation techniques involving manipulating CSS properties would not work on Android using tools like react natives animation libraries didn’t work. Although I would have preferred to animate my markers using one of the “standard” react animation libraries, doing so would not have provided me the cross-platform solution I wanted.

However, there are ways to get around that problem. I’ll present two of them here: image swapping, and requestAnimationFrame.

Image Swapping

One technique for animating markers is image swapping. GitHub user micahrye’s react-native-animated-sprite repo contains a series of images that can be combined to look like a walking monster. I downloaded those images for use in my project. I then imported and placed them in an array in the component’s state:

The walking monster is used as the maker’s image as seen in the code below:

setInterval is called when the marker is mounted in order to periodically call a function that increments the image counter resulting in changing which image the maker displays:

Finally, a simple switch statement is used to determine how fast the monster appears to be walking. As previously stated, the distance between the marker and device is measured and written to periodically to each location object in the store. Additionally, the component is passed its location object by its parent map. This means that componentWillReceiveProps will be called each time the distance is written. This provides the perfect opportunity to check that distance, and update the timeout interval of the setTimeout function which determines the speed that the images are cycled through.

Request Animation Frame

I know it may seem inappropriate for react-native, but using requestAnimationFrame provides another technique to animating items on the map. It also has the benefit of being able to change CSS properties. This means it can be used with various react components like views and Icons.

The animation loop is initiated upon component mounting as shown below.

The animationLooper function then does multiple things including calculating the time since it was last called, determining the amount the marker should grow or shrink, and setting the new size of the icon. It then recursively calls itself using requestAnimationFrame.

The render method below shows that the size of the map maker icon is determined by the value of the component’s size state variable which is changed every iteration of animationLooper.

Finally, a switch statement similar to the image swapping technique shown above is used to determine the growth speed of the marker. The closer the device is to the marker, the higher the speed.

Performance

I did not spend much time optimizing either technique. However, I though of a couple of potential improvements that could be attempted.

First, animation using setTimeout is not optimal. Preferably, one would use requestAnimationFrame for both techniques.

Additionally, an obvious improvement for the second technique would to be create one animation loop in the MapScreen component vice separate ones in each LocationMarker component. With the requestAnimationFrame technique, stutter was obvious when more than five markers were on the screen at the same time.

Unfortunately, I think performance is the Achilles heel of both techniques and I hope that Google makes it possible to use react animations or other well tested animation libraries to animate makers on Android at some point.

However, the techniques, while not optimized, still worked as can be seen in the videos below.

If you have the Expo application installed you can easily download and run this updated version of the program from this link: https://exp.host/@reggie3/geofencing-proof-of-concept-part2

Conclusion

This article demonstrated two different techniques to display animated markers on a map created using react-native-maps: image swapping, and requestAnimationFrame. Keep in mind that the techniques were required because of the limitation with Google Maps on Android that was mentioned above. Reports on this issue indicate that it is not a problem for iOS devices, so neither of these workarounds should be required there.

I really look forward to any comments related to how to improve either of these methods to make them more efficient, visually pleasing, etc.

Reginald Johnson has maintained his passion for coding throughout his 20+ year career as an Officer in the United States Navy. He enjoys applying his training and experience in programming, Systems Engineering, and Operational Planning towards programming. Follow him Twitter @reginald3.