Coder Social home page Coder Social logo

retyui / react-quick-pinch-zoom Goto Github PK

View Code? Open in Web Editor NEW
305.0 3.0 45.0 65.03 MB

A react component that providing multi-touch gestures for zooming and dragging on any DOM element.

Home Page: https://react-quick-pinch-zoom.netlify.app/

HTML 0.18% JavaScript 88.39% CSS 0.99% TypeScript 10.45%
react react-component pinch-to-zoom zoom

react-quick-pinch-zoom's Introduction

react-quick-pinch-zoom

react-quick-pinch-zoom on npm npm downloads react-quick-pinch-zoom install size Code quality

A react component that lets you zooming and dragging on any DOM element using multi-touch gestures on mobile devices and mouse-events\wheel on desktop devices. Based on this module manuelstofer/pinchzoom

Component features:

  • ๐Ÿ”ฎ Simple. Easy to use;
  • ๐ŸŽ It works with mobile touch gestures and desktop mouse events;
  • โšก Fast, 60 FPS on mobile devices.

Links

Install

npm i --save react-quick-pinch-zoom

or

yarn add react-quick-pinch-zoom

Screenshots

Video...

Usage

import React, { useCallback, useRef } from "react";
import QuickPinchZoom, { make3dTransformValue } from "react-quick-pinch-zoom";

const IMG_URL =
  "https://user-images.githubusercontent.com/4661784/" +
  "56037265-88219f00-5d37-11e9-95ef-9cb24be0190e.png";

export const App = () => {
  const imgRef = useRef();
  const onUpdate = useCallback(({ x, y, scale }) => {
    const { current: img } = imgRef;

    if (img) {
      const value = make3dTransformValue({ x, y, scale });

      img.style.setProperty("transform", value);
    }
  }, []);

  return (
    <QuickPinchZoom onUpdate={onUpdate}>
      <img ref={imgRef} src={IMG_URL} />
    </QuickPinchZoom>
  );
};

License

MIT ยฉ retyui

react-quick-pinch-zoom's People

Contributors

ashbrowning avatar blindman139 avatar bradcerasani avatar dependabot[bot] avatar dminguez avatar matt-tingen avatar maximuson avatar nicolas-cusan avatar retyui avatar tf avatar xepozz 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

react-quick-pinch-zoom's Issues

Image Zoom Issue

When clicking on the image for the very first time the transform scale is 1 and when clicking on the same image for the second time it is showing transform scale to be less than 1 .
first click
image

second click of same image
image

Focus support for children

Hi there, I'm wondering if it's possible to focus child components, using this library?

Currently, I'm unable to focus by clicking any element within QuickPinchZoom.

How to manually trigger zoom?

I need to trigger zoom by one click only. I'm able to capture the click and position X and Y related to the inner element where the user has clicked, and I'm using quickRef.scaleTo({ scale: 2, x: innerEl.clientX, y: innerEl.clientY }) to make QuickPinch to the hard work.

However, the offset is wrong, and looking to the code I was not able to completely understand the logic to calculate proper X and Y positions.

I did also try to use alignCenter, but with no success either.

Any clue how can I achieve 1 click zoom?

Demo:

Gravacao.de.Tela.2023-02-13.as.17.41.31.1.mp4

how could zoom in / zoom out when

@hello, could you help me with this functionality:

When user press double tap my image scale to 2, next if the user press double tap again then my image scale to 1. There are some event o method to manage these funcionality.

I appreciate your support. Thank you

When I build in a test environment NODE_ENV=test I have problem with require('prop-types')

In line 1058 of component.tsx the test for production should be either test against production and test or for development

