2022 Powerful Work: An Ultra-Delicate Picture Preview Component
Tags: react, photoswipe, gallery, carousel
Published: 2022-03-05 08:17:20

---
title: 2022 Powerful Work: An Ultra-Delicate Picture Preview Component
published: true
description: `react-photo-view` has an unparalleled preview interaction experience: starting from the opening of the image, the animation, details and interaction of each frame have been carefully designed and repeatedly debugged, which is comparable to the effect of native image preview.
tags: React,PhotoSwipe,gallery,carousel
//cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sa0mu4325n020o34alpk.jpg
---


When I first came into the front-end industry, I had an idea, and that was to write a super cool image preview gallery.

The component has been released an incomplete version a few years ago, and after intermittent maintenance, it always feels that something is wrong. There is no rest this year, and all the development is carried out on it. Now it is finally realized! First look at the effect:

Thumbnail perfect gradient:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vo4ir2s2629heyex3gfb.gif)

Zoom in at a specified location:

![2.gif](https://s2.loli.net/2022/03/02/gtem46cBH2lwRsr.gif)

Slow down scrolling:

![3.gif](https://s2.loli.net/2022/03/02/zskQUAY8ZIlipKn.gif)

### what is `react-photo-view`

> `react-photo-view` has an unparalleled preview interaction experience: starting from the opening of the image, the animation, details and interaction of each frame have been carefully designed and repeatedly debugged, which is comparable to the effect of native image preview.

```bash
pnpm i react-photo-view
```

Overview:

```js
import { PhotoProvider, PhotoView } from 'react-photo-view';
import 'react-photo-view/dist/react-photo-view.css';

export default function MyComponent() {
  return (
    <PhotoProvider>
      <PhotoView src="/1.jpg">
        <img src="/1-thumbnail.jpg" alt="" />
      </PhotoView>
    </PhotoProvider>
  );
}
```

### Why develop it separately?

Of course, the obsession to realize it is also an aspect, but the fundamental reason is that in the powerful ecosystem of `React`, there is simply no easy-to-use image preview solution. At that time, I followed the principle of use, and I found a circle of `React`-based zoom preview component libraries on the Internet. The result surprised me a bit. The number of image zoom preview libraries is obviously not comparable to the carousel component library. What is even more suffocating is that most of these meager component libraries are secondary encapsulation based on the `PhotoSwipe` open source library. In addition, the preview component library that can be used in actual production... It seems that there is no (or maybe I can't find it), this situation is not only reflected in the `React` library, other frameworks `Vue` and even native related Libraries are like that.

Of course, `PhotoSwipe` is not unusable, but the native operation `DOM` is out of place in `React`, and its volume is also above `gzip 12KB`, which is a bit bloated, so I have this bold idea.

### How good is it?

It has very perfect details and features:

- Support touch gestures, drag and pan physical effect sliding, two-finger specified position to zoom in and out
- All aspects of animation connection, open and close the rebound touch edge, let the natural interaction effect
- The image is adaptive, with a suitable initial rendering size, and adapts according to the adjustment
- Support for custom previews like `<video>` or any `HTML` element
- Keyboard navigation, perfect for desktop
- Support custom node expansion, easy to achieve full-screen preview, rotation control, picture introduction and more functions
- Based on `typescript`, `7KB Gzipped`, supports server-side rendering
- Simple and easy to use `API`, zero cost to get started

It also exports `JS` that supports `ES2017` and above, and can achieve `6KB Gzipped`. It is not easy to add a lot of experience details to such a volume. More functions can be achieved through very easy custom rendering, which is perfectly in line with the `React` concept, thus avoiding built-in functions that are not rigidly needed. .

### Comparison of popular libraries

The following table summarizes the functions required for most scenarios, showing a comparison of `react-photo-view` , `PhotoSwipe` and `rc-image` (ant-design):

|                                | react-photo-view | PhotoSwipe                                   | rc-image             |
| ------------------------------ | ---------------- | -------------------------------------------- | -------------------- |
| MINIFIED                       | 19KB             | 47KB                                         | 40KB                 |
| MINIFIED + GZIPPED             | 7.3KB            | 12KB                                         | 14KB                 |
| Basic preview                  | Support          | Support                                      | Support              |
| Toggle preview                 | Support          | Support                                      | Not support          |
| Mobile                         | Support          | Support                                      | Not support          |
| Thumbnail perfect gradient     | Support          | Support                                      | Not support          |
| Thumbnail crop animation       | Support          | Support（need to be specified manually）     | Not support          |
| Adaptive Image Size            | Support          | Not support（need to be specified manually） | Support              |
| fallback                       | Support          | Not support                                  | Support              |
| Mouse wheel zoom               | Support          | Not support                                  | （missing location） |
| Spring physical roll           | Support          | Support                                      | Not support          |
| Animation parameter adjustment | Support          | Support                                      | Not support          |
| Easy-to-use API                | Support          | Not support                                  | Support              |
| TypeScript                     | Support          | Not support                                  | Support              |
| Keyboard navigation            | Support          | Support                                      | Support              |
| Custom element                 | Support          | Risk of XSS                                  | Not support          |
| controlled                     | Support          | Support                                      | Support              |
| Loop preview                   | Support          | Support                                      | Not support          |
| Rotation                       | Support          | Not support                                  | Support              |
| Custom toolbar                 | Support          | Support                                      | Not support          |
| Full screen                    | custom extension | Support                                      | Not support          |

### Friendly documentation

What's more important than documentation, and for this, I also prepared a super beautiful document

https://react-photo-view.vercel.app/

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bw23npcrlun1age23yea.png)

### Realization process

#### Image scrolls with your finger

Record the current trigger position state in `onTouchStart`, let it follow the finger movement in `onTouchMove`, and `onTouchEnd` can be easily implemented.

The touch position feedback makes the picture switching need to slowly ponder the details: moving after `onTouchStart`, if the picture follows the finger movement immediately, it will bring many misoperations, such as the logic of sliding up and down when you want to switch the picture . At this time, a `20px` movement buffer is needed to predict the direction of the finger movement.

#### Specify image location to zoom in

Use `transform: scale(value)` to scale the image, but the center of the image is zoomed in, and the result of the scaling may not be what you want. Originally intended to use `transform-origin` to achieve, the idea is good, although the first time can be zoomed in at the specified position. If the reduced position is not the original position, there will be chaotic beating. Obviously, this method will not work.

Later, I couldn't sleep after thinking about it, and found inspiration in my sleep: to facilitate calculation and understanding, we set the center point of the picture as `0`, and zooming in and out of any specified position means changing the position of the center of the picture. For example, the image width is `200`, the center point position is `100`, and it is doubled based on the leftmost position. Now the image width is `400`, then the position of the center point should be `200`. Then the summary formula is as follows:

```js
const centerClientX = innerWidth / 2;
// Coordinate offset conversion
const lastPositionX = centerClientX + lastX;
// zoom offset
const offsetScale = nextScale / scale;
// final offset position
const originX =
  clientX - (clientX - lastPositionX) * offsetScale - centerClientX;
```

This mode of computing can take on various positional responses, such as pinch-to-zoom, pinch-to-scroll+zoom, edge computing, and more.

#### Distance between fingers

This requires the right triangle Pythagorean theorem:

```js
Math.sqrt((nextClientX - clientX) ** 2 + (nextClientY - clientY) ** 2);
```

#### Simulate scrolling

The previous version was implemented using `transition`. The initial speed was calculated by the time difference between the start and end of the finger sliding, and it was estimated that the `transition` was used to simulate a distance to make the eyes look like a scrolling effect 😂. But this way the experience is always much worse. Later, combined with the high school physics formula, the rolling effect is simulated:

![5.png](https://s2.loli.net/2022/03/02/qd27QLtS4lDWgXm.png)

Accelerated movement:

![6.png](https://s2.loli.net/2022/03/02/h9JQwWCpte3ryzx.png)

Air resistance:

![7.png](https://s2.loli.net/2022/03/02/S6Mt3VxYksDodEc.png)

`CρS` are all constants, so just make them a quantity. As for how to get this amount... I tried it out 😂 This is only proportional to the square of `v`.

In addition, because it is opposite to the direction of motion, take the direction of `v` that is `Math.sign(-v)`

```js
function scrollMove(
  initialSpeed: number,
  callback: (spatial: number) => boolean,
) {
  const acceleration = -0.002;
  const resistance = 0.0002;

  let v = initialSpeed;
  let s = 0;
  let lastTime: number | undefined = undefined;
  let frameId = 0;

  const calcMove = (now: number) => {
    if (!lastTime) {
      lastTime = now;
    }
    const dt = now - lastTime;
    const direction = Math.sign(initialSpeed);
    const a = direction * acceleration;
    const f = Math.sign(-v) * v ** 2 * resistance;
    const ds = v * dt + ((a + f) * dt ** 2) / 2;
    v = v + (a + f) * dt;

    s = s + ds;
    // move to s
    lastTime = now;

    if (direction * v <= 0) {
      cancelAnimationFrame(frameId);
      return;
    }

    if (callback(s)) {
      frameId = requestAnimationFrame(calcMove);
      return;
    }
    cancelAnimationFrame(frameId);
  };
  frameId = requestAnimationFrame(calcMove);
}
```

#### Thumbnail crop

`PhotoSwipe` supports thumbnail cropping, but you need to manually specify the image width and height and `data-cropped`, which is quite troublesome. `react-photo-view` gets the current cropping parameters by reading the thumbnail `getComputedStyle(element).objectFit`. Realize automatic cropping effect.

#### Compatibility Handling

Since each image is a composite layer, this consumes quite a bit of memory. `IOS` has a considerable memory limit, if the image is always using `scale` when zoomed in, it will appear very blurry on `Safari`. Now by changing the width and height of the image to the specified value every time after the movement is completed, and then resetting the `scale` to 1, this method should achieve the desired effect.

### Other

The author of `PhotoSwipe` is a Ukrainian living in Kyiv who fled Kyiv and is now safe with his family in western Ukraine, and hopes he will bounce back after the war.

### epilogue

I spent a lot of time on the details of `react-photo-view`, if you like it, you can help me by clicking `Star`

[https://github.com/MinJieLiu/react-photo-view](https://github.com/MinJieLiu/react-photo-view)

Thanks!