简体   繁体   中英

Set marker timeout in React

The animation prop in this Marker component rendering gets a marker to bounce if there's one and drop if there are multiple, using the marker's array:

<Marker
  key={index}
  position={{lat: marker.lat, lng: marker.lng}}
  onClick={() => props.handleMarkerClick(marker)}
  animation={array.length === 1 ? google.maps.Animation.BOUNCE : google.maps.Animation.DROP}
>

However, my goal is to set a 750ms timeout for the bounce (based on an answer to another SO question ). I can't do this with the conditional ternary operator, so I made two attempts at creating a function for this, replacing the animation property in the Marker component with animation={array.length === 1 ? bounceMarker() : google.maps.Animation.DROP} animation={array.length === 1 ? bounceMarker() : google.maps.Animation.DROP} and both had issues.

This one I thought would work, but using google.maps.Animation.BOUNCE; throws an ESLint error "Expected an assignment or function call and instead saw an expression."

  const bounceMarker = () => {
    google.maps.Animation.BOUNCE;
    setTimeout(() => {
      marker.setAnimation(null);
    }, 750);
  };

This function is based on Google's markers tutorial , and caused the error "marker.getAnimation is not a function":

      const bounceMarker = () => {
        if (marker.getAnimation() !== null) {
          marker.setAnimation(null);
        } else {
          marker.setAnimation(google.maps.Animation.BOUNCE);
          setTimeout(() => {
            marker.setAnimation(null);
          }, 750);
        }
      };

In case it's useful here's my full code from this file, with the two functions commented out:

// Specifies global variable to ESLint (bundled with create-react-app), circumventing no-undef rule. See https://eslint.org/docs/user-guide/configuring#specifying-globals
/* global google */
// This component's code is from react-google-maps implementation instructions https://tomchentw.github.io/react-google-maps/#installation

import React, { Component , Fragment } from 'react';
import { withScriptjs , withGoogleMap, GoogleMap, Marker, InfoWindow } from 'react-google-maps';

const MyMapComponent = withScriptjs(
  withGoogleMap(props => (
    <GoogleMap
      zoom={props.zoom}
      defaultCenter={{ lat: 47.6093, lng: -122.3309 }}
    >
      {/* If there are markers, filters all visible markers (creating new array) then maps over newly created array taking the marker and marker's array index as arguments, rendering each Marker component with the marker index set as the key and the marker's lat and long as the position */}
      {props.markers &&
        props.markers.filter(marker => marker.isVisible)
        // "Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity ... When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort" https://reactjs.org/docs/lists-and-keys.html
        .map((marker, index, array) => {
          // const bounceMarker = () => {
          //   if (marker.getAnimation() !== null) {
          //     marker.setAnimation(null);
          //   } else {
          //     marker.setAnimation(google.maps.Animation.BOUNCE);
          //     setTimeout(() => {
          //       marker.setAnimation(null);
          //     }, 750);
          //   }
          // };
          // const bounceMarker = () => {
          //   google.maps.Animation.BOUNCE;
          //   setTimeout(() => {
          //     marker.setAnimation(null);
          //   }, 750);
          // };

          const venueInfo = props.venues.find(venue => venue.id === marker.id);
          return (
            <Marker
            key={index}
            position={{lat: marker.lat, lng: marker.lng}}
            // Marker click event listener, defined in App component class
            onClick={() => props.handleMarkerClick(marker)}
            animation={array.length === 1 ? bounceMarker() : google.maps.Animation.DROP}
            >
            {/* Show marker's InfoWindow when its isOpen state is set to true (set in app.js) */}
            {marker.isOpen &&
              <InfoWindow>
                {/* If a venueInfo is not falsey and: 1) if there's a name and bestPhoto property, return the venue photo and name; 2) if there's only a name property, display the name only; 3) if there's only a photo property, display the photo only. If neither are available and/or venueInfo is falsy display text indicating no info available. See SO question about multiple ternary operators https://stackoverflow.com/questions/7757549/multiple-ternary-operators */}
                {venueInfo && venueInfo.name && venueInfo.bestPhoto ?
                <Fragment>
                <p>{venueInfo.name}</p>
                <img src={`${venueInfo.bestPhoto.prefix}200x200${venueInfo.bestPhoto.suffix}`}
                // Screen readers already announce as image; don't need the word "image", "photo", etc.
                alt={"Venue"}
                />
                </Fragment> : venueInfo && venueInfo.name ? <p>{venueInfo.name}</p> : venueInfo && venueInfo.bestPhoto ? <img    src={`${venueInfo.bestPhoto.prefix}200x200${venueInfo.bestPhoto.suffix}`}
                // Screen readers already announce as image; don't need the word "image", "photo", etc.
                alt={"Venue"}
                /> : <p>No info available</p>}
              </InfoWindow>
            }
            </Marker>
          );
      })}
    </GoogleMap>
  ))
);

export default class Map extends Component {
  render() {
    return (
      <MyMapComponent
        // This is making the this.setState passed into Map component (as its prop) inside App's component class's render method available to MyMapComponent, which is how props from this.setState are eventually included inside MyMapComponent class (such as zoom={props.zoom})
        {...this.props}
        isMarkerShown
        googleMapURL="https://maps.googleapis.com/maps/api/js?v=3.exp&key=AIzaSyDjl8LxY7Edfulq6t_VDaQsYY4ymPjwN0w"
        // CSS declarations are placed in double curly braces because attributes accept JS objects; this is how to include an object literal. See https://stackoverflow.com/questions/22671582/what-is-the-purpose-of-double-curly-braces-in-reacts-jsx-syntax
        loadingElement={<div style={{ height: `100%` }} />}
        containerElement={<div style={{ height: `100%`, width: `75%` }} />}
        mapElement={<div style={{ height: `100%` }} />}
      />
    );
  }
}

You are getting this error

marker.getAnimation is not a function

since marker is not the actual google.maps.Marker object.

Instead of rendering markers and afterwards updating Marker.animation property, how about to render the marker with a timeout (basically the same way as it is demonstrated in Marker Animations With setTimeout() example)?

For that purpose it is proposed to introduce a state for keeping animated markers

class MarkersAnimation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      markers: []
    };
  }

  render() {
    return (
      <GoogleMap
        defaultZoom={this.props.zoom}
        defaultCenter={this.props.center}
      >
        {this.state.markers.map((place, i) => {
          return (
            <Marker
              animation={google.maps.Animation.BOUNCE}
              id={place.id}
              key={place.id}
              position={{ lat: place.lat, lng: place.lng }}
            />
          );
        })}
      </GoogleMap>
    );
  }

  componentDidMount() {
    const { markers, delay } = this.props;
    this.interval = setInterval(() => {
      this.setState(prev => ({
        markers: prev.markers.concat([markers.shift()])
      }));
      if (!markers.length) clearInterval(this.interval);
    }, delay);
  }

  componentWillUnmount() {
    clearInterval(this.interval);
  }
}

Here is a demo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM