React Image Pan And Zoom With Commenting

React Image Pan And Zoom With Commenting

Nifty Gateway has developed a React component for image pan and zoom, allowing users to comment on any part of an image. This component was recently utilized in a project with Sam Spratt called the Monument Game. It enables users to zoom and pan around a 20,000-pixel-wide image, creating a more interactive and immersive user experience.

The component can function independently as a standalone image pan and zoom viewer. However, if you aim to support commenting, the component offers the necessary properties for integration with your custom backend. Moreover, if commenting is enabled, and comments are persisted to your backend, you can deep-link to a specific comment via query parameters in the URL (?x=0.6016064257028112&y=0.6058201058201058). Simply provide the x and y coordinates, and the component will load the pan and zoom experience, highlighting the specified comment at those coordinates.

Why Do I Need to Provide A Src and SmallerSrc?

For desktop use, if you’re employing an image with a width equal to or less than 10,000 pixels, props.smallerSrc can be left undefined. However, for larger images, providing an image for props.smallerSrc may enhance zoom performance. The image specified for props.smallerSrc will only be displayed when a user zooms in and if the current zoom width of an image is less than or equal to the intrinsic width of props.smallerSrc. During development, we discovered that the Chrome browser struggles to provide a smooth zooming experience for images beyond 10,000 pixels in width. To address this issue, we introduced the option to supply a lower resolution image for display during zoom, significantly improving performance in Chrome browsers.

For mobile use, it’s advisable to limit the maximum width of an image to 10,000 pixels. During development, we found that using an image exceeding 10,000 pixels could randomly crash the browser or refresh the page. You can leave props.smallerSrc undefined, especially for images equal to or less than 5,000 pixels in width, but results may vary.

In the future, we plan to explore a version that supports image tiling. If you’d like to initiate work on this feature, feel free to open a pull request!

Data Flow Diagram on Commenting Flow

You can also take a look at the usage example below titled, Advanced Usage Showing How You can Tie in With A Custom Backend To Persist Comments

React Props

Prop Name Description Is Required? Default Value Type
src URL of main image to use for pan and zoom Yes string
smallerSrc URL of the main image on a smaller scale, used for pan and zoom. Displayed during zoom and after zoom if the current zoom width is <= intrinsic width of the provided image Optional undefined string OR undefined
previewBoxSrc URL of the image displayed in the pan and zoom preview box. Image width should be no larger than 100 pixels Yes string
comments List of comments and their coordinates Optional undefined { x: number, y: number, comment: string}[] OR undefined
isLoading Flag to indicate whether we are some loading state Optional FALSE boolean
isCommentSubmitLoading Flag to indicate whether we are in loading state for submitting a new comment Optional FALSE boolean
enableCommentDrop Flag to toggle whether we allow comments to be dropped when pan and zoom is active Optional TRUE boolean
enablePanAndZoomControls Flag to toggle display of pan and zoom controls (ie. zoom in/out button and reset zoom button) Optional TRUE boolean
enablePanAndZoomPreview Flag to toggle the display of a smaller version of the image indicating your current pan location location Optional TRUE boolean
persistCommentDrawerWhenOpen Flag to toggle whether the Comment Drawer stays open while the user interacts with pan and zoom. Recommended to set to false for better mobile user experience Optional TRUE boolean
addingCommentsToTheFront Flag to indicate whether new comments are being added to the front of the comments array or at the end Optional FALSE boolean
zoomToPercent Numeric value between 25-50 to indicate what percent value to set when a user clicks on a comment within the Comment Drawer Optional 50 number
dotSize Numeric value to indicate comment dot size in pixels Optional 20 number
buttonEnablePanZoomText Text on button to enable pan and zoom Optional Observe string
makeObservationText Text when pan and zoom is enabled and commenting is allowed to indicate a user can leave a comment Optional Make Observation string
commentDialogHeadingText Text for Comment Dialog heading Optional What Do You See? string
commentDialogSecondaryHeadingText Text for Comment Dialog secondary heading Optional Make an observation. string
commentDialogConfirmHeadingText Text for Comment Dialog confirmation heading Optional Confirmation string
commentDialogConfirmSecondaryHeadingText Text for Comment Dialog confirmation secondary heading Optional As a last step please confirm your submission. Once it’s submitted, it can’t be undone. string
commentDialogConfirmCheckboxText Text to accompany Comment Dialog checkbox Optional I understand this can’t be undone string
commentDialogTextAreaPlaceholderText Placeholder text for Comment Dialog textarea Optional string
commentDialogCancelButtonText Text for Comment Dialog Cancel button when user is in pre-confirmation state Optional Cancel string
commentDialogBackButtonText Text for Comment Dialog Back button when user is in confirmation state Optional Back string
commentDialogReviewButtonText Text for Comment Dialog Review button when user is in pre-confirmation state Optional Review string
commentDialogSubmitButtonText Text for Comment Dialog Submit button when user is in confirmation state Optional Submit string
primaryColor Primary color (hex or rgba) Optional #fff string
secondaryColor Secondary color (hex or rgba) Optional #000 string
tertiaryColor Tertiary color (hex or rgba) Optional #aaa string
dotColor Comment dot color (hex or rgba) Optional red string
activeDotColor Active comment dot color (hex or rgba) Optional orange string
imgLoader React component or string to display when main image is loading Optional undefined React.ReactElement OR string OR undefined
zoomSupportLoader React component or string to display when zoom support image is being loaded. This will only come into play if you are using a very large image (10,000+ pixels in width) for props.src Optional undefined React.ReactElement OR string OR undefined
errorDisplay React component or string to display when an error is encountered during main image load Optional undefined React.ReactElement OR string OR undefined
onCommentSubmit Callback function called when a new comment is submitted Optional undefined Function OR undefined
onImageLoad Callback function called when the main image has loaded Optional undefined Function OR undefined
onTogglePanAndZoom Callback function called when pan and zoom is toggled on/off Optional undefined Function OR undefined

