react-native-zoomable-view
                                
                                 react-native-zoomable-view copied to clipboard
                                
                                    react-native-zoomable-view copied to clipboard
                            
                            
                            
                        What the ...., thousands of lines of code?
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 ok, but why haven't you done this and shared with us yet?
@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 I tried your code - nice work! Here are a few little things I noticed:
- The pinch-zoom scales from the centre of the viewport, instead of from the centre of the pinch gesture.
- No bounding - the image can be dragged completely out of view
- 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 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
@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 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!
@elliottkember indeed, v2 of
react-native-gesture-handlerjust 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 might want to check out this gist https://gist.githubusercontent.com/intergalacticspacehighway/9e931614199915cb4694209f12bf6f11/raw/dc67da12841d2e7e34b2e4f50a03953cffa86d76/PinchToZoom.tsx
It works great.
Hats off to @intergalacticspacehighway
@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 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
@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.
@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 @thomasttvo awesome job guys. Looks great.
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 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.
@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)