Install

Run the following commands in the React project directory:

pnpm:

pnpm i react-photo-view

yarn:

yarn add react-photo-view

or with npm:

npm install react-photo-view

Quick start

Import the CSS file at the entry point of the application

import 'react-photo-view/dist/react-photo-view.css';

Introduce PhotoProvider and PhotoView components where they need to be used, basic usage:

import { PhotoProvider, PhotoView } from 'react-photo-view';

export default function MyComponent() {
  return (
    <PhotoProvider>
      <div className="foo">
        {images.map((item, index) => (
          <PhotoView key={index} src={item}>
            <img src={item} alt="" />
          </PhotoView>
        ))}
      </div>
    </PhotoProvider>
  );
}

With the PhotoProvider as the boundary, all the PhotoView images in it will be extracted as a set of image preview galleries in the running order. When an <img > is clicked, it will locate the specified image and open the preview:

If you need the images of the entire page to form a group for preview, you can nest PhotoProvider at the entrance of the page.

💡

Note: react-photo-view only keeps three images loaded in the DOM at a time, since each image is a compositing layer, which consumes quite a bit of memory.

Animation

Transition effect

react-photo-view uses quite a lot of CSS3 animations to ensure a smoother experience, and uses spring animations to stay close to the native scrolling effect when scrolling. CSS3 animations have a default duration of 400ms, using the cubic-bezier(0.25, 0.8, 0.25, 1) animation function.

You can customize the animation time or function by setting speed , easing:

<PhotoProvider
  speed={() => 800}
  easing={(type) => (type === 2 ? 'cubic-bezier(0.36, 0, 0.66, -0.56)' : 'cubic-bezier(0.34, 1.56, 0.64, 1)')}
/>

speed and easing are functions. The type of the current animation can be judged by the parameter type. The values of type are:

  • 1 - open
  • 2 - closing
  • 3 - toggle index
💡
If the child node is not an img node, an additional opacity fade effect will be added.

Crop Thumbnails

The <PhotoView> component automatically recognizes the child node type, and if the child node is <img >, and the object-fit property is set using CSS, it will have a cropping effect in the zoom animation:

<PhotoView src={imageURL}>
  <img src={imageURL} style={{ objectFit: 'cover' }} alt="" />
</PhotoView>

Custom toolbar

Add parameter toolbarRender function to <PhotoProvider>, which must return a ReactNode node.

Zoom in or zoom out

Add a scale control to achieve zoom in and out buttons:

<PhotoProvider
  toolbarRender={({ onScale, scale }) => {
    return (
      <>
        <svg className="PhotoView-Slider__toolbarIcon" onClick={() => onScale(scale + 1)} />
        <svg className="PhotoView-Slider__toolbarIcon" onClick={() => onScale(scale - 1)} />
      </>
    );
  }}
/>

Rotate

react-photo-view does not have a built-in rotation control button by default, but provides a rotation API. We can achieve the rotation effect by simply adding an SVG image:

<PhotoProvider
  toolbarRender={({ rotate, onRotate }) => {
    return <svg className="PhotoView-Slider__toolbarIcon" onClick={() => onRotate(rotate + 90)} />;
  }}
/>

The parameters provided by the toolbarRender function are:

  • images - list of images DataType[]
  • index - the current index number
  • onIndexChange - index change callback (index: number) => void
  • visible - whether to see boolean
  • onClose - close event callback () => void
  • overlayVisible - whether the overlay is visible boolean
  • overlay - Overlay
  • rotate - the current rotation angle number
  • onRotate - Rotate event callback (rotate: number) => void
  • scale - the current scale number
  • onScale - Scale event callback (scale: number) => void

The following example implements a rotating full-screen zoom in and out toolbar:

Long image mode

When loading images whose aspect ratio exceeds 3 times, the mobile device will enter the long image mode by default. That is, the preview starts at the top of the image, and the width of the image fills the width with the mobile device:

Trigger node

<PhotoView> child components can be of the regular React.HTMLAttributes type, such as <img>, <div>, etc.

If it is a custom component, you need to ensure that onClick event can be triggered normally, and forward ref to HTMLElement to ensure the correctness of opening and closing the animation source.

<PhotoView src={imageURL}>
  <Button primary>Click</Button>
</PhotoView>
Click

PhotoSlider

In general, using <PhotoProvider> in combination with <PhotoView> already suffices for most needs. For more advanced custom controls, <PhotoSlider> can be used.

The parameters of <PhotoSlider> are inherited from <PhotoProvider>. Several additional APIs are exposed:

  • images list of images, DataType[]
  • visible whether the controlled property boolean is visible
  • onClose close event callback () => void
  • afterClose callback after closing animation () => void
  • index The current index controlled property number
  • onIndexChange index change callback (index: number) => void

visible is controlled with onClose, and index is controlled with onIndexChange.

export default function MyComponent() {
  const [visible, setVisible] = useState(false);
  const [index, setIndex] = useState(0);

  return (
    <>
      <Button onClick={() => setIndex(2)}>setIndex(2)</Button>
      <Button onClick={() => setIndex(4)}>setIndex(4)</Button>
      <Button onClick={() => setVisible(true)} primary>
        Click
      </Button>

      <PhotoSlider
        images={images.map((item) => ({ src: item, key: item }))}
        visible={visible}
        onClose={() => setVisible(false)}
        index={index}
        onIndexChange={setIndex}
      />
    </>
  );
}
setIndex(2)
setIndex(4)
Click

Preview more

<PhotoView > can also be added to the preview queue without subcomponents, but it cannot directly trigger the preview of the current picture. Through this feature, we can realize the function of previewing more pictures through an entrance:

<PhotoProvider>
  {images.map((item, index) => (
    <PhotoView key={index} src={item}>
      {index < 2 ? <img src={item} alt="" /> : undefined}
    </PhotoView>
  ))}
</PhotoProvider>

Custom render node

<PhotoView> has built-in support for image preview by default, just pass src, the component will automatically recognize the width and height of the image.

We can also implement the preview of custom nodes through the render function to support the needs of video or pdf.

const elementSize = 400;

function MyComponent() {
  return (
    <PhotoView
      width={elementSize}
      height={elementSize}
      render={({ scale, attrs }) => {
        const width = attrs.style.width;
        const offset = (width - elementSize) / elementSize;
        const childScale = scale === 1 ? scale + offset : 1 + offset;

        return (
          <div {...attrs}>
            <div style={{ transform: `scale(${childScale})`, width: elementSize, transformOrigin: '0 0' }}>
              <div>Hello world</div>
              <Button>button</Button>
              <input onMouseDown={(e) => e.stopPropagation()} />
            </div>
          </div>
        );
      }}
    >
      <Button primary>Click</Button>
    </PhotoView>
  );
}
💡

Note: After the gallery is zoomed, it will first be scaled with scale, and after the animation is over, it will be adjusted to the width and height corresponding to the current scale, and the scale will be reset to 1.

Click

PhotoProvider parameter

Custom description

Add the overlayRender function to <PhotoProvider> to implement custom nodes. The return value of the function parameter is the same as that of toolbarRender.

Loop preview

Add parameter loop on <PhotoProvider> to change the number of loop previews. Set to boolean to enable and disable, set to number type to exceed the specific number to enable loop preview, the default value is 3 sheets:

<PhotoProvider loop={4} />

Mask Transparency

The default is opaque, and the transparency will gradually decrease during the pull-up process. You can add maskOpacity to <PhotoProvider> to adjust the default transparency, which can be set to a number between 0-1:

<PhotoProvider maskOpacity={0.5} />

Don't show top button area

Set bannerVisible to false to hide the top area:

<PhotoProvider bannerVisible={false} />

Masking event

pullClosable (default true) can close the gallery by pulling down, maskClosable (default true) can click on the mask to close the gallery:

<PhotoProvider pullClosable={false} maskClosable={false} />

Custom feedback

<PhotoView> If loading fails, empty nodes are rendered by default, and you may need to set your own error feedback nodes according to the current UI.

  • brokenElement - set the load failure pattern React.ReactElement
  • loadingElement - set the loading pattern React.ReactElement
Click