intergalacticspacehighway / react-native-reanimated-zoom

Component for zooming react native views. 🔎
MIT License
315 stars 19 forks source link

Enhancement proposal: avoid scrolling zoomed image beyond the borders #3

Open ManuViola77 opened 2 years ago

ManuViola77 commented 2 years ago

Hi!

I loved this library and the only thing I found that could make it better is to have the option of not scrolling beyond the zoomed image borders, meaning that it doesn't leave black spaces horizontally or vertically (except the image is actually smaller than the height/width).

In case Im not being clear, what I mean is that if the image is this:

it could only let scroll upto the borders like this:

without letting have empty black space between the image and the borders.

This requires lots of math 😅 I managed to make a version but is not perfect at all, I would love to have a more official code for doing that 😅 In my case, for example, all math is broken if the user turns the phone horizontally 😂

putuoka commented 2 years ago

it's bindToBorders from https://github.com/openspacelabs/react-native-zoomable-view i tried to look into the code but hard for me since i don't know typescript also they are not using reanimated

ManuViola77 commented 2 years ago

Yeah, you are right, basically is this code:

image

I think it doesn't really matter Typescript or Reanimated. The issue here is if the offset is greater than what it should be to avoid scrolling over the boundaries, so basically assuming 0 is in the middle of the image (which it is), you can calculate the biggest offset accepted according to the current zoom scale, and, if the offset is greater than that, you assign the maximum allowed, otherwise you assign the offset you have. Also, if the image is smaller than the window's width/height, in those cases the offset should be 0.

This implies a lot of math, like you can see here for example.

What I did for this case, you have inside the pan gesture that the offset X is this:

const offsetX = prevTranslationX.value + e.translationX - panTranslateX.value;

so what I do with that is, instead of assigning that directly to translationX.value, I call a getPanTranslation I created with these parameters: getPanTranslation(offsetX, IMAGE_WIDTH, screenWidth, scale.value) and the function has this code:

const getPanTranslation = (offset, imageOriginalDimension, screenDimension, scale) => {
    const imageDimension = imageOriginalDimension * scale;

    if (imageDimension <= screenDimension) {
      return 0; // if the image is smaller than the screen dimension, the offset is 0
    }

    const max = (imageDimension - screenDimension) / 2;  // this is the max offset accepted
    const sign = offset < 0 ? -1 : 1;
    const absoluteOffset = Math.abs(offset);

    return Math.min(absoluteOffset, max) * sign;
  };

I think this is not perfect but is the way I managed to do it 😅

Davidson-Mike commented 2 years ago

+1 for this enhancement - this is a rather critical UX feature for any zoom/pan system to prevent the content from being panned outside of the view.

This is a great library and I'd love to see a solution built in for this, like bindToBorders from react-native-zoomable-view

intergalacticspacehighway commented 2 years ago

Thanks for the suggestions! Just pushed a fix in 0.1.4. https://github.com/intergalacticspacehighway/react-native-reanimated-zoom/pull/7. Can you try and let me know if it works as expected?

intergalacticspacehighway commented 2 years ago

Closing as it's solved in 0.2.0. Let me know if you still face any issues.

ManuViola77 commented 2 years ago

Hi! Thanks for the quick response! I cloned this and run the example and horizontal works perfect as expected, vertical I see it still scrolls more than the borders. But maybe is just because of how the example is implemented, I didn't check in my particular project.

https://user-images.githubusercontent.com/8755889/176504358-2af596a2-64b6-4f03-b202-9221ec499e71.mp4

intergalacticspacehighway commented 2 years ago

Yes, right. I missed checking the vertical. Thanks for reporting. I'll look into it!

ManuViola77 commented 2 years ago

Thanks for being so attentive 😄

intergalacticspacehighway commented 2 years ago

@ManuViola77 i just checked and it kind of works as expected 😅. In the above example, the image is less in height, so it allows a bit of scrolling. Try the below example, it won't allow scrolling out of image borders. Let me know if this is expected!

 <Zoom>
      <Image
        source={{
          uri: 'https://images.unsplash.com/photo-1536152470836-b943b246224c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1038&q=80',
        }}
        style={{
          width: Dimensions.get('window').width,
          height: Dimensions.get('window').height,
        }}
      />
</Zoom>