Giter Club home page Giter Club logo

react-map-interaction's Introduction

react-map-interaction

Add map like zooming and panning to any React element. This works on both touch devices (pinch to zoom, drag to pan) as well as with a mouse or trackpad (wheel scroll to zoom, mouse drag to pan).

example zooming map

Install

npm install --save react-map-interaction

Usage

Basic

import { MapInteractionCSS } from 'react-map-interaction';

// This component uses CSS to scale your content.
// Just pass in content as children and it will take care of the rest.
const ThingMap = () => {
  return (
    <MapInteractionCSS>
      <img src="path/to/thing.png" />
    </MapInteractionCSS>
  );
}

Usage without CSS

import { MapInteraction } from 'react-map-interaction';

// Use MapInteraction if you want to determine how to use the resulting translation.
const NotUsingCSS = () => {
  return (
    <MapInteraction>
      {
        ({ translation, scale }) => { /* Use the passed values to scale content on your own. */ }
      }
    </MapInteraction>
  );
}

Controlled

import { MapInteractionCSS } from 'react-map-interaction';

// If you want to have control over the scale and translation,
// then use the `scale`, `translation`, and `onChange` props.
class Controlled extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: {
        scale: 1,
        translation: { x: 0, y: 0 }
      }
    };
  }

  render() {
    const { scale, translation } = this.state;
    return (
      <MapInteractionCSS
        value={this.state.value}
        onChange={(value) => this.setState({ value })}
      >
        <img src="path/to/thing.png" />
      </MapInteractionCSS>
    );
  }
}

Controlled vs Uncontrolled

Similar to React's <input /> component, you can either control the state of MapInteraction yourself, or let it handle that for you. It is not recommended, however, that you change this mode of control during the lifecycle of a component. Once you have started controlling the state, keep controlling it under you unmount MapInteraction (likewise with uncontrolled). If you pass value prop, we assume you are controlling the state via a onChange prop.

Click and drag handlers on child elements

This component lets you decide how to respond to click/drag events on the children that you render inside of the map. To know if an element was clicked or dragged, you can attach onClick or onTouchEnd events and then check the e.defaultPrevented attribute. MapInteraction will set defaultPrevented to true if the touchend/mouseup event happened after a drag, and false if it was a click. See index.stories.js for an example.

Prop Types for MapInteractionCSS (all optional)

MapInteraction doesn't require any props. It will control its own internal state, and pass values to its children. If you need to control the scale and translation then you can pass those values as props and listen to the onChange event to receive updates.

{
  value: PropTypes.shape({
    // The scale applied to the dimensions of the contents. A scale of 1 means the
    // contents appear at actual size, greater than 1 is zoomed, and between 0 and 1 is shrunken.
    scale: PropTypes.number,
    // The distance in pixels to translate the contents by.
    translation: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }),    
  }),

  defaultValue: PropTypes.shape({
    scale: PropTypes.number,
    translation: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }),
  }),

  // Stops user from being able to zoom, but will still adhere to props.scale
  disableZoom: PropTypes.bool,

  // Stops user from being able to pan. Note that translation can still be
  // changed via zooming, in order to keep the focal point beneath the cursor. This prop does not change the behavior of the `translation` prop.
  disablePan: PropTypes.bool,

  // Apply a limit to the translation in any direction in pixel values. The default is unbounded.
  translationBounds: PropTypes.shape({
    xMin: PropTypes.number, xMax: PropTypes.number, yMin: PropTypes.number, yMax: PropTypes.number
  }),

  // Called with an object { scale, translation }
  onChange: PropTypes.func,

  // The min and max of the scale of the zoom. Must be > 0.
  minScale: PropTypes.number,
  maxScale: PropTypes.number,

  // When 'showControls' is 'true', plus/minus buttons are rendered
  // that let the user control the zoom factor
  showControls: PropTypes.bool,

  // Content to render in each of the control buttons (only when 'showControls' is 'true')
  plusBtnContents: PropTypes.node,
  minusBtnContents: PropTypes.node,

  // Class applied to the controls wrapper (only when 'showControls' is 'true')
  controlsClass: PropTypes.string,

  // Class applied to the plus/minus buttons (only when 'showControls' is 'true')
  btnClass: PropTypes.string,

  // Classes applied to each button separately (only when 'showControls' is 'true')
  plusBtnClass: PropTypes.string,
  minusBtnClass: PropTypes.string,
};

Prop Types for MapInteraction (all optional)

{
  // Function called with an object { translation, scale }
  // translation: { x: number, y: number }, The current origin of the content
  // scale:       number, The current multiplier mapping original coordinates to current coordinates
  children: PropTypes.func,

  // The rest of the prop types are the same as MapInteractionCSS
  ...MapInteractionCSS.propTypes,
}

Development

Please feel free to file issues or put up a PR. Note the node version in .nvmrc file.

$ yarn install
$ yarn test
$ yarn run storybook

react-map-interaction's People

Contributors

ahadik avatar anuraghazra avatar c-emil avatar felipe-imanishi avatar huan-ji avatar mathieuloutre avatar scottyantipa avatar wtlyu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-map-interaction's Issues

touch not working

This is a really fantastic!! However, when objects in the zoom view are clicked with a mouse they act as expected, but with a touch screen there is no event listener for touch as. Is there an intention to add touch support?

Update 1: I narrowed down the issue. Touch is supported but once the first touch support has fired it gets locked to that type. For example if the first touch is movement then all subsequent touches are for for movement. If the first touch fires a click like event all subsequent touches are clicks and movement is lost.

Update 2: To overcome the above issues. In the source code I changed the onTouchDown function. Then in my React component I added both a click handler and a touch handler.

Source for react-map-interaction

  onTouchDown(e) {
    e.preventDefault();
    //  e.stopPropagation();
    this.setPointerState(e.touches);
  }

React

onTouchStart={() => this.toggle(clickedHeader, true)}
onClick={() => this.toggle(clickedHeader, true)}

How to set onDoubleClick event for image

Hello. I would like to set default position and scale for an image when user double clicks on it. But I cannot set neither onDoubleClick nor onClick event for an image. It does not work. How can I implement this?

Jerky zooming when pinching vertically

We see a jerky zoom if you pinch up and down vertically for some reason. Sometimes the zoom will even invert such that pinching inwards will cause the scale to increase, not decrease

Notice below that the horizontal pinching works fine. But going vertical causes the jank.
pinch-vertical-bug

Not able to drag material UI card properly after using react-map-interaction

image
We are using react-beautiful-dnd and material UI cards.
We have implemented "react-map-interaction" in our app.
When we using react-map-interaction the material UI card not able to drag and drop properly.
Maybe it's the overriding default style of react-beautiful-dnd and material UI cards. Its dragging cursor also changed.
Can you please help?

Zooming on mobile is sometimes janky

To repro: Open the example app on a mobile device and try pinch zooming. You will notice that frequently the zoom effect is shaky/janky. This seems to be correlated to the speed at which you zoom and also the initial zoom level.

Component won't install on React 17.

It looks like when you set the "minimum" version to 16.3.0 you did it incorrectly. ^16.3.0 means any version of 16... which means react 17 doesn't count and so you get an error trying to install:

npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR! 
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/react
npm ERR!   react@"^17.0.1" from the root project
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^16.3.0" from [email protected]
npm ERR! node_modules/react-map-interaction
npm ERR!   react-map-interaction@"*" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 
npm ERR! See /home/echo/.npm/eresolve-report.txt for a full report.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/echo/.npm/_logs/2021-03-22T20_48_36_892Z-debug.log```

Change translation center points

Currently, the translation "roots" are located at the top left corner of the wrapper <div>, and the top left corner of the image.
I have SVG images that can be really long in one direction, so this means that they appear really tiny and take up only the upper portion of space.
It'd be nice if I could change the location of either root to be, i.e. in the center.

Having Trouble with Interactive Components on the Map

Hey I'm trying to get some markers to show up on the "map" area and I'm having some issues getting them to interact properly with the library. I'd like to have the markers stay on the same place of the map (currently they change place if the size of the map element changes) and to have tooltip modals that pop up when hovered or clicked that always have a scale of 1. I can get the tooltips to line up if I put them inside of the map and use the transform:translate, but then they change scale if I zoom in or out. If I put them outside of the map, I can't get them to line up with the markers.

Any tips on how I could implement this?

Creating a minimap controlled from Main Map

Hi
I have created a minimap which follows the main maps translation and zoom. However I have noticed that once I start using state to control map movement and zoom after some time the performance of the app drops significantly. Eg much slower panning and zoom responsiveness. I was wondering if you could shed any light on the issue/what I am doing wrong.

Much appreciated!

Project Github: https://github.com/MrTesau/travel-azeroth-client

Moving RMI to a new github org (no functional change)

My new employer is https://github.com/strateos which is a company that came about from a merger of transcriptic and another company. Since they'll be supporting the project I'm going to move this repo over there. It will not impact how you use or install this package (via npm). The only change is that you will use the new repo location for issues, cloning, etc. I'll be doing this in the next week or so, this is just a heads up.

https://help.github.com/en/github/administering-a-repository/transferring-a-repository

Importantly

When you transfer a repository, its issues, pull requests, wiki, stars, and watchers are also transferred. If the transferred repository contains webhooks, services, secrets, or deploy keys, they will remain associated after the transfer is complete. Git information about commits, including contributions, is preserved.

All links to the previous repository location are automatically redirected to the new location. When you use git clone, git fetch, or git push on a transferred repository, these commands will redirect to the new repository location or URL. However, to avoid confusion, we strongly recommend updating any existing local clones to point to the new repository URL.

defaultScale, scale is scaling from corner instead of middle.

Is there any way to set anchor in the middle of the component and then set the scale? so that when we set defaultScale the img inside will resize from the middle instead of being resized from top left corner

for example:

<MapInteractionCSS defaultScale={0.7} anchor={{x: '50%', y: '50%'}}>
  <img src="..." />
</MapInteractionCSS>

Dont set overflow:hidden by default

MapInteractionCSS applies overflow: 'hidden', to the outermost div. This should instead be an option provided in props, and should default to visible.

The scale is not properly being changed during a pinch/zoom.

To reproduce:

  • open on a touch enabled device
  • change the initial scale by pinching/zooming at least once
  • pinch/zoom again and you'll see the issue

Notice on the second pinch/zoom you will see the behavior in the below gif. The new scale isnt properly calculated. The computed change is too large which means as you zoom in, the points beneath your fingers will zoom together too quickly (and when zooming out, they zoom away too quickly).

not-scaling-proportionally

Is it possible to auto center from mount?

Right now the element we use is just aligned on top left of its container, it would be nice to be able to use a full screen container while still be able to center the zoomable element inside of it automtically.

Support for Programmatic Transforms and Zooms ?

Is there a way to programmatically define zooms and transform targets and have the controller automatically interpolate to the desired point?

I tried stepping with a controlled interaction component but the update time took too long and it looked choppy.

How to set scale down at the starting position ?

Hi !
I would like to know, if it was possible to set a scale down to go back to the initial position ?
Actually when i zoom then drag and after unzoom, the image is not centered anymore and i got blank space

Problems with clicking on mobile

Hey there!

There are certain issues with clicking items other than MapInteraction when using this component on mobile. Think about this like there is a header with buttons above a MapInteraction component and the buttons in the header don't respond to clicking.

The problem seems to be that the touchstart, touchmove and touchend event listeners are set on the window object rather than the containerNode ref, and e.preventDefault() sort of "eats" the event and prevents the buttons form handling it. Setting the event listeners on the containerNode seemingly solves the problem.

I really think that it should be an option whether to set the event listeners on the window or the containerNode. Gonna submit a PR for this later.

Add zoomStep

Hi,

There should be an option to change the step of zooming.

Ability to style wrapper divs

I think library adds two wrapper divs. Both of them have inline style and they have no classname or id given to them.

`onClick` events within map on mobile don't work

This is somewhat a continuation of #31.

Before the previous fix, all touch events on the page weren't firing. Now, onClick events within the <MapInteraction> component fail to fire.

In the following example, onClick works fine in desktop mode but fails mobile. To test, try clicking on the nodes: ComplexDiagram

As a work around, I can use a combination of onMouseUp and onTouchEnd.

Preventing panning when dragging certain descendent elements.

Hi, I've got a drag and drop field inside of my <MapInteractionCSS /> component. Is it possible to prevent panning when mousedowning on draggable elements? stopPropagation on mouseDown doesn't seem to be doing the trick. The docs hint that this is possible but I'm not sure what I'm missing. Thanks.

Sending click event while dragging

Yo Scott. First of all I wanna say I loved this module.

I was waiting for some days when you will fix the mobile device click problem from another issue and it's finally there! But there is another problem that came out now which had not been there before. When you drag around some clickable element on your desktop and you pull finger up in the same spot or elsewhere, it doesn't really matter, it instantly activates the click event. I suppose it is not the wanted feature. I'm sure you will be able to reproduce the problem. Cheers!

Resetting pan to defaultTranslation?

Is it currently possible to somehow trigger a reset to the defaultTranslation passed in?

Eg have a button outside that triggers the MapInteractionCss to reset to its defaultTranslation?

Passing it a translation prop seems to fix the translation to that x and y, with panning disabled.

On Chrome 56 e.preventDefault doesn't work properly

In our onWheel handler we preventDefault so that the browser does not try to scroll the node containing the pinch/zoom element. However in recent versions of chrome they introduced a passive option to the event handler which is true by default. See the blog post below. Instead, we should be using the css property touch-action (I believe setting touch-action: none on the containerNode should do it).

This causes the bad bug of the container node trying to scroll vertically while the user pinch/zooms.

https://developers.google.com/web/updates/2017/01/scrolling-intervention

Screen Shot 2019-04-17 at 7 56 26 AM
Above you can see the js warning from us calling preventDefault inside a non-passive event handler.

JS error occurs when pinching off screen

This doesn't appear to break any functionality, but it is annoying.

To repro:

  • Open in simulator or on a mobile device
  • pinch and zoom, then bring one of those fingers onto the edge of the device so it just touches
  • note the js error

This also may have to do with the "pull back to go back in history" default behavior of the browser.

undefined-object-bug

License info

Hi there ๐Ÿ‘‹

First of all, thank you very much for your hard work on react-map-interaction, I was amazed by how well-polished panning and zooming feel.

I've just noticed that there seems to be no information regarding the project's license. Is it correct to assume it's licensed under MIT or BSD?

Thanks a lot once again! ๐Ÿ™

Two finger navigation

When using the trackpad, two-finger gesture navigation is more convenient and more common that dragging. Dragging requires a push to click, which is inconvenient. How can we implement two-finger gesture navigation similar to Lucidchart, Draw.io, ...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.