Demo

https://gemini-oss.github.io/react-image-pan-and-zoom-with-commenting/

Installation

yarn add react-image-pan-and-zoom-with-commenting
npm i react-image-pan-and-zoom-with-commenting

Basic Usage Image Pan And Zoom With Commenting

import ImagePanAndZoom from 'react-image-pan-and-zoom-with-commenting';

const App = () => {
    return (
        <App src='LINK_TO_IMAGE' previewSrc='LINK_TO_PREVIEW_IMAGE' />
    )
}

export default App

Basic Usage Image Pan And Zoom Only

import ImagePanAndZoom from 'react-image-pan-and-zoom-with-commenting';

const App = () => {
    return (
        <App src='LINK_TO_IMAGE' previewSrc='LINK_TO_PREVIEW_IMAGE' enableCommentDrop={false} />
    )
}

export default App

Advanced Usage Showing How You can Tie in With A Custom Backend To Persist Comments

import ImagePanAndZoom from 'react-image-pan-and-zoom-with-commenting';

const App = () => {
  const [isCommentSubmitLoading, setIsCommentSubmitLoading] =
    React.useState(false);
  const [isLoading, setIsLoading] = React.useState(true);
  const [comments, setComments] = React.useState<CommentDotProps[]>([]);

  const handleCommentSubmit = (comment: CommentDotProps) => {
    setIsCommentSubmitLoading(true);

    // Simulate a POST request to save a new comment
    setTimeout(() => {
      setComments([...comments, { ...comment }]);
      setIsCommentSubmitLoading(false);
    }, 3000);
  };

  React.useEffect(() => {
    // Simulate a GET request to retrieve existing comments 
    setTimeout(() => {
      setComments([
        {
          x: 0.6016064257028112,
          y: 0.6058201058201058,
          comment: "Lorem ipsum dolor sit amet. Et quod alias qui sequi labore est nostrum debitis sed officiis laborum qui ipsam consequuntur in rerum unde et recusandae quibusdam. Cum iste repudiandae sit sint placeat sed quia velit et quidem doloribus qui doloremque unde est asperiores similique.",
        },
        {
          x: 0.7653844927601894,
          y: 0.4520125762495482,
          comment:
            "Ut ipsum numquam qui expedita dolorem cum quaerat consequatur ut natus laudantium aut aspernatur laboriosam et asperiores assumenda. Ea voluptatem neque non quia laborum ut consequatur voluptatum in voluptas debitis et quisquam quidem. Ut ipsum numquam qui expedita dolorem cum quaerat consequatur ut natus laudantium aut aspernatur laboriosam et asperiores assumenda. Ea voluptatem neque non quia laborum ut consequatur voluptatum in voluptas debitis et quisquam quidem.",
        },
        {
          x: 0.45380107538609804,
          y: 0.6221498059102408,
          comment: "Rem aliquam galisum sed ducimus velit aut natus magni vel illum tempora est voluptatem vero et quam culpa eos sequi nostrum. Sed aliquam nihil ut fugit amet aut rerum dolorem et tempora voluptas et vitae repellendus et itaque dolor.",
        },
      ]);
      setIsLoading(false);
    }, 2000);
  }, []);

  return (
    <App
      src='LINK_TO_IMAGE'
      previewBoxSrc='LINK_TO_PREVIEW_IMAGE'
      comments={comments}
      isLoading={isLoading}
      isCommentSubmitLoading={isCommentSubmitLoading}
      onCommentSubmit={handleCommentSubmit}
    />
  );
};

export default App

ClassNames You Can Target

Component Description ClassName
Active Comment Component used to display an active comment .active-comment
Comment Dialog Component used to allow comment entry .comment-dialog
Comment Display Switch Component used to toggle comment display .comment-display-switch
Comment Drawer Component used to display list of comments .comment-drawer
Comment Drawer Backdrop Component used to backdrop for list of comments .comment-drawer-tint
Dot Container Container component to house comment dots .dot-container
Dot Container Dots Component used for a comment dot .button–dot
Image Pan And Zoom Component used for image pan and zoom .image-pan-and-zoom

GitHub

View Github