google-map-react / google-map-react

Google map library for react that allows rendering components as markers :tada:
http://google-map-react.github.io/google-map-react/map/main/
MIT License
6.39k stars 834 forks source link

How to add a draggable circle? #59

Closed kad3nce closed 8 years ago

kad3nce commented 9 years ago

Hey there! I'm using google-map-react with GeoFire and need to implement a draggable circle as in this demo.

I didn't see any documentation on doing something like this, so decided to try to use the Google Maps API directly. The circle shows up and the hand cursor changes to a pointer when hovering over it, but unfortunately, once I start dragging, the entire map is dragged rather than just the circle. Is there a workaround for this? (example code and screenshot below).

Thanks for the help and for making this great component!

Best, -Jedidiah

  initDraggableCircle({ map, maps }) {
    let circle = new maps.Circle({
      strokeColor: '#6D3099',
      strokeOpacity: 0.7,
      strokeWeight: 1,
      fillColor: '#B650FF',
      fillOpacity: 0.35,
      map: map,
      center: new google.maps.LatLng(...this.props.mapConfig.center),
      radius: this.props.mapConfig.radius * 1000,
      draggable: true
    });
    maps.event.addListener(circle, 'drag', ::this.onCircleDragged);
  }
  render() {
    return (
      <div>
        <h2>Welcome to the map!</h2>
        <div style={{height:"500px"}}>
          <GoogleMap center={this.props.mapConfig.center}
                     defaultZoom={this.props.mapConfig.zoom}
                     onGoogleApiLoaded={::this.initDraggableCircle}
                     yesIWantToUseGoogleMapApiInternals={true}>
            {this.geofireMarkers()}
          </GoogleMap>
        </div>
      </div>
    );
  }

image

istarkov commented 9 years ago

In short there are three methods u need to create marker draggable

onChildMouseDown(hoverKey, childProps, mouse)
onChildMouseUp(hoverKey, childProps, mouse)
onChildMouseMove(hoverKey, childProps, mouse)

I have no time to publish working examples now (need to remove proprietary code) but its easy as make any Item dragable with standard mousedown, mousemove, mouseup

PS: mouse has lat,lng

kad3nce commented 9 years ago

That’s a huge help, Ivan! No sweat on the examples. I really appreciate you taking the time to reply. I’ll dive in.

All the best,

-Jedidiah

On Wed, Nov 4, 2015 at 12:00 PM, Ivan Starkov notifications@github.com wrote:

In short there are three methods u need to create marker draggable

onChildMouseDown(hoverKey, childProps, mouse)
onChildMouseUp(hoverKey, childProps, mouse)
onChildMouseMove(hoverKey, childProps, mouse)

I have no time to publish working examples now (need to remove proprietary code) but its easy as make any Item dragable with standard mousedown, mousemove, mouseup

PS: mouse has lat,lng

Reply to this email directly or view it on GitHub: https://github.com/istarkov/google-map-react/issues/59#issuecomment-153830959

kad3nce commented 9 years ago

Hello again @istarkov. I implemented the callbacks and am able to get the events to fire when I interact with a child marker. However, I'm still not sure how to prevent the Google Map itself from being dragged when I'm trying to drag a child object. Any pointers on that?

Thanks again for your help!

Current code:

...
onCircleInteraction(childKey, childProps, mouse) {
  // function is just a stub to test callbacks
  if (childKey !== 'map-circle') return;
  console.log('onCircleInteraction called with', childKey, childProps, mouse);
}
render() {
  let center = this.props.mapConfig.center;
  return (
    <div>
      <h2>Welcome to the map!</h2>
      <div style={{height:"500px"}}>
        <GoogleMap center={center}
                   defaultZoom={this.props.mapConfig.zoom}
                   onChildMouseDown={::this.onCircleInteraction}
                   onChildMouseUp={::this.onCircleInteraction}
                   onChildMouseMove={::this.onCircleInteraction}>
          <MapCircle key="map-circle" lat={center[0]} lng={center[1]} />
          {this.geofireMarkers()}
        </GoogleMap>
      </div>
    </div>
  );
}
...
istarkov commented 9 years ago

O sorry forget to say, map has a boolean draggable property, just set it to false in onChildMouseDown and to true in onChildMouseUp

kjeske commented 8 years ago

@kad3nce I see you've created MapCircle component. How do you get there the map reference to apply the circle on it? Maybe you could post your implementation for that component, it might be useful.

hemedani commented 7 years ago

please someone create a full documented example for draggable markers

Fi1osof commented 7 years ago

@hemedani My example:



import PropTypes from 'prop-types'; 

import GoogleMapReact from 'google-map-react';

class AnyReactComponent extends Component{

    render(){

        let {
            text,
        } = this.props;

        return <div
          style={{
            background: '#fff',
            display: 'inline-block',
          }}
         >{text}</div>
    }
}

export default class MapExample extends Component{

    contextTypes = {

        updateItem: PropTypes.func.isRequired,
    }

    static defaultProps = {
    center: {lat: 59.95, lng: 30.33},
    zoom: 11
    };

    static propTypes = {
        PlacesStore: PropTypes.object.isRequired,
    };

    constructor(props){

        super(props);

        this.state = {
            draggable: true,  // By default map is draggable
        }
    }

    onChildMouseDown(){ 
               // set map no draggable 
        this.setState({
            draggable: false,
        });
    }

    onChildMouseUp(){ 
                set map draggable again
        this.setState({
            draggable: true,
        });
    }

