react-native-zoomable-view icon indicating copy to clipboard operation
react-native-zoomable-view copied to clipboard

What the ...., thousands of lines of code?

Open ChiragMDave opened this issue 4 years ago • 16 comments

These guys have written thousands of lines of code for what can be achieved by writing just 50 lines. Including zooming in/out from lastScale and pan moving from lastOffsets using PanGestureHandler, PinchGestureHandler, State.

ChiragMDave avatar Sep 03 '21 08:09 ChiragMDave

@ChiragMDave ok, but why haven't you done this and shared with us yet?

joaogabrieldasilva avatar Oct 04 '21 14:10 joaogabrieldasilva

@ChiragMDave ok, but why haven't you done this and shared with us yet?

import React from 'react'; import { Alert, Animated, Easing, View } from 'react-native'; import { PanGestureHandler, PinchGestureHandler, State } from 'react-native-gesture-handler'; import { PressBox } from './PressBox';

const IMAGEWIDTH = 250; const IMAGEHEIGHT = 400; export default class PinchableBox extends React.Component { constructor(props) { super(props);

	this._translateXY = new Animated.ValueXY({ x: 0, y: 0 });
	this._baseScale = new Animated.Value(1);
	this._pinchScale = new Animated.Value(1);
	this._scale = Animated.multiply(this._baseScale, this._pinchScale);
	this._lastScale = 1;

	this._onPanGestureEvent = Animated.event([{ nativeEvent: { translationX: this._translateXY.x, translationY: this._translateXY.y, } }], {
		useNativeDriver: true,
	});

	this._onPanHandlerStateChange = (event) => {
		if (event.nativeEvent.oldState === State.ACTIVE) {
			this._translateXY.extractOffset();
		}
	}

	this._onPinchGestureEvent = Animated.event([{ nativeEvent: { scale: this._pinchScale } }], {
		useNativeDriver: false
	});

	this._onPinchHandlerStateChange = (event) => {
		if (event.nativeEvent.oldState === State.ACTIVE) {
			this._lastScale *= event.nativeEvent.scale;
			this._baseScale.setValue(this._lastScale);
			this._pinchScale.setValue(1);
		}
	};
}

_onDoubleTap = ref => {
	this._translateXY.flattenOffset();
	this._pinchScale.flattenOffset(); this._baseScale.flattenOffset();
	Animated.parallel([
		Animated.spring(
			this._translateXY,
			{ toValue: { x: 0, y: 0 }, friction: 5, useNativeDriver: true }
		),
		Animated.spring(
			this._pinchScale,
			{ toValue: 1, friction: 5, useNativeDriver: true }
		),
		Animated.spring(
			this._baseScale,
			{ toValue: 1, friction: 5, useNativeDriver: true }
		),
	]).start(() => this._lastScale = 1);
};

doubleTapRef = React.createRef();

render() {
	return (
		<PressBox ref={this.doubleTapRef} onDoubleTap={this._onDoubleTap}>
			<PinchGestureHandler
				onGestureEvent={this._onPinchGestureEvent}
				onHandlerStateChange={this._onPinchHandlerStateChange}>
			        <View style={{ flex: 1, alignSelf: 'stretch', justifyContent: 'center', }} collapsable={false}>
				        <PanGestureHandler onBegan={this._onBegan}
					        onGestureEvent={this._onPanGestureEvent}
					        onHandlerStateChange={this._onPanHandlerStateChange}
				        >
					        <Animated.Image
						        source={{ uri: this.props.imageUri }}
						        style={[
							        {
								        alignSelf: 'center',
								        height: this.props.imageHeight ? this.props.imageHeight :IMAGEHEIGHT, 
								        width: this.props.imageWidth ? this.props.imageWidth : IMAGEWIDTH,
								        transform: [
									        { perspective: 200 },
									        { scale: this._scale },
									        { translateX: this._translateXY.x },
									        { translateY: this._translateXY.y },
								        ],
							        },
						        ]}
					        />
				        </PanGestureHandler>
			        </View>
			</PinchGestureHandler>
		</PressBox>
	);
}

}

ChiragMDave avatar Oct 09 '21 01:10 ChiragMDave

@ChiragMDave I tried your code - nice work! Here are a few little things I noticed:

  1. The pinch-zoom scales from the centre of the viewport, instead of from the centre of the pinch gesture.
  2. No bounding - the image can be dragged completely out of view
  3. No easing, or spring animations that I could see.

I'm sure these can be achieved in less code than used in this repo, but I wanted to point out that there are a few other features implemented here.

elliottkember avatar Oct 16 '21 16:10 elliottkember

@elliottkember can we write code with the same brevity that addresses your concerns?

Update: checkout this code https://github.com/enzomanuelmangano/animate-with-reanimated/blob/main/04-pinch-gesture-handler-basics/App.tsx

cloudorbush avatar Nov 23 '21 23:11 cloudorbush

@10000multiplier That example's great! The react-native-reanimated and react-native-gesture-handler libraries have some features that would simplify all this calculation a lot. This library has been around since 2018 - I'm sure there have been some new features since then that we can use.

Would you like to draft up a PR to @openspacelabs/react-native-zoomable-view and we can work on the implementation? It will be quite a big refactor, but I think it would be great to work towards it.

elliottkember avatar Dec 09 '21 17:12 elliottkember

