Clue Mediator

Animate marker on Google Maps route in React

๐Ÿ“…April 16, 2023
๐Ÿ—ReactJS

Animating markers on Google Maps route in React can make the user's experience more enjoyable and engaging. Google Maps API provides a way to animate the marker smoothly along the route. To achieve this, we can get the multiple coordinates on the routes, and then we can use the `setPosition()` method to move the marker along the path.

Demo Application

Output - Animate marker on Google Maps route in React - Clue Mediator

Output - Animate marker on Google Maps route in React - Clue Mediator

Animate marker on Google Maps route in React

  1. Project structure
  2. Package dependencies
  3. Implementation
  4. Output

1. Project structure

  • animate-marker-google-maps-route-react

    • node_modules

    • public

      • index.html
    • src

      • App.js
      • GMap.js
      • index.css
      • index.js
    • package-lock.json

    • package.json

    • README.md

2. Package dependencies

Run the following command to install @googlemaps/js-api-loader npm package.

npm i @googlemaps/js-api-loader

You will find the version of the following packages in React application.

react

^18.2.0

@googlemaps/js-api-loader

^1.15.1

3. Implementation

Refer to the following files for implementation.

GMap.js

import React, { useEffect, useRef, useState } from 'react';

const GMap = () => {
  const googleMapRef = useRef(null);
  const [map, setMap] = useState(null);

  useEffect(() => {
    const googleMap = initGoogleMap();
    setMap(googleMap);
  }, []);

  useEffect(() => {
    if (!map) return;

    const routeOptions = new window.google.maps.Polyline({
      strokeOpacity: 0,
      icons: [{
        icon: {
          path: "M 0,-0.1 0,0.1",
          strokeOpacity: 0.8,
          strokeColor: 'red',
          scale: 5,
        },
        offset: "0",
        repeat: "10px",
      }],
    });

    const directionsService = new window.google.maps.DirectionsService();
    const directionsRenderer = new window.google.maps.DirectionsRenderer({ polylineOptions: routeOptions });
    const haight = new window.google.maps.LatLng(37.7699298, -122.4469157);
    const oceanBeach = new window.google.maps.LatLng(37.7683909618184, -122.51089453697205);

    const request = {
      origin: haight,
      destination: oceanBeach,
      travelMode: 'WALKING'
    };
    directionsService.route(request, function (response, status) {
      if (status == 'OK') {
        directionsRenderer.setDirections(response);
        directionsRenderer.setMap(map);
        moveMarkerOnRoute(map, response.routes[0].overview_path);
      }
    });
  }, [map])

  const initGoogleMap = () => {
    return new window.google.maps.Map(googleMapRef.current, {
      center: new window.google.maps.LatLng(37.7699298, -122.4469157),
      zoom: 12
    });
  }

  const moveMarkerOnRoute = async (map, pathCoords) => {
    const marker = new window.google.maps.Marker({
      position: pathCoords[0],
      map,
      icon: {
        url: '/person-marker.png',
        scaledSize: new window.google.maps.Size(40, 40),
      },
      optimized: false,
      zIndex: 99
    });

    for (let i = 0; i < pathCoords.length; i++) {
      await animatedMove(marker, marker.getPosition(), pathCoords[i], 0.05);
    }
  }

  const animatedMove = async (marker, moveFrom, moveTo, t, delta = 100) => {
    return new Promise(resolve => {
      const deltalat = (moveTo.lat() - moveFrom.lat()) / delta;
      const deltalng = (moveTo.lng() - moveFrom.lng()) / delta;
      let delay = 10 * t, count = 0;
      for (let i = 0; i < delta; i++) {
        (function (ind) {
          setTimeout(
            function () {
              let lat = marker.position.lat();
              let lng = marker.position.lng();
              lat += deltalat;
              lng += deltalng;
              marker.setPosition(new window.google.maps.LatLng(lat, lng));

              count++
              if (count === delta) {
                resolve(marker);
              }
            }, delay * ind
          );
        })(i)
      }
    })
  }

  return <div 300="" ref={googleMapRef} style={{ width: 600, height: }}>
}

export default GMap;</div>

App.js

import React, { useState, useEffect } from 'react';
import { Loader } from '@googlemaps/js-api-loader';
import GMap from './GMap';

// API key of the google map
const GOOGLE_MAP_API_KEY = '<your_google_map_api_key>';

const App = () => {
  const [loadMap, setLoadMap] = useState(false);

  useEffect(() => {
    const options = {
      apiKey: GOOGLE_MAP_API_KEY,
      version: "weekly",
      libraries: ['geometry']
    };

    new Loader(options).load().then(() => {
      setLoadMap(true);
    }).catch(e => {
      console.error('Sorry, something went wrong: Please try again later. Error:', e);
    });
  }, []);

  return (
    <div class="App">
      <h4>Animate marker on Google Maps route in React - <a href="https://www.cluemediator.com">Clue Mediator</a></h4>
      {!loadMap ? <div>Loading...</div> : <gmap>}
      <br>
      <small><b>Note:</b> In order to make it work, you have to set the YOUR_GOOGLE_MAP_API_KEY in App.js file. </small>
    </gmap></div>
  );
}

export default App;
</your_google_map_api_key>

4. Output

Run your application and check the output in the browser.
Live Demo

That's it for today.
Thank you for reading. Happy Coding..!!

Demo & Source Code

GitHub Repository StackBlitz Project