    onChildMouseMove(key, marker, newCoords){
                // Change item data with new coordinates
                // you need set here own store and update function

        let {
            item,
        } = marker;

        let {

            PlacesStore
        } = this.props;

        let {
            updateItem,
        } = this.context;

        PlacesStore.getDispatcher().dispatch(PlacesStore.actions['UPDATE'], item, newCoords);

        this.forceUpdate();
    }

    render(){

        let {
            PlacesStore
        } = this.props;

        let {
            draggable,
        } = this.state;

    let items = [];

        PlacesStore.getState().map(n => {

        // console.log(n);

        let {
            id,
            lat,
            lng,
            name,
        } = n;

        items.push(<AnyReactComponent 
                key={id}
          item={n}
          lat={lat}
          lng={lng}
          text={name}
        />);
    })

    return <GoogleMapReact
            draggable={draggable}
                        defaultCenter={this.props.center}
                        defaultZoom={this.props.zoom}
                        apiKey="xxxxxxxxxxxxxxxxx"
          onChildMouseDown={::this.onChildMouseDown}
          onChildMouseUp={::this.onChildMouseUp}
          onChildMouseMove={::this.onChildMouseMove}
    >
      {items}
    </GoogleMapReact>

    }
} ```
yarnball commented 6 years ago

@Fi1osof Could you please show a working example? Your example does not render a map for me! I put it in CodeSandBox:

https://codesandbox.io/s/llj2kryzqz

Cheers

nicolasazoidis commented 5 years ago

I suppose you've found your way out, in any case here is the code :

import React from "react"; import { compose, withProps } from "recompose"; import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps"; import { GOOGLE_API_KEY } from "../wherever you have it in Constants";

const MyMapComponent = compose( withProps({ googleMapURL: GOOGLE_API_KEY, loadingElement: <div style={{ height: "give your height" }} />, containerElement: <div style={{ height: "give your height" }} />, mapElement: <div style={{ height: "give your height" }} /> }), withScriptjs, withGoogleMap )(props => ( <GoogleMap defaultZoom={11} defaultCenter={{ lat: whateverLat, lng: whateverLng }}> <Marker position={{ lat: whateverLat, lng: whateverLng }} draggable={true} onClick={props.onMarkerClick} /> ));

class MyFancyComponent extends React.PureComponent { state = { isMarkerShown: false, draggable: true };

onChildMouseDown() { this.setState({ draggable: false }); }

onChildMouseUp() { this.setState({ draggable: true }); }

onChildMouseMove(key, marker, newCoords) { let { item } = marker;

let { PlacesStore } = this.props;

let { updateItem } = this.context;

PlacesStore.getDispatcher().dispatch(
  PlacesStore.actions["UPDATE"],
  item,
  newCoords
);

this.forceUpdate();

}

render() { let { PlacesStore } = this.props;

let { draggable } = this.state;

let items = [ ];

PlacesStore.getState().map(n => {
  let { id, lat, lng } = n;

  items.push(<MyMapComponent key={id} item={n} lat={lat} lng={lng} />);
});

return (
  <MyMapComponent
    isMarkerShown={this.state.isMarkerShown}
    onMarkerClick={this.handleMarkerClick}
    onChildMouseDown={this.onChildMouseDown}
    onChildMouseUp={this.onChildMouseUp}
    onChildMouseMove={this.onChildMouseMove}
  />
);

} }

export default MyMapComponent;

octocat01 commented 4 years ago

@hemedani My example:


import PropTypes from 'prop-types'; 

import GoogleMapReact from 'google-map-react';

class AnyReactComponent extends Component{

  render(){

      let {
          text,
      } = this.props;

      return <div
        style={{
          background: '#fff',
          display: 'inline-block',
        }}
       >{text}</div>
  }
}

export default class MapExample extends Component{

  contextTypes = {

      updateItem: PropTypes.func.isRequired,
  }

  static defaultProps = {
    center: {lat: 59.95, lng: 30.33},
    zoom: 11
  };

  static propTypes = {
      PlacesStore: PropTypes.object.isRequired,
  };

  constructor(props){

      super(props);

      this.state = {
          draggable: true,  // By default map is draggable
      }
  }

  onChildMouseDown(){ 
               // set map no draggable 
      this.setState({
          draggable: false,
      });
  }

  onChildMouseUp(){ 
                set map draggable again
      this.setState({
          draggable: true,
      });
  }

  onChildMouseMove(key, marker, newCoords){
                // Change item data with new coordinates
                // you need set here own store and update function

      let {
          item,
      } = marker;

      let {

          PlacesStore
      } = this.props;

      let {
          updateItem,
      } = this.context;

      PlacesStore.getDispatcher().dispatch(PlacesStore.actions['UPDATE'], item, newCoords);

      this.forceUpdate();
  }

  render(){

      let {
          PlacesStore
      } = this.props;

      let {
          draggable,
      } = this.state;

      let items = [];

      PlacesStore.getState().map(n => {

          // console.log(n);

          let {
              id,
              lat,
              lng,
              name,
          } = n;

          items.push(<AnyReactComponent 
              key={id}
        item={n}
        lat={lat}
        lng={lng}
        text={name}
      />);
      })

      return <GoogleMapReact
          draggable={draggable}
                        defaultCenter={this.props.center}
                        defaultZoom={this.props.zoom}
                        apiKey="xxxxxxxxxxxxxxxxx"
        onChildMouseDown={::this.onChildMouseDown}
        onChildMouseUp={::this.onChildMouseUp}
        onChildMouseMove={::this.onChildMouseMove}
    >
      {items}
    </GoogleMapReact>

  }
} ```

@Fi1osof Thanks man, it really helped. 👍