@elliottkember indeed, v2 of react-native-gesture-handler just came out with a rewrite of the old API!

You may want to get familiar with some of the examples: https://github.com/software-mansion/react-native-gesture-handler/tree/master/example/src/new_api.

I'm not in a position right now where I can put much time in this, but in the future I'd be glad to help out with a PR if needed!

cloudorbush avatar Dec 09 '21 19:12 cloudorbush

@elliottkember indeed, v2 of react-native-gesture-handler just came out with a rewrite of the old API!

You may want to get familiar with some of the examples: https://github.com/software-mansion/react-native-gesture-handler/tree/master/example/src/new_api.

I'm not in a position right now where I can put much time in this, but in the future I'd be glad to help out with a PR if needed!

Superb. I'll have a look at the new API soon, thank you for the link. It would be neat to aim for a simpler implementation of this component - it works well enough that there is no real rush, but it seems doable and a fun project.

My colleague Thomas did all the real work fixing bugs for our fork, I am just helping out with the handover.

elliottkember avatar Dec 10 '21 17:12 elliottkember

@elliottkember might want to check out this gist https://gist.githubusercontent.com/intergalacticspacehighway/9e931614199915cb4694209f12bf6f11/raw/dc67da12841d2e7e34b2e4f50a03953cffa86d76/PinchToZoom.tsx

It works great.

Hats off to @intergalacticspacehighway

cloudorbush avatar Jan 05 '22 00:01 cloudorbush

@10000multiplier This is awesome. Super impressive work @intergalacticspacehighway!

As it turns out, we may have a need to implement this in a new version of the library. There are many benefits to react-native-gesture-handler. However it is likely we would have to start with the most basic features to begin with, so it may start with a much simpler API while we get going.

I'll update this thread when I have something more concrete to share, but if you are interested in helping with this, let me know!

elliottkember avatar Jan 27 '22 19:01 elliottkember

@elliottkember here's my updated version with the new API. Works like a charm on iOS, haven't tested it on Android.

https://gist.github.com/10000multiplier/264779c850e083ec3cb92eb8413559c0

cloudorbush avatar Jan 27 '22 20:01 cloudorbush

@10000multiplier This is great! Unfortunately this doesn't solve the use-case where a user can zoom and pan the image like a map, which is how we use it. We're working through it but it's a difficult transform. It's important to be able to zoom the image in at the location the user is pinching.

elliottkember avatar Jan 28 '22 23:01 elliottkember

@10000multiplier We've spent the last couple of days working this into a new version of the plugin. You can check it out in the V3 branch in our repo.

The new version has dependencies on react-native-reanimated and react-native-gesture-handler, but the code is very short. None of the configuration props are implemented yet, but it's quite functional and seems to be very performant. I will be testing it on a really old Android tablet when it arrives.

Big shout-out to @thomasttvo for doing all the math, I nearly broke my brain trying!

elliottkember avatar Jan 29 '22 18:01 elliottkember

@elliottkember @thomasttvo awesome job guys. Looks great.

cloudorbush avatar Jan 29 '22 18:01 cloudorbush

Hi all,

I've made zoom package on reanimated & gesture-handler Hope it could be useful for somebody: https://github.com/kesha-antonov/react-native-zoom-reanimated

https://user-images.githubusercontent.com/11584712/174408748-21b53fd8-2442-475e-bec3-30e3186c770e.mp4

kesha-antonov avatar Jun 17 '22 22:06 kesha-antonov

@kesha-antonov that's amazing. I love it. As far as I can see it does not have all of the features and configurations of this package or @elliottkember s package, but it is still a very good alternative.

I have added it to the readme disclaimer.

As for the question in the title of this issue: Yes, the code was written this way, because back then (when we first started this component - and even before we published it), there was no reanimated and a lot of other issues too.

Funny enough: I already had a working prototype with reanimated1, when it came out. It worked really well and was - performancewise - nice, but I lost the code with my harddrive.

To be honest, that was also what made me stop working on this package, because I was frustrated of losing all this progress of a complete rewrite and did not want to start again. Especially, because we are not using react-native internally anymore and so there was no business case for us to work on it, apart from the love for Opensource.

I am glad though, that now with your 2 packages there are alternatives out there.

SimonErich avatar Jul 05 '22 12:07 SimonErich

@kesha-antonov that's amazing.

I love it. As far as I can see it does not have all of the features and configurations of this package or @elliottkember s package, but it is still a very good alternative.

I have added it to the readme disclaimer.

As for the question in the title of this issue:

Yes, the code was written this way, because back then (when we first started this component - and even before we published it), there was no reanimated and a lot of other issues too.

Funny enough:

I already had a working prototype with reanimated1, when it came out.

It worked really well and was - performancewise - nice, but I lost the code with my harddrive.

To be honest, that was also what made me stop working on this package, because I was frustrated of losing all this progress of a complete rewrite and did not want to start again.

Especially, because we are not using react-native internally anymore and so there was no business case for us to work on it, apart from the love for Opensource.

I am glad though, that now with your 2 packages there are alternatives out there.

Thanks man!

Yeah, sad story with that hard drive)

I also wrote my package internally almost a year ago I think. But was busy with other stuff, and now I've found time to open source that finally)

kesha-antonov avatar Jul 05 '22 12:07 kesha-antonov