if (process.env.NODE_ENV !== 'production') { <-------------------------------- should be === 'development' or !== 'test! || !== 'production'
const { any, element, object, number, func, bool } = require('prop-types');

// @ts-ignore
PinchZoom.propTypes = {
children: element,
containerProps: object,

if (process.env.NODE_ENV !== 'production') {

Quickest way to zoom into element?

Hi there,

Thanks for sharing and maintaining this awesome lib!
Been using it for a while now, and it works perfectly.

One thing I'd like to know how to do is zoom into a child element.
I know I can use scaleTo and alignCenter to achieve this, but having difficulty calculating the required numbers for the child divs.

Any help would be appreciated.

Should tslib be a dependency?

I'm seeing Webpack dependency resolution errors that tslib is missing. I did not notice this during development since I had other packages that depended on tslib in my dev dependencies. During deploy it failed, though.

This library uses importHelpers: true, but declares tslib only as a peerDependency. Others (e.g., [1], [2]) seem to declare it as dependency as well. Alternatively, I think, it would need to be mentioned in the install instructions in the README.

Let me know what you think and I can create PR.

[1] dojo/meta#226
[2] https://angular.io/guide/migration-update-libraries-tslib

Way to block dragging completely and allow only zooming?

Hello,
I only need the zooming functionality but there seems no way to block the dragging events and only allow zooming.
All the event listener props for drag events onDragStart/End/Update doesn't seem to provide a way to disable the event.
Is this supported at all? If not, can you suggest me an alternative?

Thanks in advance

Support touch and mouse at the same time

Hi, I have been using quick pinch zoom and I really like it.

I found a problem however, I was trying to use it in a PC but mouse and scroll were not working.
Turns out to that the isTouch default function was detecting that this PC supports touch on the screen.

When I force the isTouch to be false, it starts working. This is desirable because after all is a PC, but would be cool if the user could use both mouse or touch if both are supported by his machine.

I don't know if this is already possible to do in the current version but if it is please let me know how.

Thanks :)

cancel handled touchEnd events

Currently it is not possible to integrate this library to an application that handles for example swipe events on an outer element.
Imagine you want to use this library inside an image slider with touch support and the "wipe to next image" event is also triggered if you want to move your zoomed in image.

So it is necessary to cancel the touchEnd event, if it is handled by the library and let it bubble if not.

Cancel if:

  • user performed a zoom interaction
  • user performed a drag interaction if zoom factor is greather than 1

Bug Report: alignCenter not working the same way on different container size

Thanks for such a good library!!! I completely solved my problem until I faced this...

It seems that alignCenter method doesn't work the same way with all different sizes of container div.
Suppose you have an image with size = 5000 x 5000,
container div with size = 1000 x 1000
Now if I call alignCenter({x: 2000, y: 2000, scale: 5}) it should center image so that container div's center snaps on image's (2000, 2000) point in px.
Now if I change container div size to 900 x 400, still result should be the same. But it isn't !!!

To reproduce the issue try changing container div's size by using developer mode and Dimension: Responsive for google chrome browser. No need to set container size and image size as above. Those are just numbers. Key is to change container !!!

onZoomStart not triggered

HI,

I am trying to get onZoomStart triggered when the zoom happens -

import React, { useCallback, useRef } from "react";
import QuickPinchZoom, { make3dTransformValue } from "react-quick-pinch-zoom";

const IMG_URL =
  "https://user-images.githubusercontent.com/4661784/" +
  "56037265-88219f00-5d37-11e9-95ef-9cb24be0190e.png";

export const App = () => {
  const imgRef = useRef();
  const onUpdate = useCallback(({ x, y, scale }) => {
    const { current: img } = imgRef;

    if (img) {
      const value = make3dTransformValue({ x, y, scale });
      console.log(value)
      img.style.setProperty("transform", value);
    }
  }, []);

  return (
    <QuickPinchZoom minZoom={0.1} onUpdate={onUpdate} onZoomStart={() => console.log(" --- onZoomStart")}>
      <img ref={imgRef} src={IMG_URL} />
    </QuickPinchZoom>
  );
};

But nothing gets logged.

Also on zoom out when the scale is < 1, it resets back to 1.
I am doing simple logging at onUpdate function -

here is the output when I zoom out

{x: 72.83009088725078, y: 55.54549531761776, scale: 0.9178569267690161}
Panner.js?9bbc:41 {x: 119.7066319161354, y: 86.69372323812661, scale: 0.8686783502995947}
Panner.js?9bbc:41 {x: 308.07706112986756, y: 211.86091633409336, scale: 0.7147810028493384}
Panner.js?9bbc:41 {x: 481.9505963415516, y: 327.3953048629097, scale: 0.6143219803273658}
Panner.js?9bbc:41 {x: 589.6057507255324, y: 398.9293219206864, scale: 0.5651434038579445}
Panner.js?9bbc:41 {x: 1102.290229507924, y: 739.5946663747756, scale: 0.40915739521384054}
Panner.js?9bbc:41 {x: 1625.9387979364026, y: 1087.5453598700144, scale: 0.31917693123221214}
Panner.js?9bbc:41 {x: 1821.9788167700958, y: 1217.8087934371395, scale: 0.29489774689078146}
Panner.js?9bbc:41 {x: 1821.7349318776428, y: 1217.6457810223505, scale: 0.29492558284567405}
Panner.js?9bbc:41 {x: 1797.8472706236953, y: 1201.6792924649944, scale: 0.2976777178255819}
Panner.js?9bbc:41 {x: 1657.1039106653482, y: 1107.6065733984744, scale: 0.31499649768602667}
Panner.js?9bbc:41 {x: 1445.90928142397, y: 966.4442973887088, scale: 0.3451268847537115}
Panner.js?9bbc:41 {x: 1189.6146038851614, y: 795.1371948334756, scale: 0.39045016262500487}
Panner.js?9bbc:41 {x: 954.9099893865093, y: 638.2608685195243, scale: 0.44382496618667916}
Panner.js?9bbc:41 {x: 733.6860260135514, y: 490.39499574708645, scale: 0.5094696462981266}
Panner.js?9bbc:41 {x: 548.667697418752, y: 366.7289325437731, scale: 0.5813873798929099}
Panner.js?9bbc:41 {x: 399.0464075267813, y: 266.72221411284124, scale: 0.6563085169043642}
Panner.js?9bbc:41 {x: 286.84672120504086, y: 191.72805755852505, scale: 0.7265157828713544}
Panner.js?9bbc:41 {x: 193.42002850346924, y: 129.28175090199713, scale: 0.7975578423502805}
Panner.js?9bbc:41 {x: 122.22329286193744, y: 81.69392500071638, scale: 0.8617753847643028}
Panner.js?9bbc:41 {x: 72.28253621004039, y: 48.31357390014767, scale: 0.9133610474292736}
Panner.js?9bbc:41 {x: 32.9914008730863, y: 22.051418886569806, scale: 0.9585016392742813}
Panner.js?9bbc:41 {x: 11.308433829813163, y: 7.558545703819909, scale: 0.9853768099906063}
Panner.js?9bbc:41 {x: 0.7640230903436237, y: 0.5106722587800954, scale: 0.9989983670862254}
Panner.js?9bbc:41 {x: 1.8363088827300092e-13, y: 1.1579626146840385e-13, scale: 0.9999999999999999}
Panner.js?9bbc:41 {x: 39.3088725093902, y: 26.119711338476375, scale: 0.9508214235305785}
Panner.js?9bbc:41 {x: 75.15894041432026, y: 49.941138038462796, scale: 0.9100064229965209}
Panner.js?9bbc:41 {x: 183.70169213405478, y: 122.06494016802324, scale: 0.8053392362594604}
Panner.js?9bbc:41 {x: 362.13627794255433, y: 240.63002679077624, scale: 0.6772795915603638}
Panner.js?9bbc:41 {x: 645.5440714007531, y: 428.9470474439214, scale: 0.540715880393982}
Panner.js?9bbc:41 {x: 848.6590395776715, y: 563.9115986667422, scale: 0.4724431848526002}
Panner.js?9bbc:41 {x: 2248.704283126769, y: 1494.2048197092347, scale: 0.25260043144226085}
Panner.js?9bbc:41 {x: 6172.842747117509, y: 4101.691562229398, scale: 0.10962314128875743}
Panner.js?9bbc:41 {x: 6839.999999999998, y: 4545, scale: 0.1}
Panner.js?9bbc:41 {x: 6839.999999999998, y: 4545, scale: 0.1}
Panner.js?9bbc:41 {x: 6839.999999999998, y: 4545, scale: 0.1}
Panner.js?9bbc:41 {x: 6839.999999999998, y: 4545, scale: 0.1}
Panner.js?9bbc:41 {x: 6837.30067084508, y: 4543.206366811534, scale: 0.10003553010828276}
Panner.js?9bbc:41 {x: 6470.746398599763, y: 4299.64069906958, scale: 0.1051067148679387}
Panner.js?9bbc:41 {x: 5197.524540594338, y: 3453.618280263343, scale: 0.12756976405575668}
Panner.js?9bbc:41 {x: 3709.2097857707063, y: 2464.6722918607984, scale: 0.17005243352409322}
Panner.js?9bbc:41 {x: 2490.537112040962, y: 1654.896370500902, scale: 0.23380751359051788}
Panner.js?9bbc:41 {x: 1738.9926431630201, y: 1155.514848417533, scale: 0.30412254396957894}
Panner.js?9bbc:41 {x: 1217.3955006847718, y: 808.9272734813285, scale: 0.3843439512918948}
Panner.js?9bbc:41 {x: 815.4610012184941, y: 541.852375809657, scale: 0.48239848489565923}
Panner.js?9bbc:41 {x: 554.2973871113915, y: 368.31602696217436, scale: 0.5782557337881911}
Panner.js?9bbc:41 {x: 378.78375987474476, y: 251.69184044308668, scale: 0.6673786778304536}
Panner.js?9bbc:41 {x: 250.94420630828623, y: 166.745821296953, scale: 0.7517724472405145}
Panner.js?9bbc:41 {x: 152.95555854114355, y: 101.63494350431215, scale: 0.8324611125808152}
Panner.js?9bbc:41 {x: 87.52287423688446, y: 58.15664669687682, scale: 0.896730959249105}
Panner.js?9bbc:41 {x: 38.127642230095454, y: 25.334814902892017, scale: 0.9522286408680687}
Panner.js?9bbc:41 {x: 12.026836739087496, y: 7.991516517419623, scale: 0.9844217374749732}
Panner.js?9bbc:41 {x: 0.43220649374257186, y: 0.28718984123648095, scale: 0.9994316304727078}
Panner.js?9bbc:41 {x: -9.99200722162641e-16, y: -3.603228826420946e-13, scale: 0.9999999999999999}

The scale goes to the minimum but resets back to 1.

Do you know why?

Dragging doesn't work properly in a popup window

If you host the QuickPinchZoom component inside of a second window created with window.open, dragging won't work properly - the mouse tracking only works while the mouse is over the primary window. This is consistent with the code using the 'window' global instead of getting the window for the contained element via element.document.defaultView.

SVG support

v3.0.0 .SVG does not zoom >100% and loads very small, does not fit parent container on load.

Stricter position sanitization/Toggle zoom on double tap/Swipe to close

Thank you very much for this great library!

We've been using it to build a zoomable fullscreen overlay for inline images. To tailor the experience to our use case, we made some changes/additions. Ideally, we would like to contribute those back upstream. I'm not 100% sure, though, if some of the features might be too specific to our use case. Therefore, I wanted to discuss our suggestions upfront, before putting more work into the API design details.

Stricter position sanitization

Currently it is possible to move the content out of bounds while dragging and zooming (in the code this is called an "insane offset"). Once the interaction stops, the content animates back to an allowed position. There already is a draggableUnZoomed prop, which prevents dragging the content when it is not zoomed. Once zoomed in a bit, the option no longer takes effect, though.

We would prefer if dragging and zooming was always confined to "sane" positions. In particular, when zooming into a landscape image on a portrait device such that the image still fits in the viewport vertically, we want to ensure that dragging is locked to the x-axis.

We have this working and could contribute a PR to add this to the available options. To be backwards compatible this could be another boolean option (name suggestion?), which would then take precedence over draggableUnZoomed. From an API design perspective, I'd prefer to combine draggableUnZoomed and this new option into a union type that describes different ways the offset is confined.

Toggle zoom on double tap

There already is an option doubleTapZoomOutOnMaxScale which allows zooming back out once max scale is reached. We would prefer, though, if double tapping a second time always zoomed out again even if it's still possible to zoom in further via pinch gesture. This is, for example, how the image viewer in the Android Twitter app works. Again, this could be a new boolean option or could be combined into something line doubleTapBehavior: 'zoomIn' | 'zoomOutOnMaxScale' | 'toggleZoom' etc. Happy to prepare a PR.

Swipe to close

The most specific thing we needed was an easy way to close our overlay on mobile. We decided that swiping up or down while the image is unzoomed would be a nice way to do this. Here's a video showing how this interaction works:

screen-20230510-103501_3.mp4

To enable the fade-out effect, we needed to pass an additional parameter to onUpdate which represents the amount of "overscroll". I really liked, how this interaction turned out, still it might not be universal enough to be a feature of the library?

One other option could be to allow passing a custom sanitizeOffset function. If we pass enough information (zoom factor, current interaction), this could be enough to implement behaviors like this. Instead of relying on an extended onUpdate payload, client component could store additional state in refs.

Let me know how you want to proceed.

Calling scaleTo with outside control returns element to start position

Hi! Thanks for great library! I have one issue which can't solve and don't understand why it happens, so write here.

Use case:

  • Draggable and zoomable item should be zoomIn and zoomOut also by keyboard or zoom buttons

Own expectations:

  • Position would stay where it was when press the button

Actually happens:

  • Element scale correctly, but onUpdate will called as long as x and y are back to 0

Codesandbox: https://codesandbox.io/s/sad-dijkstra-x98nd

  • To reproduce: Zoom container for example with wheel, drag it somewhere. Press then - or + button which trigger scaleTo
  • I have much more stuff inside children than one image, but this is simplest example and children div styles are same than mine real implementation. Also QuickPinchZoom component props are same than mine real case.

So, am I doing something wrong here? How I get the position staying to where it have dragged? I can see scaleTo took first correct position and moves there, then start this onUpdate iterating back to initial position.

Set initial scale / x /y

Hi, I've go a need to set default coordinates and scale when SVG is initially rendered. it seems like scaleTo method can set custom values for scale , x , y but it's not clear where to use it

Y axis value never goes negative

When zooming in or dragging down on my content, y axis value never goes below 0, which makes it difficult to navigate when I am trying to zoom in to the bottom half of the content, every time I drag or zoom so that the y axis becomes a negative number, it just automatically jump back to 0. Hope someone can help me here, thanks a lot!

react import missing in types.ts

Getting below error while running npm run build in my project

(!) Plugin postcss: start value has mixed support, consider using flex-start instead src/Components/LineLinkDisplay/LineLinkItem/LineLinkItem.module.scss created dist/cjs, dist/es in 7.9s node_modules/react-quick-pinch-zoom/esm/PinchZoom/types.d.ts:28:21 - error TS2503: Cannot find namespace 'React'. 28 containerProps: React.HTMLAttributes<HTMLDivElement>; ~~~~~ Found 1 error in node_modules/react-quick-pinch-zoom/esm/PinchZoom/types.d.ts:28

Scrolling using trackpad on macos does not work

When using two fingers to scroll on a macos trackpad, nothing happens whereas I would expect a normal scroll experience. Pinching-to-zoom works fine, which is based off the same WheelEvent (just that the event.ctrl flag is set to true).

How to enable mouse wheel zooming

Hello,

How can I enable mouse wheel zooming without having to hold on the ctrl keyboard button?

Currently I am using the package like this:

import QuickPinchZoom, { make3dTransformValue } from "react-quick-pinch-zoom";
import React, {useRef} from "react";

export default function MyComponent({

const imgRef2 = useRef();

const onUpdate = useCallback(({ x, y, scale }) => {
   const { current: img } = imgRef;
   const value = make3dTransformValue({ x, y, scale });

   img.style.setProperty("transform", value);
  }, []);


return (
 <QuickPinchZoom onUpdate={onUpdate}>
        <img ref={imgRef2} ...../>
  </QuickPinchZoom>
)

)}

SVG support

v3.0.0 .SVG does not zoom >100% and loads very small, does not fit parent container on load.

Does not work with TS.

(alias) class QuickPinchZoom
import QuickPinchZoom
JSX element class does not support attributes because it does not have a 'props' property.ts(2607)
'QuickPinchZoom' cannot be used as a JSX component.
  Its instance type 'PinchZoom' is not a valid JSX element.
    Type 'PinchZoom' is missing the following properties from type 'ElementClass': render, context, setState, forceUpdate, and 3 more.ts(2786)

Also, unable to attach ref to the img tag within.

Calling alignCenter for the second time

Hi there,

First of all, thanks for the great package!

I'm trying the following:

  1. Calling alignCenter with my X and Y where I just clicked and a scale of 2 - works great.
  2. After another click, calling alignCenter again with different X and Y values but the same scale which results in a weird positioning of the center.

What would be the correct way to align the center again to a different point using the same scale?

All child drag events are ignored when `enabled={false}`

Hey there, thanks for making this library!

I would like to be able to dynamically enable or disable the pan/zoom in order to interact with some child components that require drag interaction (<input type="range" />), however children do not seem to receive drag events when the enabled prop is false. I would expect that this should work, and that events would propagate normally when disabled.

const Example = () => {
  const [enabled, setEnabled] = useState(true);

  return (
    <QuickPinchZoom enabled={enabled}>
      <input  // <--- Doesn't respond to gestures
        type="range"
        onMouseEnter={() => setEnabled(false)}
        onMouseLeave={() => setEnabled(true)}
      /> 
    </QuickPinchZoom>
  );
}

Example is contrived of course, and my real input is deeply nested in a child, but either way is impossible to interact with any element via drag if they are within the QuickPinchZoom, whether it's enabled or not.

Zoom start/end events not firing for wheel events

When I zoom on a MacBook trackpad (and therefore firing a wheel event), I'm able to get callbacks for onZoomUpdate, but nothing for onZoomStart and onZoomEnd. Can these be called to facilitate handling updates based on the zoom lifecycle when scrolling/using a trackpad?

Thanks for this package by the way - had fun playing around with it last night!

Module not found: Can't resolve 'classnames'

Failed to compile.

./node_modules/react-quick-pinch-zoom/dist/index.es.js
Module not found: Can't resolve 'classnames' in '...\node_modules\react-quick-pinch-zoom\dist'

I'm using your main example and immediatelly getting this error. Any idea how to quick fix this at least temporary? Fork and add classnames dependency?

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.